summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/mysql/config.m429
-rw-r--r--ext/mysql/php_mysql.c504
-rw-r--r--ext/mysql/php_mysql.h35
-rw-r--r--ext/mysqli/config.m464
-rw-r--r--ext/mysqli/config.w3264
-rw-r--r--ext/mysqli/config9.m4103
-rw-r--r--ext/mysqli/mysqli.c306
-rw-r--r--ext/mysqli/mysqli_api.c1016
-rw-r--r--ext/mysqli/mysqli_driver.c12
-rw-r--r--ext/mysqli/mysqli_embedded.c8
-rw-r--r--ext/mysqli/mysqli_exception.c8
-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.c403
-rw-r--r--ext/mysqli/mysqli_prop.c29
-rw-r--r--ext/mysqli/mysqli_repl.c8
-rw-r--r--ext/mysqli/mysqli_report.c14
-rw-r--r--ext/mysqli/mysqli_warning.c169
-rw-r--r--ext/mysqli/mysqlnd/config-win.h98
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd.c1680
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd.h917
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_charset.c608
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_charset.h35
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_enum_n_def.h231
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_libmysql_compat.h121
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_loaddata.c334
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_palloc.c558
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_palloc.h109
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_priv.h194
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_ps.c1581
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_ps_codec.c852
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_qcache.c141
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_result.c1085
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_result.h48
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_result_meta.c401
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_result_meta.h40
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_statistics.c130
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_statistics.h129
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c1817
-rw-r--r--ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h328
-rw-r--r--ext/mysqli/mysqlnd/php_mysqlnd.h29
-rw-r--r--ext/mysqli/mysqlnd/portability.h514
-rw-r--r--ext/mysqli/php_mysqli.h350
-rw-r--r--ext/mysqli/php_mysqli_structs.h400
45 files changed, 14477 insertions, 1173 deletions
diff --git a/ext/mysql/config.m4 b/ext/mysql/config.m4
index 84f0e8a9a1..3653cfab55 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
@@ -97,6 +100,11 @@ Note that the MySQL client library is not bundled anymore!])
;;
esac
+ dnl for compat with PHP 4 build system
+ if test -z "$PHP_LIBDIR"; then
+ PHP_LIBDIR=lib
+ fi
+
for i in $PHP_LIBDIR $PHP_LIBDIR/mysql; do
MYSQL_LIB_CHK($i)
done
@@ -132,14 +140,25 @@ 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)
+
+ dnl These 3 lines are neeeded to be able to build ext/mysql and/or ext/mysqli with/without mysqlnd.
+ dnl Need to do this here for the file to be always available.
+ $php_shtool mkdir -p ext/mysql/
+ echo > ext/mysql/php_have_mysqlnd.h
+ test "$PHP_MYSQL" = "mysqlnd" && PHP_DEFINE(HAVE_MYSQLND, 1, [ext/mysql])
+fi
diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c
index d3ae44de48..eb09043126 100644
--- a/ext/mysql/php_mysql.c
+++ b/ext/mysql/php_mysql.c
@@ -58,7 +58,6 @@
# endif
#endif
-#include <mysql.h>
#include "php_ini.h"
#include "php_mysql.h"
@@ -73,7 +72,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
@@ -83,7 +82,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
@@ -95,10 +94,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)
@@ -118,10 +113,23 @@ 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[]
*/
zend_function_entry mysql_functions[] = {
@@ -178,7 +186,7 @@ zend_function_entry mysql_functions[] = {
PHP_FE(mysql_get_server_info, NULL)
#endif
- PHP_FE(mysql_info, NULL)
+ PHP_FE(mysql_info, NULL)
#ifdef MYSQL_HAS_SET_CHARSET
PHP_FE(mysql_set_charset, NULL)
#endif
@@ -210,10 +218,23 @@ zend_function_entry mysql_functions[] = {
};
/* }}} */
+/* Dependancies */
+static 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),
@@ -221,7 +242,7 @@ zend_module_entry mysql_module_entry = {
PHP_RINIT(mysql),
PHP_RSHUTDOWN(mysql),
PHP_MINFO(mysql),
- NO_VERSION_YET,
+ "1.0",
PHP_MODULE_GLOBALS(mysql),
PHP_GINIT(mysql),
NULL,
@@ -238,6 +259,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) { \
@@ -256,7 +297,8 @@ void timeout(int sig);
} \
} while(0); \
} \
-} \
+}
+#endif
/* {{{ _free_mysql_result
* This wrapper is required since mysql_free_result() returns an integer, and
@@ -289,7 +331,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;
@@ -305,7 +347,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)--;
@@ -320,7 +362,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);
@@ -355,6 +397,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()
/* }}} */
@@ -371,7 +417,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
}
/* }}} */
@@ -395,9 +446,14 @@ 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_server_init(0, NULL, NULL)) {
return FAILURE;
}
+#else
+ mysql_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MySG(cache_size));
+ mysql_mysqlnd_qcache = mysqlnd_qcache_init_cache();
+#endif
return SUCCESS;
}
@@ -407,6 +463,7 @@ ZEND_MODULE_STARTUP_D(mysql)
*/
PHP_MSHUTDOWN_FUNCTION(mysql)
{
+#ifndef HAVE_MYSQLND
#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 */
@@ -416,6 +473,10 @@ PHP_MSHUTDOWN_FUNCTION(mysql)
#else
mysql_server_end();
#endif
+#else
+ mysqlnd_palloc_free_cache(mysql_mysqlnd_zval_cache);
+ mysqlnd_qcache_free_cache_reference(&mysql_mysqlnd_qcache);
+#endif
UNREGISTER_INI_ENTRIES();
return SUCCESS;
@@ -426,7 +487,7 @@ PHP_MSHUTDOWN_FUNCTION(mysql)
*/
PHP_RINIT_FUNCTION(mysql)
{
-#ifdef ZTS
+#if !defined(HAVE_MYSQLND) && defined(ZTS)
if (mysql_thread_init()) {
return FAILURE;
}
@@ -437,6 +498,11 @@ PHP_RINIT_FUNCTION(mysql)
MySG(connect_error) = NULL;
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;
}
/* }}} */
@@ -445,9 +511,10 @@ PHP_RINIT_FUNCTION(mysql)
*/
PHP_RSHUTDOWN_FUNCTION(mysql)
{
-#ifdef ZTS
+#if !defined(HAVE_MYSQLND) && defined(ZTS)
mysql_thread_end();
#endif
+
if (MySG(trace_mode)) {
if (MySG(result_allocated)){
php_error_docref("function.mysql-free-result" TSRMLS_CC, E_WARNING, "%lu result set(s) not freed. Use mysql_free_result to free result sets which were requested using mysql_query()", MySG(result_allocated));
@@ -457,6 +524,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;
}
/* }}} */
@@ -474,13 +545,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();
DISPLAY_INI_ENTRIES();
@@ -499,6 +583,10 @@ 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;
@@ -558,10 +646,10 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
if (persistent) {
client_flags= new_link;
}
-
+
/* disable local infile option for open_basedir */
- if (PG(open_basedir) && strlen(PG(open_basedir)) && (client_flags & CLIENT_LOCAL_FILES)) {
- client_flags ^= CLIENT_LOCAL_FILES;
+ if (((PG(open_basedir) && PG(open_basedir)[0] != '\0')) && (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);
@@ -570,7 +658,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
/* We cannot use mysql_port anymore in windows, need to use
* mysql_real_connect() to set the port.
*/
- if (host_len && (tmp=strchr(host_and_port, ':'))) {
+ if (host_and_port && (tmp=strchr(host_and_port, ':'))) {
host = estrndup(host_and_port, tmp-host_and_port);
free_host = 1;
tmp++;
@@ -602,12 +690,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();
@@ -615,40 +703,53 @@ 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 (UG(unicode)) {
+ mysql_options(mysql->conn, MYSQL_SET_CHARSET_NAME, "utf8");
}
- 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));
+#if !defined(HAVE_MYSQLND)
if (UG(unicode)) {
#ifdef MYSQL_HAS_SET_CHARSET
- mysql_set_character_set(&mysql->conn, "utf8");
+ mysql_set_character_set(mysql->conn, "utf8");
#else
- char *encoding = (char *)mysql_character_set_name(&mysql->conn);
- if (strcasecmp(encoding, "utf8")) {
+ char *encoding = mysql_character_set_name(mysql->conn);
+ if (strcasecmp((char*)encoding, "utf8")) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect in Unicode mode. Client library was compiled with default charset %s", encoding);
MYSQL_DO_CONNECT_RETURN_FALSE();
}
#endif
}
+#endif
/* hash it up */
Z_TYPE(new_le) = le_plink;
@@ -664,36 +765,42 @@ 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;
/* 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) {
+ if (mysql_ping(mysql->conn)) {
+ if (mysql_errno(mysql->conn) == 2006) {
+ if (UG(unicode)) {
+ mysql_options(mysql->conn, MYSQL_SET_CHARSET_NAME, "utf8");
+ }
+#ifndef HAVE_MYSQLND
+ if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
#else
- if (!strcasecmp(mysql_stat(le->ptr), "mysql server has gone away")) { /* the link died */
+ if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,
+ port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
#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) {
+ {
+ 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();
+ }
+#if !defined(HAVE_MYSQLND)
+ if (UG(unicode)) {
+#ifdef MYSQL_HAS_SET_CHARSET
+ mysql_set_character_set(mysql->conn, "utf8");
#else
- if (mysql_connect(le->ptr, host, user, passwd)==NULL) {
+ char *encoding = mysql_character_set_name(mysql->conn);
+ if (strcasecmp((char*)encoding, "utf8")) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect in Unicode mode. Client library was compiled with default charset %s", encoding);
+ MYSQL_DO_CONNECT_RETURN_FALSE();
+ }
#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();
+ }
+#endif
+ mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));
}
}
-#if MYSQL_VERSION_ID < 32231
- signal(SIGPIPE, handler);
-#endif
-
- mysql = (php_mysql_conn *) le->ptr;
- mysql->active_result_id = 0;
}
ZEND_REGISTER_RESOURCE(return_value, mysql, le_plink);
} else { /* non persistent */
@@ -726,7 +833,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();
@@ -734,29 +841,60 @@ 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 (UG(unicode)) {
+ mysql_options(mysql->conn, MYSQL_SET_CHARSET_NAME, "utf8");
+ }
if (connect_timeout != -1) {
- mysql_options(&mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout);
+ 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
efree(hashed_details);
+ /* free mysql structure */
+#ifdef HAVE_MYSQLND
+ mysqli_close(mysql->conn, MYSQLI_CLOSE_DISCONNECTED);
+#endif
efree(mysql);
MYSQL_DO_CONNECT_RETURN_FALSE();
}
+#if !defined(HAVE_MYSQLND)
+ if (UG(unicode)) {
+#ifdef MYSQL_HAS_SET_CHARSET
+ mysql_set_character_set(mysql->conn, "utf8");
+#else
+ char *encoding = mysql_character_set_name(mysql->conn);
+ if (strcasecmp((char*)encoding, "utf8")) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect in Unicode mode. Client library was compiled with default charset %s", encoding);
+ MYSQL_DO_CONNECT_RETURN_FALSE();
+ }
+#endif
+ }
+#endif
+ 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);
@@ -879,14 +1017,11 @@ PHP_FUNCTION(mysql_select_db)
Returns a string that represents the client library version */
PHP_FUNCTION(mysql_get_client_info)
{
- char *info;
-
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
- info = (char *)mysql_get_client_info();
- RETURN_UTF8_STRING(info, ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_get_client_info(), ZSTR_DUPLICATE);
}
/* }}} */
@@ -897,7 +1032,6 @@ PHP_FUNCTION(mysql_get_host_info)
zval **mysql_link;
int id;
php_mysql_conn *mysql;
- char *info;
switch(ZEND_NUM_ARGS()) {
case 0:
@@ -917,8 +1051,7 @@ PHP_FUNCTION(mysql_get_host_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- info = (char *)mysql_get_host_info(&mysql->conn);
- RETURN_UTF8_STRING(info, ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_get_host_info(mysql->conn), ZSTR_DUPLICATE);
}
/* }}} */
@@ -948,7 +1081,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));
}
/* }}} */
@@ -978,7 +1111,7 @@ PHP_FUNCTION(mysql_get_server_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_UTF8_STRING((char *)mysql_get_server_info(&mysql->conn), ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_get_server_info(mysql->conn), ZSTR_DUPLICATE);
}
/* }}} */
@@ -1002,7 +1135,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_UTF8_STRING(str,ZSTR_DUPLICATE);
} else {
RETURN_FALSE;
@@ -1028,7 +1161,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));
}
/* }}} */
@@ -1039,6 +1172,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;
@@ -1051,8 +1188,17 @@ PHP_FUNCTION(mysql_stat)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
PHPMY_UNBUFFERED_QUERY_CHECK();
+#ifndef HAVE_MYSQLND
+ if ((stat = (char *)mysql_stat(mysql->conn))) {
+ RETURN_UTF8_STRING(stat, ZSTR_DUPLICATE);
+#else
+ if (mysqlnd_stat(mysql->conn, &stat, &stat_len) == PASS) {
+ RETURN_UTF8_STRINGL(stat, stat_len, ZSTR_AUTOFREE);
+#endif
+ } else {
+ RETURN_FALSE;
+ }
- RETURN_UTF8_STRING((char *)mysql_stat(&mysql->conn), ZSTR_DUPLICATE);
}
/* }}} */
@@ -1075,7 +1221,7 @@ PHP_FUNCTION(mysql_client_encoding)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_UTF8_STRING((char *)mysql_character_set_name(&mysql->conn), ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_character_set_name(mysql->conn), ZSTR_DUPLICATE);
}
/* }}} */
#endif
@@ -1107,7 +1253,7 @@ PHP_FUNCTION(mysql_set_charset)
RETURN_FALSE;
}
- if (!mysql_set_character_set(&mysql->conn, csname)) {
+ if (!mysql_set_character_set(mysql->conn, csname)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -1128,7 +1274,7 @@ PHP_FUNCTION(mysql_create_db)
php_mysql_conn *mysql;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&|r", &db, &db_len, UG(utf8_conv), &mysql_link) == FAILURE) {
- return;
+ RETURN_FALSE;
}
if (ZEND_NUM_ARGS() == 1) {
@@ -1140,7 +1286,7 @@ PHP_FUNCTION(mysql_create_db)
PHPMY_UNBUFFERED_QUERY_CHECK();
- if (mysql_create_db(&mysql->conn, db)==0) {
+ if (mysql_create_db(mysql->conn, db)==0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -1158,7 +1304,7 @@ PHP_FUNCTION(mysql_drop_db)
php_mysql_conn *mysql;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&|r", &db, &db_len, UG(utf8_conv), &mysql_link) == FAILURE) {
- return;
+ RETURN_FALSE;
}
if (ZEND_NUM_ARGS() == 1) {
@@ -1168,7 +1314,7 @@ PHP_FUNCTION(mysql_drop_db)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
- if (mysql_drop_db(&mysql->conn, db)==0) {
+ if (mysql_drop_db(mysql->conn, db)==0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -1195,6 +1341,8 @@ static void php_mysql_do_query_general(char *query, zval **mysql_link, int link_
PHPMY_UNBUFFERED_QUERY_CHECK();
+ MYSQL_DISABLE_MQ;
+#ifndef HAVE_MYSQLND
/* check explain */
if (MySG(trace_mode)) {
if (!strncasecmp("select", query, 6)){
@@ -1202,14 +1350,14 @@ static void php_mysql_do_query_general(char *query, zval **mysql_link, int link_
char *newquery;
spprintf(&newquery, 0, "EXPLAIN %s", query);
- mysql_real_query(&mysql->conn, newquery, strlen(newquery));
+ mysql_real_query(mysql->conn, newquery, strlen(newquery));
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]);
@@ -1221,36 +1369,37 @@ static void php_mysql_do_query_general(char *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, query, strlen(query))!=0) {
+ if (mysql_real_query(mysql->conn, query, strlen(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, query)!=0) {
+ if (mysql_query(mysql->conn, 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 {
@@ -1359,7 +1508,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;
}
@@ -1372,19 +1521,29 @@ PHP_FUNCTION(mysql_list_dbs)
List tables in a MySQL database */
PHP_FUNCTION(mysql_list_tables)
{
- zval *mysql_link;
+ zval *mysql_link = NULL;
char *db;
int id = -1, db_len;
php_mysql_conn *mysql;
MYSQL_RES *mysql_result;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&|r", &db, &db_len, UG(utf8_conv), &mysql_link) == FAILURE) {
- return;
- }
- if (ZEND_NUM_ARGS() == 1) {
- id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
- CHECK_LINK(id);
+
+ switch(ZEND_NUM_ARGS()) {
+ case 1:
+ if (zend_parse_parameters(1 TSRMLS_CC, "s&", &db, &db_len, UG(utf8_conv)) == FAILURE) {
+ RETURN_FALSE;
+ }
+ id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+ CHECK_LINK(id);
+ break;
+ case 2:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&r", &db, &db_len, UG(utf8_conv), &mysql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
}
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
@@ -1394,7 +1553,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;
}
@@ -1414,10 +1573,10 @@ PHP_FUNCTION(mysql_list_fields)
MYSQL_RES *mysql_result;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&s&|r", &db, &db_len, UG(utf8_conv), &table, &table_len, UG(utf8_conv), &mysql_link) == FAILURE) {
- return;
+ RETURN_FALSE;
}
- if (ZEND_NUM_ARGS() == 1) {
+ if (ZEND_NUM_ARGS() < 3) {
id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
CHECK_LINK(id);
}
@@ -1430,7 +1589,7 @@ PHP_FUNCTION(mysql_list_fields)
PHPMY_UNBUFFERED_QUERY_CHECK();
- if ((mysql_result=mysql_list_fields(&mysql->conn, table, NULL))==NULL) {
+ if ((mysql_result=mysql_list_fields(mysql->conn, table, NULL))==NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");
RETURN_FALSE;
}
@@ -1460,7 +1619,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;
@@ -1476,7 +1635,7 @@ PHP_FUNCTION(mysql_list_processes)
PHP_FUNCTION(mysql_error)
{
zval **mysql_link;
- int id;
+ int id = -1;
php_mysql_conn *mysql;
switch(ZEND_NUM_ARGS()) {
@@ -1503,7 +1662,7 @@ PHP_FUNCTION(mysql_error)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_UTF8_STRING((char *)mysql_error(&mysql->conn), ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_error(mysql->conn), ZSTR_DUPLICATE);
}
/* }}} */
@@ -1541,7 +1700,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
/* }}} */
@@ -1574,7 +1733,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;
}
/* }}} */
@@ -1594,6 +1753,7 @@ PHP_FUNCTION(mysql_escape_string)
new_str = (char *) safe_emalloc(str_len, 2, 1);
new_str_len = mysql_escape_string(new_str, str, str_len);
+ /* Now we kind of realloc() by returning a zval pointing to a buffer with enough size */
RETVAL_UTF8_STRINGL(new_str, new_str_len, ZSTR_DUPLICATE);
efree(new_str);
}
@@ -1622,7 +1782,8 @@ 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);
+ /* Now we kind of realloc() by returning a zval pointing to a buffer with enough size */
RETVAL_UTF8_STRINGL(new_str, new_str_len, ZSTR_DUPLICATE);
efree(new_str);
}
@@ -1655,7 +1816,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;
}
/* }}} */
@@ -1667,8 +1828,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()) {
@@ -1695,12 +1858,9 @@ 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) {
+ /* XXX: What about unicode type ??? Please test :) */
switch(Z_TYPE_PP(field)) {
case IS_STRING: {
int i=0;
@@ -1748,11 +1908,20 @@ 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]) {
RETURN_UTF8_STRINGL(sql_row[field_offset], sql_row_lengths[field_offset], ZSTR_DUPLICATE);
} else {
Z_TYPE_P(return_value) = IS_NULL;
}
+#else
+ mysqlnd_result_fetch_field_data(mysql_result, field_offset, return_value);
+#endif
}
/* }}} */
@@ -1794,18 +1963,37 @@ PHP_FUNCTION(mysql_num_fields)
}
/* }}} */
+#define MYSQL_BINARY_CHARSET_NR 63
+
+#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)
+/* we have BIT */
+#define IS_BINARY_DATA(f) (((f)->type == MYSQL_TYPE_TINY_BLOB || (f)->type == MYSQL_TYPE_BLOB || \
+ (f)->type == MYSQL_TYPE_MEDIUM_BLOB || (f)->type == MYSQL_TYPE_LONG_BLOB || \
+ (f)->type == MYSQL_TYPE_BIT || \
+ (f)->type == MYSQL_TYPE_VAR_STRING || (f)->type == MYSQL_TYPE_VARCHAR || \
+ (f)->type == MYSQL_TYPE_STRING)&& (f)->charsetnr == MYSQL_BINARY_CHARSET_NR)
+#else
+#define IS_BINARY_DATA(f) (((f)->type == MYSQL_TYPE_TINY_BLOB || (f)->type == MYSQL_TYPE_BLOB || \
+ (f)->type == MYSQL_TYPE_MEDIUM_BLOB || (f)->type == MYSQL_TYPE_LONG_BLOB || \
+ (f)->type == MYSQL_TYPE_VAR_STRING || \ (f)->type == MYSQL_TYPE_VARCHAR || \
+ (f)->type == MYSQL_TYPE_STRING)&& (f)->charsetnr == MYSQL_BINARY_CHARSET_NR)
+
+#endif
+
/* {{{ php_mysql_fetch_hash
*/
static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type, int expected_args, int into_object)
{
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
if (into_object) {
char *class_name;
@@ -1858,21 +2046,25 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,
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;
MAKE_STD_ZVAL(data);
- if (UG(unicode)) {
+ if (UG(unicode) && !IS_BINARY_DATA(mysql_field)) {
UChar *ustr;
int ulen;
@@ -1920,8 +2112,12 @@ 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
- 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;
@@ -1991,7 +2187,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
}
/* }}} */
@@ -2013,7 +2221,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
}
/* }}} */
@@ -2022,7 +2243,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
}
/* }}} */
@@ -2087,6 +2320,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
@@ -2440,7 +2676,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..5f1123474f 100644
--- a/ext/mysql/php_mysql.h
+++ b/ext/mysql/php_mysql.h
@@ -34,6 +34,29 @@
#include "TSRM.h"
#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef PHP_ATOM_INC
+#include "ext/mysql/php_have_mysqlnd.h" /* HAVE_MYSQLND is defined here if enabled */
+#endif
+
+#if defined(HAVE_MYSQLND)
+#include "ext/mysqli/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 +114,7 @@ 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 +129,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
@@ -125,4 +152,8 @@ ZEND_END_MODULE_GLOBALS(mysql)
#define phpext_mysql_ptr mysql_module_ptr
+
+#include "ext/mysqli/mysqlnd/php_mysqlnd.h"
+
+
#endif /* PHP_MYSQL_H */
diff --git a/ext/mysqli/config.m4 b/ext/mysqli/config.m4
deleted file mode 100644
index 5c78193fbb..0000000000
--- a/ext/mysqli/config.m4
+++ /dev/null
@@ -1,64 +0,0 @@
-dnl
-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]])
-
-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
-
-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
-
- if test "$PHP_MYSQLI" = "yes"; then
- MYSQL_CONFIG=`$php_shtool path mysql_config`
- else
- MYSQL_CONFIG=$PHP_MYSQLI
- fi
-
- MYSQL_LIB_NAME='mysqlclient'
- if test "$PHP_EMBEDDED_MYSQLI" = "yes"; then
- AC_DEFINE(HAVE_EMBEDDED_MYSQLI, 1, [embedded MySQL support enabled])
- MYSQL_LIB_CFG='--libmysqld-libs'
- elif test "$enable_maintainer_zts" = "yes"; then
- MYSQL_LIB_CFG='--libs_r'
- MYSQL_LIB_NAME='mysqlclient_r'
- else
- MYSQL_LIB_CFG='--libs'
- fi
-
- if test -x "$MYSQL_CONFIG" && $MYSQL_CONFIG $MYSQL_LIB_CFG > /dev/null 2>&1; then
- MYSQLI_INCLINE=`$MYSQL_CONFIG --cflags | $SED -e "s/'//g"`
- MYSQLI_LIBLINE=`$MYSQL_CONFIG $MYSQL_LIB_CFG | $SED -e "s/'//g"`
- else
- AC_MSG_RESULT([mysql_config not found])
- AC_MSG_ERROR([Please reinstall the mysql distribution])
- fi
-
- dnl
- dnl Check the library
- dnl
- PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_server_option,
- [
- 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_set_character_set,
- [ ],[
- 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)
- PHP_SUBST(MYSQLI_SHARED_LIBADD)
-fi
diff --git a/ext/mysqli/config.w32 b/ext/mysqli/config.w32
index 789112ea14..876eb9ca54 100644
--- a/ext/mysqli/config.w32
+++ b/ext/mysqli/config.w32
@@ -1,14 +1,64 @@
// $Id$
// vim:ft=javascript
+// Note: The extension name is "mysqli", you enable it with "--with-mysqli".
+// The "--enable-mysqlnd" tells to use "mysqli", if enabled, with 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");
+ARG_ENABLE("mysqlnd", "MySQLi with mysqlnd 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');
- } else {
- WARNING("mysqli not enabled; libraries and headers not found");
- }
+
+ 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_MYSQLND == "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)) {
+ // 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 {
+
+ mysqlnd_source =
+ "mysqlnd.c " +
+// "mysqlnd_alloc.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("mysqli", mysqli_source);
+ // Specify that add "mysqlnd" sources, but use same object file
+ // directory as the "mysqli" sources
+ // FIXME the hard coded "ext/mysqli/mysqlnd" prevents pointing
+ // out sources in another directory? Like above: PHP_MYSQLI + "\\include;"
+ ADD_SOURCES("ext/mysqli/mysqlnd", mysqlnd_source, "mysqli");
+ AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library');
+ AC_DEFINE('HAVE_MYSQLND' , 1, 'MySQL native driver support enabled');
+ }
}
diff --git a/ext/mysqli/config9.m4 b/ext/mysqli/config9.m4
new file mode 100644
index 0000000000..086b744f01
--- /dev/null
+++ b/ext/mysqli/config9.m4
@@ -0,0 +1,103 @@
+dnl
+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].
+ 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" = "mysqlnd"; then
+ dnl This needs to be set in any extension which wishes to use mysqlnd
+ PHP_MYSQLND_ENABLED=yes
+
+elif test "$PHP_MYSQLI" != "no"; then
+
+ if test "$PHP_MYSQLI" = "yes"; then
+ MYSQL_CONFIG=`$php_shtool path mysql_config`
+ else
+ MYSQL_CONFIG=$PHP_MYSQLI
+ fi
+
+ MYSQL_LIB_NAME='mysqlclient'
+ 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
+ extra_sources="mysqli_embedded.c"
+ elif test "$enable_maintainer_zts" = "yes"; then
+ MYSQL_LIB_CFG='--libs_r'
+ MYSQL_LIB_NAME='mysqlclient_r'
+ else
+ MYSQL_LIB_CFG='--libs'
+ fi
+
+ if test -x "$MYSQL_CONFIG" && $MYSQL_CONFIG $MYSQL_LIB_CFG > /dev/null 2>&1; then
+ MYSQLI_INCLINE=`$MYSQL_CONFIG --cflags | $SED -e "s/'//g"`
+ MYSQLI_LIBLINE=`$MYSQL_CONFIG $MYSQL_LIB_CFG | $SED -e "s/'//g"`
+ else
+ AC_MSG_RESULT([mysql_config not found])
+ AC_MSG_ERROR([Please reinstall the mysql distribution])
+ fi
+
+ dnl
+ dnl Check the library
+ dnl
+ PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_server_option,
+ [
+ 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_set_character_set,
+ [ ],[
+ 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
+ ])
+
+ extra_sources="$extra_sources mysqli_repl.c"
+fi
+
+dnl Build extension
+if test -n "$extra_sources" || 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 $extra_sources"
+ PHP_NEW_EXTENSION(mysqli, $mysqli_sources, $ext_shared)
+ PHP_SUBST(MYSQLI_SHARED_LIBADD)
+
+ dnl These 3 lines are neeeded to be able to build ext/mysql and/or ext/mysqli with/without mysqlnd.
+ dnl Need to do this here for the file to be always available.
+ $php_shtool mkdir -p ext/mysqli/
+ echo > ext/mysqli/php_have_mysqlnd.h
+ test "$PHP_MYSQLI" = "mysqlnd" && PHP_DEFINE(HAVE_MYSQLND, 1, [ext/mysqli])
+fi
+
+dnl MySQLnd build
+dnl If some extension uses mysqlnd it will get compiled in PHP whether MYSQLi is enabled or not.
+if test "$PHP_MYSQLND_ENABLED" = "yes"; then
+ AC_CHECK_TYPES([int8, uint8, int16, uint16, int32, uint32, uchar],[],[], [
+ #include <sys/types.h>
+ ])
+
+ AC_CHECK_TYPES([int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t],[],[], [
+ #include <stdint.h>
+ ])
+
+ 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"
+
+ PHP_ADD_SOURCES(ext/mysqli/mysqlnd, $mysqlnd_sources)
+ PHP_ADD_BUILD_DIR([ext/mysqli/mysqlnd])
+ EXT_STATIC="$EXT_STATIC mysqlnd"
+ EXT_CLI_STATIC="$EXT_CLI_STATIC mysqlnd"
+ PHP_INSTALL_HEADERS([ext/mysqli/mysqlnd])
+fi
+
diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c
index cb3de3388a..ce7f7eebd1 100644
--- a/ext/mysqli/mysqli.c
+++ b/ext/mysqli/mysqli.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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$
@@ -28,7 +30,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 +54,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 +70,26 @@ typedef struct _mysqli_prop_handler {
mysqli_write_t write_func;
} mysqli_prop_handler;
+static int le_pmysqli;
+
+/* Destructor for Persistent Connections */
+ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor)
+{
+ if (rsrc->ptr) {
+ MYSQL *mysql = (MYSQL *)rsrc->ptr;
+#if defined(HAVE_MYSQLND)
+ mysqlnd_end_psession(mysql);
+#endif
+ mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT);
+ }
+}
+
+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)
{
@@ -100,25 +128,35 @@ 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;
}
/* }}} */
@@ -157,7 +195,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);
@@ -183,7 +223,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);
}
@@ -262,8 +302,8 @@ zval *mysqli_read_property(zval *object, zval *member, int type TSRMLS_DC)
}
if (ret == SUCCESS) {
-
- if ((!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED)) {
+ if ((!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED))
+ {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %v", obj->zo.ce->name );
retval = EG(uninitialized_zval_ptr);
return(retval);
@@ -340,7 +380,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 &&
@@ -349,6 +388,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;
@@ -366,7 +406,7 @@ static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)
} else if (obj->zo.ce == mysqli_driver_class_entry) {
f.handler = ZEND_FN(mysqli_driver_construct);
} else if (obj->zo.ce == mysqli_warning_class_entry) {
- f.handler = ZEND_FN(mysqli_warning_construct);
+ f.handler = ZEND_MN(mysqli_warning___construct);
}
return (union _zend_function*)&f;
@@ -404,7 +444,7 @@ 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)) { /* stmt object */
+ } 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;
@@ -422,16 +462,21 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_
return retval;
}
/* }}} */
-
-/* {{{ mysqli_module_entry
- */
+
+
+/* Dependancies */
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,
@@ -469,22 +514,27 @@ PHP_INI_BEGIN()
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->max_links = -1;
mysqli_globals->default_port = 0;
mysqli_globals->default_host = NULL;
mysqli_globals->default_user = NULL;
mysqli_globals->default_pw = NULL;
mysqli_globals->default_socket = NULL;
mysqli_globals->reconnect = 0;
+ mysqli_globals->allow_local_infile = 1;
mysqli_globals->report_mode = 0;
mysqli_globals->report_ht = 0;
#ifdef HAVE_EMBEDDED_MYSQLI
@@ -492,6 +542,10 @@ static PHP_GINIT_FUNCTION(mysqli)
#else
mysqli_globals->embedded = 0;
#endif
+#ifdef HAVE_MYSQLND
+ mysqli_globals->cache_size = 0;
+ mysqli_globals->mysqlnd_thd_zval_cache = NULL;
+#endif
}
/* }}} */
@@ -504,6 +558,15 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_INI_ENTRIES();
+#ifndef HAVE_MYSQLND
+ if (mysql_server_init(0, NULL, NULL)) {
+ return FAILURE;
+ }
+#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;
mysqli_object_handlers.read_property = mysqli_read_property;
@@ -513,6 +576,10 @@ PHP_MINIT_FUNCTION(mysqli)
zend_u_hash_init(&classes, 0, NULL, NULL, 1, 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);
@@ -527,33 +594,33 @@ PHP_MINIT_FUNCTION(mysqli)
ce = mysqli_driver_class_entry;
zend_u_hash_init(&mysqli_driver_properties, 0, NULL, NULL, 1, 1);
MYSQLI_ADD_PROPERTIES(&mysqli_driver_properties, mysqli_driver_property_entries);
- zend_hash_add(&classes, ce->name.s, ce->name_length+1, &mysqli_driver_properties, sizeof(mysqli_driver_properties), NULL);
+ zend_u_hash_add(&classes, ZEND_STR_TYPE, ce->name, ce->name_length+1, &mysqli_driver_properties, sizeof(mysqli_driver_properties), NULL);
ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
REGISTER_MYSQLI_CLASS_ENTRY("mysqli", mysqli_link_class_entry, mysqli_link_methods);
ce = mysqli_link_class_entry;
zend_u_hash_init(&mysqli_link_properties, 0, NULL, NULL, 1, 1);
MYSQLI_ADD_PROPERTIES(&mysqli_link_properties, mysqli_link_property_entries);
- zend_hash_add(&classes, ce->name.s, ce->name_length+1, &mysqli_link_properties, sizeof(mysqli_link_properties), NULL);
+ zend_u_hash_add(&classes, ZEND_STR_TYPE, ce->name, ce->name_length+1, &mysqli_link_properties, sizeof(mysqli_link_properties), NULL);
REGISTER_MYSQLI_CLASS_ENTRY("mysqli_warning", mysqli_warning_class_entry, mysqli_warning_methods);
ce = mysqli_warning_class_entry;
ce->ce_flags |= ZEND_ACC_FINAL_CLASS | ZEND_ACC_PROTECTED;
zend_u_hash_init(&mysqli_warning_properties, 0, NULL, NULL, 1, 1);
MYSQLI_ADD_PROPERTIES(&mysqli_warning_properties, mysqli_warning_property_entries);
- zend_hash_add(&classes, ce->name.s, ce->name_length+1, &mysqli_warning_properties, sizeof(mysqli_warning_properties), NULL);
+ zend_u_hash_add(&classes, ZEND_STR_TYPE, ce->name, ce->name_length+1, &mysqli_warning_properties, sizeof(mysqli_warning_properties), NULL);
REGISTER_MYSQLI_CLASS_ENTRY("mysqli_result", mysqli_result_class_entry, mysqli_result_methods);
ce = mysqli_result_class_entry;
zend_u_hash_init(&mysqli_result_properties, 0, NULL, NULL, 1, 1);
MYSQLI_ADD_PROPERTIES(&mysqli_result_properties, mysqli_result_property_entries);
- zend_hash_add(&classes, ce->name.s, ce->name_length+1, &mysqli_result_properties, sizeof(mysqli_result_properties), NULL);
+ zend_u_hash_add(&classes, ZEND_STR_TYPE, ce->name, ce->name_length+1, &mysqli_result_properties, sizeof(mysqli_result_properties), NULL);
REGISTER_MYSQLI_CLASS_ENTRY("mysqli_stmt", mysqli_stmt_class_entry, mysqli_stmt_methods);
ce = mysqli_stmt_class_entry;
zend_u_hash_init(&mysqli_stmt_properties, 0, NULL, NULL, 1, 1);
MYSQLI_ADD_PROPERTIES(&mysqli_stmt_properties, mysqli_stmt_property_entries);
- zend_hash_add(&classes, ce->name.s, ce->name_length+1, &mysqli_stmt_properties, sizeof(mysqli_stmt_properties), NULL);
+ zend_u_hash_add(&classes, ZEND_STR_TYPE, ce->name, ce->name_length+1, &mysqli_stmt_properties, sizeof(mysqli_stmt_properties), NULL);
/* mysqli_options */
@@ -562,6 +629,12 @@ 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_NUMERIC_AND_DATETIME_AS_UNICODE", MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, 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);
@@ -583,7 +656,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);
@@ -591,7 +664,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
@@ -637,17 +710,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);
@@ -662,10 +737,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;
}
/* }}} */
@@ -674,6 +745,7 @@ PHP_MINIT_FUNCTION(mysqli)
*/
PHP_MSHUTDOWN_FUNCTION(mysqli)
{
+#ifndef HAVE_MYSQLND
#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 */
@@ -683,6 +755,10 @@ PHP_MSHUTDOWN_FUNCTION(mysqli)
#else
mysql_server_end();
#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);
@@ -700,13 +776,16 @@ PHP_MSHUTDOWN_FUNCTION(mysqli)
*/
PHP_RINIT_FUNCTION(mysqli)
{
-#ifdef ZTS
+#if !defined(HAVE_MYSQLND) && defined(ZTS)
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;
}
@@ -716,16 +795,20 @@ PHP_RINIT_FUNCTION(mysqli)
*/
PHP_RSHUTDOWN_FUNCTION(mysqli)
{
-#ifdef ZTS
+#if !defined(HAVE_MYSQLND) && defined(ZTS)
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)
@@ -733,9 +816,23 @@ PHP_MINFO_FUNCTION(mysqli)
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());
+#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();
@@ -760,7 +857,7 @@ ZEND_FUNCTION(mysqli_stmt_construct)
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);
@@ -814,15 +911,19 @@ ZEND_FUNCTION(mysqli_result_construct)
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;
}
@@ -839,11 +940,19 @@ 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;
}
/* }}} */
+#define MYSQL_BINARY_CHARSET_NR 63
+
+#define IS_BINARY_DATA(f) (((f).type == MYSQL_TYPE_TINY_BLOB || (f).type == MYSQL_TYPE_BLOB || \
+ (f).type == MYSQL_TYPE_MEDIUM_BLOB || (f).type == MYSQL_TYPE_LONG_BLOB || \
+ (f).type == MYSQL_TYPE_BIT || (f).type == MYSQL_TYPE_VAR_STRING || (f).type == MYSQL_TYPE_VARCHAR ||\
+ (f).type == MYSQL_TYPE_STRING) && (f).charsetnr == MYSQL_BINARY_CHARSET_NR)
+
+
/* {{{ php_mysqli_fetch_into_hash
*/
void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags, int into_object)
@@ -851,17 +960,20 @@ 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;
- void *classname;
- int classname_len;
- zend_uchar classname_type;
+#endif
if (into_object) {
+ void *classname;
+ int classname_len;
+ zend_uchar classname_type;
+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|Tz", &mysql_result, mysqli_result_class_entry,
&classname, &classname_len, &classname_type, &ctor_params) == FAILURE) {
return;
@@ -891,18 +1003,17 @@ 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();
}
- if (fetchtype & MYSQLI_ASSOC) {
- fields = mysql_fetch_fields(result);
- }
+ fields = mysql_fetch_fields(result);
array_init(return_value);
field_len = mysql_fetch_lengths(result);
@@ -912,8 +1023,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
zval *res;
MAKE_STD_ZVAL(res);
-
- if (UG(unicode)) {
+ if (UG(unicode) && !IS_BINARY_DATA(fields[i])) {
UChar *ustr;
int ulen;
@@ -946,20 +1056,32 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
add_index_null(return_value, i);
}
if (fetchtype & MYSQLI_ASSOC) {
- add_assoc_null(return_value, fields[i].name);
+ if (UG(unicode)) {
+ UChar *ustr;
+ int ulen;
+
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, fields[i].name, strlen(fields[i].name) TSRMLS_CC);
+ add_u_assoc_null(return_value, IS_UNICODE, ZSTR(ustr));
+ efree(ustr);
+ } else {
+ add_assoc_null(return_value, fields[i].name);
+ }
}
}
}
+#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;
@@ -971,7 +1093,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;
@@ -999,9 +1121,9 @@ 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);
+ zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %v::%v()", ce->name, ce->constructor->common.function_name);
} else {
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
@@ -1011,7 +1133,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
efree(fci.params);
}
} else if (ctor_params) {
- zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
+ zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Class %v does not have a constructor hence you cannot use ctor_params", ce->name);
}
}
}
@@ -1049,25 +1171,34 @@ 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, LOCAL_INFILE_ERROR_LEN-1);\
+php_error_docref(NULL TSRMLS_CC, E_WARNING, dest);
/* {{{ void php_set_local_infile_handler_default
*/
void php_set_local_infile_handler_default(MY_MYSQL *mysql) {
/* register internal callback functions */
+#if !defined(HAVE_MYSQLND)
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;
+ }
+#else
+ mysqlnd_local_infile_default(mysql->mysql, TRUE);
+#endif
}
/* }}} */
+#if !defined(HAVE_MYSQLND)
/* {{{ php_local_infile_init
*/
int php_local_infile_init(void **ptr, const char *filename, void *userdata)
{
mysqli_local_infile *data;
- MY_MYSQL *mysql;
- php_stream_context *context = NULL;
+ MY_MYSQL *mysql;
+ php_stream_context *context = NULL;
TSRMLS_FETCH();
@@ -1092,7 +1223,7 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)
mysql->li_stream = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context);
if (mysql->li_stream == NULL) {
- snprintf((char *)data->error_msg, sizeof(data->error_msg), "Can't find file '%-.64s'.", filename);
+ snprintf((char *)data->error_msg, sizeof(data->error_msg), "Can't find file '%-.64s'.", filename);
return 1;
}
@@ -1105,8 +1236,8 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)
/* {{{ int php_local_infile_read */
int php_local_infile_read(void *ptr, char *buf, uint buf_len)
{
- mysqli_local_infile *data;
- MY_MYSQL *mysql;
+ mysqli_local_infile *data;
+ MY_MYSQL *mysql;
zval ***callback_args;
zval *retval;
zval *fp;
@@ -1121,9 +1252,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));
@@ -1139,15 +1268,15 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)
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) {
@@ -1156,22 +1285,26 @@ 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;
}
-
+
FREE_CALLBACK_ARGS(callback_args, 1, argc);
efree(fp);
return rc;
@@ -1187,7 +1320,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;
}
@@ -1195,10 +1328,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();
@@ -1213,9 +1346,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 4535ba5516..4d806018e6 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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,7 +29,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
/* {{{ proto mixed mysqli_affected_rows(object link) U
Get number of affected rows in previous MySQL operation */
@@ -56,102 +58,54 @@ PHP_FUNCTION(mysqli_affected_rows)
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);
- if (mysql_autocommit(mysql->mysql, (my_bool)automode)) {
+ if (mysql_autocommit(mysql->mysql, automode)) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
-
-/* {{{ 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)
+/* {{{ 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 types_len;
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, &types_len, UG(utf8_conv)) == 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 (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 != 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);
- zend_wrong_param_count(TSRMLS_C);
- RETURN_FALSE;
- }
-
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;
@@ -168,86 +122,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;
+ 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_1;
+ 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;
-end_1:
- efree(args);
- efree(bind);
+ ret = mysqlnd_stmt_bind_param(stmt->stmt, params);
+
+end:
+ return ret;
}
+#endif
/* }}} */
+/* {{{ 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 num_vars;
+ int start = 2;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ char *types;
+ int types_len;
+ unsigned long rc;
-/* {{{ proto bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...]) U
- Bind variables to a prepared statement for result storage */
+ /* calculate and check number of parameters */
+ if (argc < 2) {
+ /* there has to be at least one pair */
+ WRONG_PARAM_COUNT;
+ }
-/* TODO:
- do_alloca, free_alloca
-*/
+ if (zend_parse_method_parameters((getThis()) ? 1:2 TSRMLS_CC, getThis(), "Os&", &mysql_stmt, mysqli_stmt_class_entry,
+ &types, &types_len, UG(utf8_conv)) == FAILURE) {
+ return;
+ }
-PHP_FUNCTION(mysqli_stmt_bind_result)
-{
- 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;
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+ num_vars = argc - 1;
if (getThis()) {
- start = 0;
+ start = 1;
+ } else {
+ /* ignore handle parameter in procedural interface*/
+ --num_vars;
}
-
- if (zend_parse_method_parameters((getThis()) ? 0:1 TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
- return;
+ if (!types_len) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type or no types specified");
+ RETURN_FALSE;
}
- MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+ 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 (argc < (getThis() ? 1 : 2)) {
- WRONG_PARAM_COUNT;
+ 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) {
@@ -273,7 +303,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;
@@ -310,7 +340,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;
@@ -329,7 +359,10 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DECIMAL:
#ifdef FIELD_TYPE_NEWDECIMAL
@@ -387,7 +420,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);
@@ -396,10 +428,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);
}
/* }}} */
@@ -407,10 +498,10 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
Change logged-in user of the active connection */
PHP_FUNCTION(mysqli_change_user)
{
- MY_MYSQL *mysql;
- zval *mysql_link = NULL;
- char *user, *password, *dbname;
- int user_len, password_len, dbname_len;
+ MY_MYSQL *mysql;
+ 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(), "Os&s&s&", &mysql_link, mysqli_link_class_entry,
@@ -435,9 +526,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;
- char *csname;
+ 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;
@@ -445,9 +535,7 @@ PHP_FUNCTION(mysqli_character_set_name)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- csname = (char *)mysql_character_set_name(mysql->mysql);
-
- RETURN_UTF8_STRING(csname, ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_character_set_name(mysql->mysql), ZSTR_DUPLICATE);
}
/* }}} */
@@ -455,8 +543,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;
@@ -464,10 +552,14 @@ PHP_FUNCTION(mysqli_close)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED);
- mysql_close(mysql->mysql);
php_clear_mysql(mysql);
- efree(mysql);
+
+ if (!mysql->persistent) {
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT);
+ }
+
MYSQLI_CLEAR_RESOURCE(&mysql_link);
+ efree(mysql);
RETURN_TRUE;
}
/* }}} */
@@ -476,8 +568,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;
@@ -495,8 +587,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;
@@ -504,13 +596,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);
@@ -534,25 +626,20 @@ PHP_FUNCTION(mysqli_debug)
}
/* }}} */
+
/* {{{ proto bool mysqli_dump_debug_info(object link) U
*/
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))
}
/* }}} */
@@ -560,8 +647,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;
@@ -576,16 +663,14 @@ PHP_FUNCTION(mysqli_errno)
PHP_FUNCTION(mysqli_error)
{
MY_MYSQL *mysql;
- zval *mysql_link;
- char *strerr;
+ 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);
- strerr = (char *)mysql_error(mysql->mysql);
- RETURN_UTF8_STRING(strerr, ZSTR_DUPLICATE);
+ RETURN_UTF8_STRING((char *)mysql_error(mysql->mysql), ZSTR_DUPLICATE);
}
/* }}} */
@@ -593,26 +678,29 @@ 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) {
case MYSQL_TYPE_VAR_STRING:
if (UG(unicode) && Z_TYPE_P(stmt->param.vars[i]) == IS_UNICODE) {
- zend_unicode_to_string(UG(utf8_conv), (char **)&stmt->stmt->params[i].buffer, (int *)&stmt->stmt->params[i].buffer_length,
+ zend_unicode_to_string(UG(utf8_conv), (char **)&stmt->stmt->params[i].buffer, (int *)&stmt->stmt->params[i].buffer_length,
Z_USTRVAL_PP(&stmt->param.vars[i]), Z_USTRLEN_PP(&stmt->param.vars[i]) TSRMLS_CC);
} else {
if (Z_TYPE_P(stmt->param.vars[i]) != IS_STRING) {
- convert_to_string_ex(&stmt->param.vars[i]);
+ convert_to_string_ex(&stmt->param.vars[i]);
}
stmt->stmt->params[i].buffer = Z_STRVAL_PP(&stmt->param.vars[i]);
stmt->stmt->params[i].buffer_length = Z_STRLEN_PP(&stmt->param.vars[i]);
@@ -620,18 +708,20 @@ 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);
RETVAL_FALSE;
@@ -640,45 +730,69 @@ PHP_FUNCTION(mysqli_stmt_execute)
}
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);
}
+#ifndef HAVE_MYSQLND
/* free converted utf8 strings */
- if (UG(unicode)) {
+ if (UG(unicode)) {
for (i = 0; i < stmt->param.var_cnt; i++) {
- if (stmt->stmt->params[i].buffer_type == MYSQL_TYPE_VAR_STRING) {
+ if (stmt->stmt->params[i].buffer_type == MYSQL_TYPE_VAR_STRING && Z_TYPE_P(stmt->param.vars[i]) == IS_UNICODE) {
efree(stmt->stmt->params[i].buffer);
}
}
}
+#endif
}
/* }}} */
-/* {{{ proto mixed mysqli_stmt_fetch(object stmt) U
+
+
+#if !defined(HAVE_MYSQLND)
+#define MYSQL_BINARY_CHARSET_NR 63
+
+#if MYSQL_VERSION_ID > 50002
+/* We have BIT */
+#define IS_BINARY_DATA(f) (((f).type == MYSQL_TYPE_TINY_BLOB || (f).type == MYSQL_TYPE_BLOB || \
+ (f).type == MYSQL_TYPE_MEDIUM_BLOB || (f).type == MYSQL_TYPE_LONG_BLOB || \
+ (f).type == MYSQL_TYPE_BIT || (f).type == MYSQL_TYPE_VAR_STRING || (f).type == MYSQL_TYPE_VARCHAR ||\
+ (f).type == MYSQL_TYPE_STRING) && (f).charsetnr == MYSQL_BINARY_CHARSET_NR)
+#else
+/* No BIT */
+#define IS_BINARY_DATA(f) (((f).type == MYSQL_TYPE_TINY_BLOB || (f).type == MYSQL_TYPE_BLOB || \
+ (f).type == MYSQL_TYPE_MEDIUM_BLOB || (f).type == MYSQL_TYPE_LONG_BLOB || \
+ (f).type == MYSQL_TYPE_VAR_STRING || (f).type == MYSQL_TYPE_VARCHAR ||\
+ (f).type == MYSQL_TYPE_STRING) && (f).charsetnr == MYSQL_BINARY_CHARSET_NR)
+#endif
+
+
+/* {{{ 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;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ unsigned int i;
ulong ret;
- unsigned int uval;
+ unsigned int uval;
my_ulonglong llval;
+ MYSQL_RES *result_metadata = NULL;
+ MYSQL_FIELD *fields;
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);
-
- /* reset buffers */
-
+ /* 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);
}
}
+ result_metadata = mysql_stmt_result_metadata(stmt->stmt);
+ fields = mysql_fetch_fields(result_metadata);
ret = mysql_stmt_fetch(stmt->stmt);
#ifdef MYSQL_DATA_TRUNCATED
if (!ret || ret == MYSQL_DATA_TRUNCATED) {
@@ -702,11 +816,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;
@@ -714,7 +828,7 @@ 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 digits - ALWAYS */
@@ -724,6 +838,7 @@ PHP_FUNCTION(mysqli_stmt_fetch)
}
break;
}
+#endif
}
if (stmt->stmt->fields[i].flags & UNSIGNED_FLAG) {
ZVAL_LONG(stmt->result.vars[i], *(unsigned int *)stmt->result.buf[i].val);
@@ -738,11 +853,11 @@ 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) ||
+ (!uns && (( L64(2147483647) < (my_longlong) llval) ||
(L64(-2147483648) > (my_longlong) llval))))
{
#endif
@@ -764,21 +879,27 @@ PHP_FUNCTION(mysqli_stmt_fetch)
}
#endif
else {
+ size_t copy_len;
#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_UTF8_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->stmt->bind[i].buffer_length, ZSTR_DUPLICATE);
- } else {
-#else
- {
+ copy_len = stmt->stmt->bind[i].buffer_length;
+ } else
#endif
- ZVAL_UTF8_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->result.buf[i].buflen, ZSTR_DUPLICATE);
+ {
+ copy_len = stmt->result.buf[i].buflen;
}
-
- }
+ if (!IS_BINARY_DATA(fields[i])) {
+ ZVAL_UTF8_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val,
+ copy_len, ZSTR_DUPLICATE);
+ } else {
+ ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val,
+ copy_len, 1);
+ }
+ }
break;
default:
- break;
+ break;
}
} else {
ZVAL_NULL(stmt->result.vars[i]);
@@ -787,6 +908,10 @@ PHP_FUNCTION(mysqli_stmt_fetch)
} else {
MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
}
+ if (result_metadata) {
+ mysql_free_result(result_metadata);
+ result_metadata = NULL;
+ }
switch (ret) {
case 0:
@@ -808,8 +933,44 @@ 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_utf8_string(value, "name",(field->name ? field->name : ""), ZSTR_DUPLICATE);
@@ -825,14 +986,15 @@ static void php_add_field_properties(zval *value, MYSQL_FIELD *field TSRMLS_DC)
add_property_long(value, "type", field->type);
add_property_long(value, "decimals", field->decimals);
}
+/* }}} */
/* {{{ proto mixed mysqli_fetch_field (object result) U
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;
@@ -886,8 +1048,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;
@@ -917,7 +1079,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;
}
@@ -931,7 +1093,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]);
}
}
/* }}} */
@@ -940,17 +1102,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) U
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;
@@ -979,7 +1152,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;
}
@@ -996,7 +1169,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));
}
/* }}} */
@@ -1013,8 +1186,8 @@ 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);
}
/* }}} */
@@ -1041,7 +1214,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;
@@ -1056,14 +1229,13 @@ 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));
}
/* }}} */
@@ -1073,7 +1245,7 @@ PHP_FUNCTION(mysqli_get_proto_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;
@@ -1106,14 +1278,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_UTF8_STRING((mysql->mysql->info) ? mysql->mysql->info : "", ZSTR_DUPLICATE);
+ info = mysql_info(mysql->mysql);
+ RETURN_UTF8_STRING((info) ? info : "", ZSTR_DUPLICATE);
}
/* }}} */
@@ -1124,7 +1298,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;
}
@@ -1146,8 +1325,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;
@@ -1162,15 +1341,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;
@@ -1192,11 +1376,15 @@ PHP_FUNCTION(mysqli_set_local_infile_default)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+#if !defined(HAVE_MYSQLND)
if (mysql->li_read) {
efree(Z_STRVAL_P(mysql->li_read));
zval_dtor(mysql->li_read);
mysql->li_read = NULL;
}
+#else
+ mysqlnd_local_infile_default(mysql->mysql, TRUE);
+#endif
}
/* }}} */
@@ -1205,7 +1393,7 @@ PHP_FUNCTION(mysqli_set_local_infile_default)
PHP_FUNCTION(mysqli_set_local_infile_handler)
{
MY_MYSQL *mysql;
- zval *mysql_link;
+ zval *mysql_link;
zval callback_name;
zval *callback_func;
@@ -1224,13 +1412,21 @@ 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 %R", Z_TYPE(callback_name), Z_UNIVAL(callback_name));
zval_dtor(&callback_name);
- RETURN_FALSE;
+ RETURN_FALSE;
}
zval_dtor(&callback_name);
/* save callback function */
- ALLOC_ZVAL(mysql->li_read);
- ZVAL_STRING(mysql->li_read, callback_func->value.str.val, 1);
+#if !defined(HAVE_MYSQLND)
+ 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);
+#else
+ mysqlnd_set_local_infile_handler(mysql->mysql, callback_func->value.str.val);
+#endif
RETURN_TRUE;
}
@@ -1240,8 +1436,8 @@ PHP_FUNCTION(mysqli_set_local_infile_handler)
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;
@@ -1255,14 +1451,20 @@ PHP_FUNCTION(mysqli_more_results)
/* {{{ proto bool mysqli_next_result(object link) U
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));
}
/* }}} */
@@ -1295,7 +1497,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);
}
@@ -1308,12 +1510,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;
@@ -1340,15 +1542,16 @@ PHP_FUNCTION(mysqli_options)
}
RETURN_BOOL(!ret);
-}
+}
/* }}} */
+
/* {{{ proto bool mysqli_ping(object link) U
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) {
@@ -1367,11 +1570,11 @@ PHP_FUNCTION(mysqli_ping)
PHP_FUNCTION(mysqli_prepare)
{
MY_MYSQL *mysql;
- MY_STMT *stmt;
- char *query;
- int query_len;
+ 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, UG(utf8_conv)) == FAILURE) {
@@ -1379,31 +1582,40 @@ PHP_FUNCTION(mysqli_prepare)
}
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
}
}
@@ -1422,7 +1634,6 @@ PHP_FUNCTION(mysqli_prepare)
RETURN_FALSE;
}
-
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)stmt;
@@ -1436,15 +1647,12 @@ PHP_FUNCTION(mysqli_prepare)
Open a connection to a mysql server */
PHP_FUNCTION(mysqli_real_connect)
{
- MY_MYSQL *mysql;
- char *hostname, *username, *passwd, *dbname, *socket;
- int hostname_len, username_len, passwd_len, dbname_len, 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;
- /* optional parameters have to be initialized */
- hostname = username = dbname = passwd = socket = NULL;
-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|s&s&s&s&ls&l", &mysql_link, mysqli_link_class_entry,
&hostname, &hostname_len, UG(utf8_conv), &username, &username_len, UG(utf8_conv), &passwd, &passwd_len, UG(utf8_conv),
&dbname, &dbname_len, UG(utf8_conv), &port, &socket, &socket_len, UG(utf8_conv), &flags) == FAILURE) {
@@ -1454,36 +1662,64 @@ PHP_FUNCTION(mysqli_real_connect)
if (!socket_len) {
socket = NULL;
}
- /* TODO: default values */
+ 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') {
- flags &= ~CLIENT_LOCAL_FILES;
+ flags ^= CLIENT_LOCAL_FILES;
}
- if (!socket) {
- socket = MyG(default_socket);
+ if (UG(unicode)) {
+ mysql_options(mysql->mysql, MYSQL_SET_CHARSET_NAME, "utf8");
}
- 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);
RETURN_FALSE;
}
+ /* when PHP runs in unicode, set default character set to utf8 */
+ if (UG(unicode)) {
+ mysql->conv = UG(utf8_conv);
+ }
+
+ /* 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));
/* change status */
MYSQLI_SET_STATUS(&mysql_link, MYSQLI_STATUS_VALID);
@@ -1498,8 +1734,8 @@ PHP_FUNCTION(mysqli_real_query)
{
MY_MYSQL *mysql;
zval *mysql_link;
- char *query;
- int query_len;
+ char *query = NULL;
+ 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, UG(utf8_conv)) == FAILURE) {
@@ -1511,27 +1747,26 @@ PHP_FUNCTION(mysqli_real_query)
if (mysql_real_query(mysql->mysql, query, query_len)) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
- RETVAL_FALSE;
- } else {
- if (!mysql_field_count(mysql->mysql)) {
- if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC);
- }
+ RETURN_FALSE;
+ }
+
+ if (!mysql_field_count(mysql->mysql)) {
+ if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
+ php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
- RETVAL_TRUE;
}
+
+ RETURN_TRUE;
}
/* }}} */
/* {{{ proto string mysqli_real_escape_string(object link, string escapestr) U
Escapes special characters in a string for use in a SQL statement, taking into account the current charset of the connection */
PHP_FUNCTION(mysqli_real_escape_string) {
- MY_MYSQL *mysql;
- zval *mysql_link = NULL;
- char *escapestr;
- int escapestr_len;
- char *newstr;
- int newstr_len;
+ MY_MYSQL *mysql;
+ zval *mysql_link = NULL;
+ char *escapestr, *newstr;
+ int escapestr_len, newstr_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os&", &mysql_link, mysqli_link_class_entry,
&escapestr, &escapestr_len, UG(utf8_conv)) == FAILURE) {
@@ -1543,7 +1778,7 @@ PHP_FUNCTION(mysqli_real_escape_string) {
newstr_len = mysql_real_escape_string(mysql->mysql, newstr, escapestr, escapestr_len);
newstr = erealloc(newstr, newstr_len + 1);
- RETURN_UTF8_STRING(newstr, 0);
+ RETURN_UTF8_STRING(newstr, ZSTR_AUTOFREE);
}
/* }}} */
@@ -1552,7 +1787,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;
@@ -1571,12 +1806,11 @@ PHP_FUNCTION(mysqli_rollback)
PHP_FUNCTION(mysqli_stmt_send_long_data)
{
MY_STMT *stmt;
- zval *mysql_stmt;
- long param_nr;
+ 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, UG(utf8_conv)) == FAILURE) {
return;
@@ -1586,21 +1820,21 @@ PHP_FUNCTION(mysqli_stmt_send_long_data)
if (param_nr < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter number");
RETURN_FALSE;
- }
+ }
if (mysql_stmt_send_long_data(stmt->stmt, param_nr, data, data_len)) {
RETURN_FALSE;
- } else {
- RETURN_TRUE;
}
+ RETURN_TRUE;
}
/* }}} */
+
/* {{{ proto mixed mysqli_stmt_affected_rows(object stmt) U
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) {
@@ -1620,17 +1854,17 @@ PHP_FUNCTION(mysqli_stmt_affected_rows)
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;
}
@@ -1640,9 +1874,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;
@@ -1662,7 +1896,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) {
@@ -1678,8 +1912,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;
@@ -1695,9 +1929,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;
@@ -1712,9 +1946,9 @@ PHP_FUNCTION(mysqli_stmt_insert_id)
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;
}
@@ -1726,10 +1960,10 @@ PHP_FUNCTION(mysqli_stmt_param_count)
/* {{{ proto bool mysqli_stmt_reset(object stmt) U
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;
@@ -1748,8 +1982,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) {
@@ -1767,24 +2001,22 @@ PHP_FUNCTION(mysqli_stmt_num_rows)
Select a MySQL database */
PHP_FUNCTION(mysqli_select_db)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
- char *dbname;
- int dbname_len;
-
+ MY_MYSQL *mysql;
+ 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, UG(utf8_conv)) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
-
- if (!mysql_select_db(mysql->mysql, dbname)) {
- RETURN_TRUE;
- } else {
+
+ if (mysql_select_db(mysql->mysql, dbname)) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
RETURN_FALSE;
}
+ RETURN_TRUE;
}
/* }}} */
@@ -1792,8 +2024,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;
@@ -1805,12 +2037,13 @@ PHP_FUNCTION(mysqli_sqlstate)
/* {{{ 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];
- int ssl_parm_len[5];
+ MY_MYSQL *mysql;
+ zval *mysql_link;
+ char *ssl_parm[5];
+ int ssl_parm_len[5], i;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os&s&s&s&s&", &mysql_link, mysqli_link_class_entry,
&ssl_parm[0], &ssl_parm_len[0], UG(utf8_conv), &ssl_parm[1], &ssl_parm_len[1], UG(utf8_conv),
@@ -1820,27 +2053,44 @@ PHP_FUNCTION(mysqli_ssl_set)
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+ for (i = 0; i < 5; i++) {
+ if (!ssl_parm_len[i]) {
+ ssl_parm[i] = NULL;
+ }
+ }
+
mysql_ssl_set(mysql->mysql, ssl_parm[0], ssl_parm[1], ssl_parm[2], ssl_parm[3], ssl_parm[4]);
-
+
RETURN_TRUE;
}
+#endif
/* }}} */
/* {{{ proto mixed mysqli_stat(object link) U
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_UTF8_STRING(stat, ZSTR_DUPLICATE);
+#else
+ if (mysqlnd_stat(mysql->mysql, &stat, &stat_len) == PASS)
+ {
+ RETURN_UTF8_STRINGL(stat, stat_len, ZSTR_AUTOFREE);
+#endif
} else {
RETURN_FALSE;
}
@@ -1853,16 +2103,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;
}
@@ -1875,8 +2132,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;
@@ -1901,7 +2158,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;
@@ -1934,9 +2191,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;
@@ -1962,10 +2219,10 @@ PHP_FUNCTION(mysqli_stmt_init)
*/
PHP_FUNCTION(mysqli_stmt_prepare)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
- char *query;
- int query_len;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ char *query;
+ int query_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os&", &mysql_stmt, mysqli_stmt_class_entry,
&query, &query_len, UG(utf8_conv)) == FAILURE) {
@@ -1987,9 +2244,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) {
@@ -2005,7 +2262,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);
}
/* }}} */
@@ -2013,29 +2270,34 @@ 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) {
- 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) {
+ 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);
@@ -2047,16 +2309,16 @@ PHP_FUNCTION(mysqli_stmt_store_result)
/* {{{ proto string mysqli_stmt_sqlstate(object stmt) U
*/
-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_UTF8_STRING((char *)mysql_stmt_sqlstate(stmt->stmt), ZSTR_DUPLICATE);
}
/* }}} */
@@ -2065,9 +2327,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) {
@@ -2080,22 +2342,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) U
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;
@@ -2112,17 +2375,16 @@ PHP_FUNCTION(mysqli_thread_safe)
{
RETURN_BOOL(mysql_thread_safe());
}
-
/* }}} */
/* {{{ proto mixed mysqli_use_result(object link) U
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;
@@ -2135,12 +2397,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);
}
/* }}} */
@@ -2148,8 +2410,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 37669b1b1d..a76c782c2e 100644
--- a/ext/mysqli/mysqli_driver.c
+++ b/ext/mysqli/mysqli_driver.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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> |
+----------------------------------------------------------------------+
*/
@@ -27,7 +29,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"
@@ -112,7 +114,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_RT_STRING(*retval, MYSQL_SERVER_VERSION, 1);
+ ZVAL_RT_STRING(*retval, (char *)mysql_get_client_info(), 1);
return SUCCESS;
}
/* }}} */
@@ -153,8 +155,10 @@ mysqli_property_entry mysqli_driver_property_entries[] = {
/* {{{ mysqli_driver_methods[]
*/
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..6e831ec3d8 100644
--- a/ext/mysqli/mysqli_embedded.c
+++ b/ext/mysqli/mysqli_embedded.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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> |
+----------------------------------------------------------------------+
*/
@@ -25,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 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 1fa3ecae33..9c00ebde8b 100644
--- a/ext/mysqli/mysqli_exception.c
+++ b/ext/mysqli/mysqli_exception.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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> |
+----------------------------------------------------------------------+
*/
@@ -25,7 +27,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 cb8b78fd03..965379a704 100644
--- a/ext/mysqli/mysqli_fe.c
+++ b/ext/mysqli/mysqli_fe.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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,7 +29,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 +63,24 @@ 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 +89,22 @@ 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
@@ -101,7 +121,6 @@ zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_kill, NULL)
PHP_FE(mysqli_set_local_infile_default, NULL)
PHP_FE(mysqli_set_local_infile_handler, NULL)
- PHP_FE(mysqli_master_query, NULL)
PHP_FE(mysqli_more_results, NULL)
PHP_FE(mysqli_multi_query, NULL)
PHP_FE(mysqli_next_result, NULL)
@@ -116,9 +135,6 @@ 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 @@ 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 @@ 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,32 @@ 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)
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)
PHP_FALIAS(multi_query,mysqli_multi_query,NULL)
PHP_FALIAS(mysqli,mysqli_connect,NULL)
PHP_FALIAS(more_results,mysqli_more_results, NULL)
@@ -214,16 +242,14 @@ 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)
@@ -245,11 +271,13 @@ zend_function_entry mysqli_result_methods[] = {
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 +289,7 @@ zend_function_entry mysqli_result_methods[] = {
* Every user visible function must have an entry in mysqli_stmt_functions[].
*/
zend_function_entry mysqli_stmt_methods[] = {
- PHP_FALIAS(__construct,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 +307,9 @@ 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..de62529a89
--- /dev/null
+++ b/ext/mysqli/mysqli_mysqlnd.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 MYSQL_MYSQLND_H
+#define MYSQL_MYSQLND_H
+
+#include "ext/mysqli/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 55086547d9..d2559f8e01 100644
--- a/ext/mysqli/mysqli_nonapi.c
+++ b/ext/mysqli/mysqli_nonapi.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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,18 +29,26 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
-/* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]]) U
+#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;
MYSQLI_RESOURCE *mysqli_resource;
zval *object = getThis();
- char *hostname, *username, *passwd, *dbname, *socket;
- int hostname_len, username_len, passwd_len, dbname_len, socket_len;
+ 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;
if (getThis() && !ZEND_NUM_ARGS()) {
RETURN_NULL();
@@ -52,27 +62,82 @@ PHP_FUNCTION(mysqli_connect)
return;
}
- if (!socket_len) {
- socket = NULL;
+ if (!socket_len || !socket) {
+ socket = MyG(default_socket);
}
if (!passwd) {
passwd = MyG(default_pw);
- if (!username){
- username = MyG(default_user);
- if (!hostname) {
- hostname = MyG(default_host);
- }
- }
+ passwd_len = strlen(SAFE_STR(passwd));
+ }
+ if (!username){
+ username = MyG(default_user);
+ }
+ if (!hostname || !hostname_len) {
+ hostname = MyG(default_host);
}
-
mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL));
+ if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) {
+ mysql->persistent = persistent = TRUE;
+ hostname += 2;
+
+ if (!strlen(hostname)) {
+ hostname = MyG(default_host);
+ }
+
+ /* caclulate hash length: mysqli_ + Hostname + 5 (Port) + username + dbname + pw */
+ hash_len = 7 + strlen(SAFE_STR(hostname)) + 5 + strlen(SAFE_STR(username))
+ + strlen(SAFE_STR(dbname)) + strlen(SAFE_STR(passwd)) + 1;
+
+ hash_key = emalloc(hash_len);
+ hash_len = snprintf(hash_key, hash_len, "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()) {
+ mysql->mysql = (MYSQL *)le->ptr;
+
+ /* reset variables */
+ /* todo: option for ping or change_user */
+#if G0
+ if (!mysql_change_user(mysql->mysql, username, passwd, dbname)) {
+#endif
+ if (!mysql_ping(mysql->mysql)) {
+#ifdef HAVE_MYSQLND
+ mysqlnd_restart_psession(mysql->mysql);
+#endif
+ goto end;
+ }
+ /*
+ Here we fall if the connection is not ok.
+ When we update EG(persistent_list) with a new connection, this one will
+ get destructed. No need to do it explicitly.
+ */
+ }
+ }
+ }
+
+#if !defined(HAVE_MYSQLND)
if (!(mysql->mysql = mysql_init(NULL))) {
+#else
+ if (!(mysql->mysql = mysqlnd_init(persistent))) {
+#endif
efree(mysql);
+ if (persistent) {
+ efree(hash_key);
+ }
RETURN_FALSE;
}
+ new_connection = TRUE;
+
+ if (UG(unicode)) {
+ mysql_options(mysql->mysql, MYSQL_SET_CHARSET_NAME, "utf8");
+ }
+
#ifdef HAVE_EMBEDDED_MYSQLI
if (hostname_len) {
@@ -83,44 +148,72 @@ PHP_FUNCTION(mysqli_connect)
}
#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);
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_DISCONNECTED);
efree(mysql);
+ if (persistent) {
+ efree(hash_key);
+ }
RETURN_FALSE;
}
-#ifdef HAVE_MYSQLI_SET_CHARSET
/* when PHP runs in unicode, set default character set to utf8 */
if (UG(unicode)) {
- mysql_set_character_set(mysql->mysql, "utf8");
mysql->conv = UG(utf8_conv);
}
-#endif
/* 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:
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) {
+ zend_rsrc_list_entry le;
+
+ le.type = php_le_pmysqli();
+ le.ptr = mysql->mysql;
+
+ /* save persistent connection */
+ if (zend_hash_update(&EG(persistent_list), hash_key, hash_len + 1, (void *)&le,
+ sizeof(le), NULL) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't store persistent connection");
+ }
+ }
+ if (persistent) {
+ efree(hash_key);
+ }
+
+#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 {
@@ -129,6 +222,7 @@ PHP_FUNCTION(mysqli_connect)
}
/* }}} */
+
/* {{{ proto int mysqli_connect_errno(void) U
Returns the numerical value of the error message from last connect command */
PHP_FUNCTION(mysqli_connect_errno)
@@ -149,11 +243,30 @@ PHP_FUNCTION(mysqli_connect_error)
}
/* }}} */
+
/* {{{ proto mixed mysqli_fetch_array (object result [,int resulttype]) U
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
}
/* }}} */
@@ -161,15 +274,97 @@ 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]) U
+ 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]]) U
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 */
}
/* }}} */
@@ -179,8 +374,8 @@ PHP_FUNCTION(mysqli_multi_query)
{
MY_MYSQL *mysql;
zval *mysql_link;
- char *query;
- int query_len;
+ char *query = NULL;
+ 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, UG(utf8_conv)) == FAILURE) {
@@ -190,25 +385,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;
}
/* }}} */
@@ -220,8 +420,8 @@ PHP_FUNCTION(mysqli_query)
zval *mysql_link;
MYSQLI_RESOURCE *mysqli_resource;
MYSQL_RES *result;
- char *query;
- int query_len;
+ char *query = NULL;
+ unsigned int query_len;
unsigned long resultmode = MYSQLI_STORE_RESULT;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os&|l", &mysql_link, mysqli_link_class_entry,
@@ -229,6 +429,10 @@ PHP_FUNCTION(mysqli_query)
return;
}
+ if (!query_len) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query");
+ RETURN_FALSE;
+ }
if (resultmode != MYSQLI_USE_RESULT && resultmode != MYSQLI_STORE_RESULT) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
RETURN_FALSE;
@@ -236,10 +440,6 @@ PHP_FUNCTION(mysqli_query)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- if (!query_len) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query");
- RETURN_FALSE;
- }
MYSQLI_DISABLE_MQ;
if (mysql_real_query(mysql->mysql, query, query_len)) {
@@ -250,7 +450,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;
}
@@ -258,13 +458,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((char *)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));
@@ -274,7 +474,37 @@ PHP_FUNCTION(mysqli_query)
}
/* }}} */
-/* {{{ proto object mysqli_get_warnings(object link) U */
+
+#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) U */
PHP_FUNCTION(mysqli_get_warnings)
{
MY_MYSQL *mysql;
@@ -288,13 +518,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_INITIALIZED;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
}
/* }}} */
@@ -312,14 +542,14 @@ PHP_FUNCTION(mysqli_stmt_get_warnings)
}
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_INITIALIZED;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
}
/* }}} */
@@ -331,23 +561,23 @@ PHP_FUNCTION(mysqli_set_charset)
{
MY_MYSQL *mysql;
zval *mysql_link;
- char *csname;
+ char *cs_name;
int csname_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os&", &mysql_link, mysqli_link_class_entry,
- &csname, &csname_len, UG(utf8_conv)) == FAILURE) {
+ &cs_name, &csname_len, UG(utf8_conv)) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
/* check unicode modus */
/* todo: we need also to support UCS2. This will not work when using SET NAMES */
- if (UG(unicode) && (csname_len != 4 || strncasecmp(csname, "utf8", 4))) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Character set %s is not supported when running PHP with unicode.semantics=On.", csname);
+ if (UG(unicode) && (csname_len != 4 || strncasecmp(cs_name, "utf8", 4))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Character set %s is not supported when running PHP with unicode.semantics=On.", cs_name);
RETURN_FALSE;
}
- if (mysql_set_character_set(mysql->mysql, csname)) {
+ if (mysql_set_character_set(mysql->mysql, cs_name)) {
RETURN_FALSE;
}
RETURN_TRUE;
@@ -362,7 +592,13 @@ 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;
@@ -371,35 +607,50 @@ 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
if (UG(unicode)) {
- UErrorCode status = U_ZERO_ERROR;
- UChar *ustr;
+ UChar *ustr = NULL;
int ulen;
- zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (cs.csname) ? cs.csname : "",
- (cs.csname) ? strlen(cs.csname) : 0 TSRMLS_CC);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (name) ? name : "",
+ (name) ? strlen(name) : 0 TSRMLS_CC);
add_property_unicodel(return_value, "charset", ustr, ulen, 1);
- zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (cs.name) ? cs.name : "",
- (cs.name) ? strlen(cs.name) : 0 TSRMLS_CC);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (collation) ? collation : "",
+ (collation) ? strlen(collation) : 0 TSRMLS_CC);
add_property_unicodel(return_value, "collation", ustr, ulen, 1);
- zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (cs.comment) ? cs.comment : "",
- (cs.comment) ? strlen(cs.comment) : 0 TSRMLS_CC);
- add_property_unicodel(return_value, "comment", ustr, ulen, 1);
- zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (cs.dir) ? cs.dir : "",
- (cs.dir) ? strlen(cs.dir) : 0 TSRMLS_CC);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, (dir) ? dir : "",
+ (dir) ? strlen(dir) : 0 TSRMLS_CC);
add_property_unicodel(return_value, "dir", ustr, ulen, 1);
+ efree(ustr);
} else {
- 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 63c5e01f5d..a6ea40eca5 100644
--- a/ext/mysqli/mysqli_prop.c
+++ b/ext/mysqli/mysqli_prop.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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,7 +29,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 ) { \
@@ -40,7 +42,7 @@
MYSQL *p; \
ALLOC_ZVAL(*retval);\
if (!obj->ptr || !(MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr) { \
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", obj->zo.ce->name);\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %v", obj->zo.ce->name);\
ZVAL_NULL(*retval);\
return SUCCESS; \
} else { \
@@ -52,7 +54,7 @@ if (!obj->ptr || !(MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr) { \
MYSQL_RES *p; \
ALLOC_ZVAL(*retval);\
if (!obj->ptr) { \
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", obj->zo.ce->name);\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %v", obj->zo.ce->name);\
ZVAL_NULL(*retval);\
return SUCCESS; \
} else { \
@@ -65,7 +67,7 @@ if (!obj->ptr) { \
MYSQL_STMT *p; \
ALLOC_ZVAL(*retval);\
if (!obj->ptr) { \
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", obj->zo.ce->name);\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %v", obj->zo.ce->name);\
ZVAL_NULL(*retval);\
return SUCCESS; \
} else { \
@@ -125,7 +127,7 @@ static int link_client_info_read(mysqli_object *obj, zval **retval TSRMLS_DC)
{
ALLOC_ZVAL(*retval);
CHECK_STATUS(MYSQLI_STATUS_INITIALIZED);
- ZVAL_STRING(*retval, MYSQL_SERVER_VERSION, 1);
+ ZVAL_UTF8_STRING(*retval, MYSQL_SERVER_VERSION, ZSTR_DUPLICATE)
return SUCCESS;
}
/* }}} */
@@ -145,7 +147,7 @@ static int link_connect_error_read(mysqli_object *obj, zval **retval TSRMLS_DC)
{
ALLOC_ZVAL(*retval);
CHECK_STATUS(MYSQLI_STATUS_INITIALIZED);
- ZVAL_STRING(*retval, MyG(error_msg), 1);
+ ZVAL_UTF8_STRING(*retval, MyG(error_msg), ZSTR_DUPLICATE)
return SUCCESS;
}
/* }}} */
@@ -221,24 +223,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;
}
diff --git a/ext/mysqli/mysqli_repl.c b/ext/mysqli/mysqli_repl.c
index b12706fa6b..d342ee1452 100644
--- a/ext/mysqli/mysqli_repl.c
+++ b/ext/mysqli/mysqli_repl.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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,7 +29,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 e62106f700..2e9d56d0cb 100644
--- a/ext/mysqli/mysqli_report.c
+++ b/ext/mysqli/mysqli_report.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -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$
@@ -25,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 bool mysqli_report(int flags) U
sets report level */
@@ -45,13 +47,13 @@ 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 c1b5a2bfa4..e1ba41483a 100644
--- a/ext/mysqli/mysqli_warning.c
+++ b/ext/mysqli/mysqli_warning.c
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -12,9 +12,12 @@
| 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$
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -25,55 +28,142 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
+
/* {{{ 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));
+ /*
+ XXX: THIS JUST WON'T WORK IF 'character_set_system' is different
+ than latin1 or utf8 !!!! We have to fix it by querying the server for its
+ character_set_system and create appropriate convertor for anything but
+ utf8/latin1 .
+ */
+ 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_MYSQL);
+ 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 +171,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 +207,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 +219,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 +238,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,14 +256,14 @@ 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;
}
/* }}} */
/* {{{ mysqli_warning_construct(object obj) */
-PHP_FUNCTION(mysqli_warning_construct)
+PHP_METHOD(mysqli_warning, __construct)
{
zval *z;
mysqli_object *obj;
@@ -187,14 +286,16 @@ PHP_FUNCTION(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;
}
@@ -211,18 +312,22 @@ PHP_FUNCTION(mysqli_warning_construct)
}
/* }}} */
+/* {{{ mysqli_warning_methods */
zend_function_entry mysqli_warning_methods[] = {
- PHP_FALIAS(mysqli_warning, mysqli_warning_construct, NULL)
- ZEND_ME(mysqli_warning, next, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(mysqli_warning, __construct, NULL, ZEND_ACC_PROTECTED)
+ PHP_ME(mysqli_warning, next, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
+/* }}} */
+/* {{{ mysqli_warning_property_entries */
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/mysqlnd/config-win.h b/ext/mysqli/mysqlnd/config-win.h
new file mode 100644
index 0000000000..86018221a6
--- /dev/null
+++ b/ext/mysqli/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/mysqli/mysqlnd/mysqlnd.c b/ext/mysqli/mysqlnd/mysqlnd.c
new file mode 100644
index 0000000000..3331c9a79a
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd.c
@@ -0,0 +1,1680 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "ext/standard/basic_functions.h"
+#include "ext/standard/php_lcg.h"
+#include "ext/standard/info.h"
+
+#define MYSQLND_SILENT
+
+/* 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;
+
+
+
+
+static 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;
+
+
+/* {{{ mysqlnd_library_init */
+PHPAPI void mysqlnd_library_init()
+{
+ if (mysqlnd_library_initted == FALSE) {
+ mysqlnd_library_initted = TRUE;
+ _mysqlnd_init_ps_subsystem();
+ mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS));
+#ifdef ZTS
+ mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc();
+#endif
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_library_end */
+PHPAPI void mysqlnd_library_end()
+{
+ if (mysqlnd_library_initted == TRUE) {
+#ifdef ZTS
+ tsrm_mutex_free(mysqlnd_global_stats->LOCK_access);
+#endif
+ 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;
+
+ mysqlnd_local_infile_default(conn, TRUE);
+ if (conn->current_result) {
+ conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC);
+ efree(conn->current_result);
+ conn->current_result = NULL;
+ }
+
+ if (conn->net.stream) {
+ php_stream_free(conn->net.stream, (pers) ? PHP_STREAM_FREE_RSRC_DTOR :
+ PHP_STREAM_FREE_CLOSE);
+ conn->net.stream = NULL;
+ }
+ if (conn->host) {
+ pefree(conn->host, pers);
+ conn->host = NULL;
+ }
+ if (conn->user) {
+ pefree(conn->user, pers);
+ conn->user = NULL;
+ }
+ if (conn->passwd) {
+ pefree(conn->passwd, pers);
+ conn->passwd = NULL;
+ }
+ if (conn->unix_socket) {
+ pefree(conn->unix_socket, pers);
+ conn->unix_socket = NULL;
+ }
+ if (conn->scheme) {
+ pefree(conn->scheme, pers);
+ conn->scheme = NULL;
+ }
+ if (conn->server_version) {
+ pefree(conn->server_version, pers);
+ conn->server_version = NULL;
+ }
+ if (conn->host_info) {
+ pefree(conn->host_info, pers);
+ conn->host_info = NULL;
+ }
+ if (conn->scramble) {
+ pefree(conn->scramble, pers);
+ conn->scramble = NULL;
+ }
+ if (conn->last_message) {
+ pefree(conn->last_message, pers);
+ conn->last_message = NULL;
+ }
+ if (conn->options.charset_name) {
+ 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++) {
+ pefree(conn->options.init_commands[i], pers);
+ }
+ pefree(conn->options.init_commands, pers);
+ conn->options.init_commands = NULL;
+ }
+ if (conn->options.cfg_file) {
+ pefree(conn->options.cfg_file, pers);
+ conn->options.cfg_file = NULL;
+ }
+ if (conn->options.cfg_section) {
+ pefree(conn->options.cfg_section, pers);
+ conn->options.cfg_section = NULL;
+ }
+ if (conn->options.ssl_key) {
+ pefree(conn->options.ssl_key, pers);
+ conn->options.ssl_key = NULL;
+ }
+ if (conn->options.ssl_cert) {
+ pefree(conn->options.ssl_cert, pers);
+ conn->options.ssl_cert = NULL;
+ }
+ if (conn->options.ssl_ca) {
+ pefree(conn->options.ssl_ca, pers);
+ conn->options.ssl_ca = NULL;
+ }
+ if (conn->options.ssl_capath) {
+ pefree(conn->options.ssl_capath, pers);
+ conn->options.ssl_capath = NULL;
+ }
+ if (conn->options.ssl_cipher) {
+ pefree(conn->options.ssl_cipher, pers);
+ conn->options.ssl_cipher = NULL;
+ }
+ if (conn->zval_cache) {
+ mysqlnd_palloc_free_thd_cache_reference(&conn->zval_cache);
+ conn->zval_cache = NULL;
+ }
+ if (conn->qcache) {
+ mysqlnd_qcache_free_cache_reference(&conn->qcache);
+ conn->qcache = NULL;
+ }
+ if (conn->net.cmd_buffer.buffer) {
+ pefree(conn->net.cmd_buffer.buffer, pers);
+ conn->net.cmd_buffer.buffer = NULL;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::dtor */
+static void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC)
+{
+ conn->m->free_contents(conn TSRMLS_CC);
+
+ pefree(conn, conn->persistent);
+}
+/* }}} */
+
+
+/* {{{ 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;
+ 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet",
+ mysqlnd_command_to_text[command]);
+ }
+ } else {
+#ifndef MYSQLND_SILENT
+ php_printf("\tOK from server\n");
+#endif
+ 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->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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet",
+ mysqlnd_command_to_text[command]);
+ }
+ } 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "EOF packet expected, field count wasn't 0xFE but 0x%2X",
+ ok_response.field_count);
+ }
+ } else {
+#ifndef MYSQLND_SILENT
+ php_printf("\tOK from server\n");
+#endif
+ }
+ 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;
+ }
+ 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;
+
+ 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);
+ return FAIL;
+ default:
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+ mysqlnd_out_of_sync);
+ 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) {
+ php_error(E_WARNING, "Error while sending %s packet", mysqlnd_command_to_text[command]);
+ }
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_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.
+ */
+
+ 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)
+{
+ char buffer[2];
+ int2store(buffer, (uint) option);
+ return mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer),
+ PROT_EOF_PACKET, FALSE TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_start_psession */
+PHPAPI void mysqlnd_restart_psession(MYSQLND *conn)
+{
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_REUSED);
+}
+/* }}} */
+
+
+/* {{{ 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, 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;
+
+ php_mysql_packet_greet greet_packet;
+ php_mysql_packet_auth *auth_packet;
+ php_mysql_packet_ok ok_packet;
+
+ if (conn && conn->state != CONN_ALLOCED) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+ mysqlnd_out_of_sync);
+ return NULL;
+ }
+
+ 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);
+ }
+
+ if (conn->persistent) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ /* We should generate something unique */
+ 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);
+ }
+
+ 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);
+ efree(transport);
+ } else {
+ conn->scheme = transport;
+ }
+ 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);
+ if (hashed_details) {
+ 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)) {
+#ifndef MYSQLND_SILENT
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet");
+#endif
+ goto err;
+ } else if (greet_packet.error_no) {
+ SET_CLIENT_ERROR(conn->error_info, greet_packet.error_no,
+ greet_packet.sqlstate, greet_packet.error);
+ goto err;
+ } else if (greet_packet.pre41) {
+ 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 */
+
+ /* 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 = pemalloc(SCRAMBLE_LENGTH, conn->persistent);
+ memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH);
+ PACKET_WRITE(auth_packet, conn);
+
+ if (FAIL == PACKET_READ_ALLOCA(ok_packet, conn) || ok_packet.field_count >= 0xFE) {
+ if (ok_packet.field_count == 0xFE) {
+ /* old authentication with new server !*/
+ 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));
+ }
+#ifndef MYSQLND_SILENT
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "ERROR:%d [SQLSTATE:%s] %s",
+ ok_packet.error_no, ok_packet.sqlstate, ok_packet.error);
+#endif
+ }
+ 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);
+ 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);
+
+ 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 = pemalloc(conn->net.cmd_buffer.length, conn->persistent);
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_SUCCESS);
+
+ {
+ uint as_unicode = 1;
+ conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE,
+ (char *)&as_unicode);
+ }
+
+ return conn;
+ }
+err:
+ PACKET_FREE_ALLOCA(greet_packet);
+ PACKET_FREE(auth_packet);
+ PACKET_FREE_ALLOCA(ok_packet);
+
+ if (errstr) {
+ 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);
+
+ efree(errstr);
+ }
+ if (conn->scheme) {
+ 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);
+ }
+ 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;
+
+ if (PASS != mysqlnd_simple_command(conn, COM_QUERY, query, query_len,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE TSRMLS_CC)) {
+ 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);
+
+ return ret;
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_conn::list_method */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, char *achtung_wild, char *par1 TSRMLS_DC)
+{
+ char *show_query = NULL;
+ size_t show_query_len;
+ MYSQLND_RES *result = NULL;
+
+ 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) {
+ efree(show_query);
+ }
+ 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)
+{
+ return mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"),
+ newstr, escapestr, escapestr_len);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::escape_string */
+static ulong
+MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len)
+{
+ if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
+ return mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
+ }
+ return mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::dump_debug_info */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC)
+{
+ 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;
+ 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);
+
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::ping */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC)
+{
+ enum_func_status ret;
+ ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, 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);
+
+ 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;
+
+ ret = mysqlnd_simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE TSRMLS_CC);
+ if (FAIL == ret) {
+ return FAIL;
+ }
+ PACKET_INIT_ALLOCA(stats_header, PROT_STATS_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(stats_header, conn))) {
+ return FAIL;
+ }
+ *message = stats_header.message;
+ *message_len = stats_header.message_len;
+ /* Ownership transfer */
+ stats_header.message = NULL;
+ PACKET_FREE_ALLOCA(stats_header);
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::kill */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned long pid TSRMLS_DC)
+{
+ enum_func_status ret;
+ char buff[4];
+
+ 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;
+ }
+ 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);
+
+ if (!charset) {
+ SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
+ "Invalid characterset or character set not supported");
+ 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;
+ }
+ efree(query);
+ 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];
+ int1store(bits, options);
+
+ 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];
+ int1store(bits, level);
+
+ return mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_send_close */
+static enum_func_status
+mysqlnd_send_close(MYSQLND * conn TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ switch (conn->state) {
+ case CONN_READY:
+ ret = mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST,
+ conn->tmp_int? TRUE : FALSE 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_CONN_STATISTIC(NULL, STAT_CLOSE_IN_MIDDLE);
+#ifndef MYSQLND_SILENT
+ php_printf("Brutally closing connection [%p][%s]\n", conn, conn->scheme);
+#endif
+ /*
+ 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;
+
+ 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];
+
+ MYSQLND_INC_CONN_STATISTIC(NULL, stat);
+
+ mysqlnd_send_close(conn TSRMLS_CC);
+
+ conn->m->free_reference(conn TSRMLS_CC);
+
+ 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 void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC)
+{
+ 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.
+ */
+ mysqlnd_send_close(conn TSRMLS_CC);
+ conn->m->dtor(conn TSRMLS_CC);
+ }
+}
+/* }}} */
+
+
+/* {{{ 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;
+
+ if (conn->state != CONN_NEXT_RESULT_PENDING) {
+ 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))) {
+#ifndef MYSQLND_SILENT
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error");
+#endif
+ conn->state = CONN_QUIT_SENT;
+ }
+
+ 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;
+
+ if (!user) {
+ user = "";
+ }
+ if (!passwd) {
+ passwd = "";
+ }
+ if (!db) {
+ db = "";
+ }
+
+ /* 1. user ASCIIZ */
+ user_len = strlen(user);
+ memcpy(p, user, MIN(user_len, MYSQLND_MAX_ALLOWED_DB_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)) {
+ 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);
+ }
+ }
+
+ /*
+ Here we should close all statements. Unbuffered queries should not be a
+ problem as we won't allow sending COM_CHANGE_USER.
+ */
+
+ 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)
+{
+ switch (option) {
+ case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
+ conn->options.numeric_and_datetime_as_unicode = *(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:
+ return FAIL;
+ }
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn::use_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+
+ if (!conn->current_result) {
+ 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);
+ 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);
+
+ return result->m.use_result(result, FALSE TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn::store_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+
+ if (!conn->current_result) {
+ 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);
+ return NULL;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
+
+ result = conn->current_result;
+ conn->current_result = NULL;
+
+ return result->m.store_result(result, conn, FALSE TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ 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)
+{
+ mysqlnd_fill_stats_hash(&(conn->stats), return_value TSRMLS_CC ZEND_FILE_LINE_CC);
+}
+/* }}} */
+
+
+MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn);
+
+
+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_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)
+{
+ MYSQLND *ret = pecalloc(1, sizeof(MYSQLND), persistent);
+ SET_ERROR_AFF_ROWS(ret);
+ ret->persistent = persistent;
+
+ ret->m = & mysqlnd_mysqlnd_conn_methods;
+ ret->m->get_reference(ret);
+
+ 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[] */
+};
+/* }}} */
+
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(mysqlnd)
+{
+ mysqlnd_library_init();
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(mysqlnd)
+{
+ mysqlnd_library_end();
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ 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) {
+ 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)
+{
+ 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);
+ zval_dtor(&values);
+ php_info_print_table_end();
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_module_entry
+ */
+zend_module_entry mysqlnd_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "mysqlnd",
+ mysqlnd_functions,
+ PHP_MINIT(mysqlnd),
+ PHP_MSHUTDOWN(mysqlnd),
+ NULL,
+ NULL,
+ PHP_MINFO(mysqlnd),
+ MYSQLND_VERSION,
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+
+/*
+ * 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/mysqli/mysqlnd/mysqlnd.h b/ext/mysqli/mysqlnd/mysqlnd.h
new file mode 100644
index 0000000000..d8f00bd211
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd.h
@@ -0,0 +1,917 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 - 070702 - $Revision$"
+#define MYSQLND_VERSION_ID 50002
+
+#define phpext_mysqlnd_ptr &mysqlnd_module_entry
+extern zend_module_entry mysqlnd_module_entry;
+
+
+/* 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.
+*/
+#define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1
+
+#include "portability.h"
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#include "mysqlnd_enum_n_def.h"
+
+/*
+ /-----> 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_ROWS_FETCHED_FROM_SERVER,
+ STAT_ROWS_FETCHED_FROM_CLIENT,
+ STAT_ROWS_SKIPPED,
+ 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_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_LAST /* Should be always the last */
+} enum_mysqlnd_collected_stats;
+
+
+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;
+ unsigned long long affected_rows;
+ unsigned long long 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;
+ zval *callback;
+} 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
+} 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 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);
+ 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);
+
+ 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 long 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_method)(MYSQLND *conn, const char *query, 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);
+ 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);
+ void (*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);
+ 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);
+ MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr);
+
+ 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);
+ 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);
+ 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);
+ 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);
+ enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind);
+ 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);
+
+ 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);
+ enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value);
+};
+
+
+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;
+ unsigned long long thread_id;
+ char *server_version;
+ char *host_info;
+ unsigned char *scramble;
+ const MYSQLND_CHARSET *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;
+
+};
+
+
+
+
+#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1
+
+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;
+
+ struct st_mysqlnd_stmt_methods *m;
+};
+
+
+
+/* Library related */
+PHPAPI void mysqlnd_library_init();
+PHPAPI void mysqlnd_library_end();
+PHPAPI void mysqlnd_restart_psession(MYSQLND *conn);
+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 */
+PHPAPI MYSQLND * mysqlnd_init(zend_bool persistent);
+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)
+
+/* 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))
+
+/*****************************************************************************************************/
+#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))
+#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))
+#define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr))
+
+/* 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, zend_bool free_callback);
+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_method((conn), wild? "SHOW FIELDS FROM %s LIKE %s":"SHOW FIELDS FROM %s", wild, tab 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))
+#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))
+#define mysqlnd_escape_string(newstr, escapestr, escapestr_len) \
+ mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len))
+
+PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len);
+
+
+/* PS */
+#define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn))
+#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))
+#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))
+#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind))
+#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt))
+#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt))
+
+#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))
+#define mysqlnd_stmt_attr_set(stmt, attr, value) (stmt)->m->set_attribute((stmt), (attr), (value))
+
+#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 */
+PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size);
+PHPAPI void mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache);
+PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value);
+
+PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache);
+PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache);
+
+
+PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache);
+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);
+
+
+/* There two should not be used from outside */
+void * mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated);
+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);
+
+#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/mysqli/mysqlnd/mysqlnd_charset.c b/ext/mysqli/mysqlnd/mysqlnd_charset.c
new file mode 100644
index 0000000000..9208e7f256
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_charset.c
@@ -0,0 +1,608 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+/* {{{ utf8 functions */
+
+static uint check_mb_utf8(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 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 ((end - start) > 1 && valid_big5head(*(start)) && 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 (((end > start) + 1) && valid_cp932head((uint)start[0]) &&
+ valid_cp932tail((uint)start[1])) ? 2 : 0;
+}
+
+
+static uint mysqlnd_mbcharlen_cp932(uint cp932)
+{
+ return (valid_cp932head(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 (*((uint *)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 <= (c) && (c) <= 0xF7)
+#define valid_gb2312_tail(c) (0xA1 <= (c) && (c) <= 0xFE)
+
+
+static uint check_mb_gb2312(const char *start, const char *end)
+{
+ return (end - start > 1 || valid_gb2312_head((uint)start[0]) ||
+ 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) ((uint)(c) >> 8)
+#define valid_gbk_tail(c) ((uint)(c) & 0xFF)
+
+static uint check_mb_gbk(const char *start, const char *end)
+{
+ if (end - start <= 1) {
+ return 0; /* invalid length */
+ }
+ return (valid_gbk_head(start[0]) && 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)
+{
+ if (end - start <= 1) {
+ return 0;
+ }
+ return (valid_sjis_head((uint)start[0]) && valid_sjis_tail((uint)start[1])) ? 2 : 0;
+}
+
+
+static uint mysqlnd_mbcharlen_sjis(uint sjis)
+{
+ return (valid_sjis_head((uint)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 ((uint)start[0] < 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)
+{
+ if (((ujis & 0xFF) >= 0xA1 && (ujis & 0xFF) <= 0xFE) ||
+ ((ujis & 0xFF) == 0x8E)) {
+ return 2;
+ }
+ if ((ujis & 0xFF) == 0x8F) {
+ return 3;
+ }
+ return 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_eucjpms},
+ { 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},
+ { 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, },
+ { 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},
+ { 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},
+ { 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 },
+ { 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 },
+ { 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8},
+ { 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 },
+ { 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)
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ zend_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ uint len = 0;
+ /* check unicode characters */
+
+ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 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 (*newstr == '\'') {
+ 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) {
+ return (ulong)~0;
+ }
+ 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)
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ zend_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ char esc = '\0';
+ uint len = 0;
+
+ /* check unicode characters */
+ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 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 &&
+ (len = cset->mb_valid(escapestr, end))) {
+ 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) {
+ return (ulong)~0;
+ }
+ 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/mysqli/mysqlnd/mysqlnd_charset.h b/ext/mysqli/mysqlnd/mysqlnd_charset.h
new file mode 100644
index 0000000000..85b0cd51a8
--- /dev/null
+++ b/ext/mysqli/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);
+
+PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr,
+ const char *escapestr, int escapestr_len);
+
+
+/*
+ * 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/mysqli/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqli/mysqlnd/mysqlnd_enum_n_def.h
new file mode 100644
index 0000000000..53d5f7d8d7
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_enum_n_def.h
@@ -0,0 +1,231 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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,
+ MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200,
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ MYSQLND_OPT_INT_AND_YEAR_AS_INT = 201,
+#endif
+} 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
+
+#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/mysqli/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqli/mysqlnd/mysqlnd_libmysql_compat.h
new file mode 100644
index 0000000000..8f8e0e69bc
--- /dev/null
+++ b/ext/mysqli/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/mysqli/mysqlnd/mysqlnd_loaddata.c b/ext/mysqli/mysqlnd/mysqlnd_loaddata.c
new file mode 100644
index 0000000000..ce7a27bcf4
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_loaddata.c
@@ -0,0 +1,334 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+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] = 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]);\
+ efree(a[i]);\
+ }\
+ 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;
+
+ *ptr= info= ((MYSQLND_INFILE_INFO *)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;
+ 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;
+ return 1;
+ }
+
+ 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;
+
+ /* default processing */
+ if (!info->callback) {
+ count = (int)php_stream_read(info->fd, buf, buf_len);
+
+ if (count < 0) {
+ strcpy(info->error_msg, "Error reading file");
+ info->error_no = MYSQLND_EE_READ;
+ }
+
+ return count;
+ } else {
+ zval ***callback_args;
+ zval *retval;
+ zval *fp;
+ int argc = 4;
+ int i;
+ long rc;
+
+ ALLOC_CALLBACK_ARGS(callback_args, 1, argc);
+
+ /* set parameters: filepointer, buffer, buffer_len, errormsg */
+
+ MAKE_STD_ZVAL(fp);
+ php_stream_to_zval(info->fd, fp);
+ callback_args[0] = &fp;
+ 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,
+ info->callback,
+ &retval,
+ argc,
+ callback_args,
+ 0,
+ NULL TSRMLS_CC) == SUCCESS) {
+
+ rc = Z_LVAL_P(retval);
+ zval_ptr_dtor(&retval);
+
+ if (rc > 0) {
+ const char * msg = NULL;
+ if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) {
+ msg = "Mismatch between the return value of the callback and the content "
+ "length of the buffer.";
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, msg);
+ rc = -1;
+ } else if (Z_STRLEN_P(*callback_args[1]) > buf_len) {
+ /* check buffer overflow */
+ msg = "Too much data returned";
+ rc = -1;
+ } else {
+ memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1])));
+ }
+ if (rc == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, msg);
+ strcpy(info->error_msg, msg);
+ info->error_no = MYSQLND_EE_READ;
+ }
+ } else if (rc < 0) {
+ strncpy(info->error_msg, Z_STRVAL_P(*callback_args[3]), MYSQLND_ERRMSG_SIZE);
+ info->error_no = MYSQLND_EE_READ;
+ }
+ } else {
+ strcpy(info->error_msg, "Can't execute load data local init callback function");
+ info->error_no = MYSQLND_EE_READ;
+ rc = -1;
+ }
+
+ efree(fp);
+ FREE_CALLBACK_ARGS(callback_args, 1, argc);
+ return rc;
+
+ }
+}
+/* }}} */
+
+
+/* {{{ 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;
+
+ if (info) {
+ strncpy(error_buf, info->error_msg, error_buf_len);
+
+ return info->error_no;
+ }
+
+ strncpy(error_buf, "Unknown error", error_buf_len);
+ 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);
+ }
+ efree(info);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_local_infile_default */
+PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback)
+{
+ 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;
+ conn->infile.userdata = NULL;
+ if (free_callback == TRUE && conn->infile.callback) {
+ zval_ptr_dtor(&conn->infile.callback);
+ conn->infile.callback = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ 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;
+
+ 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;
+ }
+
+ /* check if we have valid functions */
+ if (!conn->infile.local_infile_init || !conn->infile.local_infile_read ||
+ !conn->infile.local_infile_error || !conn->infile.local_infile_end) {
+ mysqlnd_local_infile_default(conn, FALSE);
+ }
+
+ infile = conn->infile;
+ /* allocate buffer for reading data */
+ buf = (char *)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;
+ }
+
+ /* pass callback handler */
+ if (infile.callback) {
+ MYSQLND_INFILE_INFO *ptr = (MYSQLND_INFILE_INFO *)info;
+ ptr->callback = infile.callback;
+ }
+
+
+ /* 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) {
+ 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;
+ 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);
+ efree(buf);
+ 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/mysqli/mysqlnd/mysqlnd_palloc.c b/ext/mysqli/mysqlnd/mysqlnd_palloc.c
new file mode 100644
index 0000000000..a5df45c82e
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_palloc.c
@@ -0,0 +1,558 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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
+
+
+#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)
+{
+ MYSQLND_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_ZVAL_PCACHE));
+ unsigned int i;
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_init_cache %p]\n", ret);
+#endif
+
+#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]);
+ }
+
+
+ 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)
+{
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_free_cache %p]\n", cache);
+#endif
+
+#ifdef ZTS
+ tsrm_mutex_free(cache->LOCK_access);
+#endif
+
+ /* Data in pointed by 'block' was cleaned in RSHUTDOWN */
+ free(cache->block);
+ free(cache->free_list.ptr_line);
+ free(cache);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_init_thd_cache */
+PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache)
+{
+ MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE));
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_init_thd_cache %p]\n", ret);
+#endif
+ 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;
+
+ 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)
+{
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_free_thd_cache %p]\n", cache);
+#endif
+
+ free(cache->gc_list.ptr_line);
+ free(cache);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_free_thd_cache_reference */
+PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache)
+{
+ if (*cache) {
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_free_thd_cache_reference %p] refs=%d\n", *cache, (*cache)->references);
+#endif
+ --(*cache)->parent->references;
+
+ if (--(*cache)->references == 0) {
+ mysqlnd_palloc_free_thd_cache(*cache);
+ }
+ *cache = NULL;
+ }
+}
+/* }}} */
+
+
+/*
+ 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)
+{
+ void *ret = NULL;
+
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_get_zval %p] *last_added=%p free_items=%d\n",
+ thd_cache, thd_cache? thd_cache->parent->free_list.last_added:NULL,
+ thd_cache->parent->free_items);
+#endif
+
+ 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));
+ }
+
+ 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;
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_zval_ptr_dtor %p] parent_block=%p last_in_block=%p *zv=%p type=%d refc=%d\n",
+ thd_cache,
+ thd_cache->parent? thd_cache->parent->block:NULL,
+ thd_cache->parent? thd_cache->parent->last_in_block:NULL,
+ *zv, type, ZVAL_REFCOUNT(*zv));
+#endif
+ *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);
+ 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);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_rinit */
+PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache)
+{
+ return mysqlnd_palloc_init_thd_cache(cache);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_rshutdown */
+PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache)
+{
+ MYSQLND_ZVAL_PCACHE *cache;
+ mysqlnd_zval **p;
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_palloc_rshutdown %p]\n", thd_cache);
+#endif
+ 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);
+}
+/* }}} */
+
+
+/* {{{ 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/mysqli/mysqlnd/mysqlnd_palloc.h b/ext/mysqli/mysqlnd/mysqlnd_palloc.h
new file mode 100644
index 0000000000..434df01730
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_palloc.h
@@ -0,0 +1,109 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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
+
+/* 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/mysqli/mysqlnd/mysqlnd_priv.h b/ext/mysqli/mysqlnd/mysqlnd_priv.h
new file mode 100644
index 0000000000..cea3887796
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_priv.h
@@ -0,0 +1,194 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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_READ 2
+#define MYSQLND_EE_FILENOTFOUND 29
+
+#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) \
+ {\
+ if ((buf)) { \
+ efree((buf)); \
+ } \
+ (buf) = (message); \
+ (buf_len) = (len); \
+ /* Transfer ownership*/ \
+ (message) = NULL; \
+ }
+
+#define SET_EMPTY_MESSAGE(buf, buf_len) \
+ {\
+ if ((buf)) { \
+ efree((buf)); \
+ (buf) = NULL; \
+ } \
+ (buf_len) = 0; \
+ }
+
+
+#define SET_EMPTY_ERROR(error_info) \
+ { \
+ error_info.error_no = 0; \
+ error_info.error[0] = '\0'; \
+ strcpy(error_info.sqlstate, "00000"); \
+ }
+
+#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;
+
+
+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 mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p,
+ size_t *buf_len, unsigned int null_byte_offset);
+
+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, zend_bool is_bit 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/mysqli/mysqlnd/mysqlnd_ps.c b/ext/mysqli/mysqlnd/mysqlnd_ps.c
new file mode 100644
index 0000000000..46d2435831
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_ps.c
@@ -0,0 +1,1581 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+
+#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);
+
+
+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);
+
+
+/* {{{ 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;
+
+ /* be compliant with libmysql - NULL will turn */
+ if (!stmt->field_count) {
+ return NULL;
+ }
+
+ if (stmt->cursor_exists) {
+ /* Silently convert buffered to unbuffered, for now */
+ return stmt->m->use_result(stmt TSRMLS_CC);
+ }
+
+
+ /* 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);
+ 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);
+ efree(stmt->result);
+ stmt->result = NULL;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+
+ 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;
+
+ /* be compliant with libmysql - NULL will turn */
+ if (!stmt->field_count) {
+ return NULL;
+ }
+
+ if (stmt->cursor_exists) {
+ /* Silently convert buffered to unbuffered, for now */
+ return stmt->m->use_result(stmt TSRMLS_CC);
+ }
+
+ /* 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);
+ 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));
+
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
+
+ /* 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;
+ }
+
+ 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;
+
+ 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);
+ return FAIL;
+ }
+ }
+ PACKET_FREE_ALLOCA(field_packet);
+ 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;
+
+ 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);
+
+ 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;
+
+ 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);
+ 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);
+
+ 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;
+
+ 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);
+ /* 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 */
+ efree(stmt_to_prepare);
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ return PASS;
+
+fail:
+ if (stmt_to_prepare != stmt) {
+ stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC);
+ }
+ stmt->state = MYSQLND_STMT_INITTED;
+
+ 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;
+
+ 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);
+ 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");
+ return FAIL;
+ }
+
+ request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request);
+
+ /* 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) {
+ efree(request);
+ }
+
+ if (ret == FAIL) {
+ stmt->error_info = conn->error_info;
+ return FAIL;
+ }
+
+ 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;
+ return PASS;
+ } else if (conn->last_query_type == QUERY_LOAD_LOCAL) {
+ 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.
+ */
+#ifndef MYSQLND_SILENT
+ php_printf("server_status=%d cursor=%d\n", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
+#endif
+ 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;
+ } 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;
+ } else {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ }
+ }
+ }
+
+ 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;
+
+ /* 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) {
+ 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;
+ } else {
+ result->data->data_cursor = NULL;
+ *fetched_anything = FALSE;
+#ifndef MYSQLND_SILENT
+ php_printf("NO MORE DATA\n ");
+#endif
+ }
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
+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;
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ *fetched_anything = FALSE;
+ 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);
+ 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);
+
+ 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);
+ }
+ }
+ }
+ }
+ } else {
+ /*
+ 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.
+ */
+ 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) {
+ /* 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;
+ }
+
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::use_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+ MYSQLND *conn = stmt->conn;
+
+ 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);
+ 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 = 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 = 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 :) */
+
+ 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;
+
+ if (!stmt) {
+ 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);
+ 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;
+ 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()
+ */
+
+ 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) {
+ efree(row_packet->row_buffer);
+ row_packet->row_buffer = NULL;
+ }
+ } 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;
+
+ 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)
+{
+ 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);
+
+ 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);
+
+ /*
+ 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;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT);
+
+ return stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ 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 */];
+
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
+ if (stmt->stmt_id) {
+ if (stmt->param_bind) {
+ unsigned int i;
+ /* 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) {
+ stmt->default_rset_handler(stmt TSRMLS_CC);
+ stmt->state = MYSQLND_STMT_USER_FETCHING;
+ }
+
+ if (stmt->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;
+ }
+ 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;
+
+ 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);
+ return FAIL;
+ }
+ if (!stmt->param_bind) {
+ SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ return FAIL;
+ }
+
+ if (param_no >= stmt->param_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ 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);
+ 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 = 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);
+ 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.
+ */
+#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;
+ }
+ }
+
+ 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)
+{
+ unsigned int i = 0;
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ 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");
+ return FAIL;
+ } else if (stmt->param_bind) {
+ /*
+ 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;
+ }
+ }
+ 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 */
+ 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;
+ }
+ 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)
+{
+ uint i = 0;
+
+ 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) {
+ efree(result_bind);
+ }
+ return FAIL;
+ }
+
+ if (stmt->field_count) {
+ if (!result_bind) {
+ return FAIL;
+ }
+
+ mysqlnd_stmt_separate_result_bind(stmt);
+
+ 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) {
+ efree(result_bind);
+ }
+ 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)
+{
+ return stmt->result? stmt->result->m.seek_data(stmt->result, row) : 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)
+{
+ MYSQLND_RES *result;
+
+ if (!stmt->field_count || !stmt->conn || !stmt->result ||
+ !stmt->result->meta)
+ {
+ 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);
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ result->unbuf->eof_reached = TRUE;
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
+
+ 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)
+{
+ unsigned long val = *(unsigned long *) value;
+ 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");
+ return FAIL;
+ }
+ 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)
+{
+ 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:
+ return FAIL;
+ }
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::free_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ if (!stmt->result) {
+ return PASS;
+ }
+
+ 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);
+ }
+
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ /* 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);
+
+ /* 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;
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_separate_result_bind */
+void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt)
+{
+ int i;
+
+ if (!stmt->result_bind) {
+ 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) {
+ /*
+ 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);
+ }
+ }
+ }
+ efree(stmt->result_bind);
+ stmt->result_bind = NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_stmt_content */
+static
+void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ /* 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);
+ }
+ }
+
+ 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);
+ /* 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) {
+ 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;
+ }
+}
+/* }}} */
+
+
+/* {{{ 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 */];
+
+ 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) {
+ 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) {
+ 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_CONN_STATISTIC(NULL, 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;
+ return FAIL;
+ }
+ }
+
+ mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+
+ 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;
+
+ MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
+ STAT_STMT_CLOSE_EXPLICIT);
+
+ if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) {
+ efree(stmt);
+ }
+
+ 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)
+{
+ MYSQLND_STMT *stmt = ecalloc(1, sizeof(MYSQLND_STMT));
+
+ stmt->m = &mysqlnd_stmt_methods;
+ stmt->state = MYSQLND_STMT_INITTED;
+ stmt->cmd_buffer.length = 4096;
+ stmt->cmd_buffer.buffer = 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);
+
+ 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/mysqli/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqli/mysqlnd/mysqlnd_ps_codec.c
new file mode 100644
index 0000000000..85676adbd5
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_ps_codec.c
@@ -0,0 +1,852 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+
+#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, zend_bool is_bit TSRMLS_DC)
+{
+ char tmp[22];
+ size_t tmp_len = 0;
+ 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, FALSE 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, FALSE 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, FALSE 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= 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, FALSE 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);
+ 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);
+ 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);
+ 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, TRUE 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)
+{
+ 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 = 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 = 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)
+{
+ 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);
+
+ *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/mysqli/mysqlnd/mysqlnd_qcache.c b/ext/mysqli/mysqlnd/mysqlnd_qcache.c
new file mode 100644
index 0000000000..a08c7d488d
--- /dev/null
+++ b/ext/mysqli/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/mysqli/mysqlnd/mysqlnd_result.c b/ext/mysqli/mysqlnd/mysqlnd_result.c
new file mode 100644
index 0000000000..e1027e7ae8
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_result.c
@@ -0,0 +1,1085 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "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;
+ if (!unbuf) {
+ 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;
+ }
+}
+/* }}} */
+
+/* {{{ 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;
+
+ 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);
+ MYSQLND_INC_CONN_STATISTIC(NULL, copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED:
+ STAT_COPY_ON_WRITE_SAVED);
+ }
+ pefree(current_row, set->persistent);
+ pefree(current_buffer, set->persistent);
+ }
+ 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);
+ }
+ pefree(set, set->persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_result_buffers */
+void
+MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC)
+{
+
+ 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;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result_contents */
+static
+void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC)
+{
+ result->m.free_result_buffers(result TSRMLS_CC);
+
+ if (result->row_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) {
+ mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache);
+ result->zval_cache = NULL;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result */
+static
+void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC)
+{
+ /*
+ 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);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::read_result_metadata */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC)
+{
+ /*
+ 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);
+
+ /* 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);
+ 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.
+ */
+
+ 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;
+
+ 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;
+ 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 */
+ 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);
+ /* 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;
+ uint stat = -1;
+
+ SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len);
+
+ 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));
+ } else {
+ if (!stmt->result) {
+ /*
+ 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));
+ } 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;
+ }
+ 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))) {
+ 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 {
+ 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 != -1) {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
+ }
+ }
+
+ PACKET_FREE_ALLOCA(fields_eof);
+
+ break;
+ }
+ }
+ } while (0);
+ PACKET_FREE_ALLOCA(rset_header);
+ 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;
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ *fetched_anything = FALSE;
+ 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);
+ return FAIL;
+ }
+ /* Let the row packet fill our buffer and skip additional 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);
+
+ 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;
+ }
+ *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 */
+
+ 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;
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::use_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)
+{
+ 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 = 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 = ecalloc(result->field_count, sizeof(unsigned long));
+
+ /* No multithreading issues as we don't share the connection :) */
+
+ 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;
+
+ /* 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;
+ } else {
+ result->data->data_cursor = NULL;
+ *fetched_anything = FALSE;
+#ifndef MYSQLND_SILENT
+ php_printf("NO MORE DATA\n ");
+#endif
+ }
+ 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;
+
+ free_rows = next_extend;
+
+ result->data = set = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
+ set->data = pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache);
+ set->row_buffers= 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 = perealloc(set->data, total_rows * sizeof(zval **), set->persistent);
+
+ set->row_buffers = 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, STAT_ROWS_FETCHED_FROM_CLIENT,
+ 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 = perealloc(set->data,
+ (size_t) set->row_count * sizeof(zval **),
+ set->persistent);
+ set->row_buffers = 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);
+
+ 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;
+
+ 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 = 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;
+ }
+
+ 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;
+
+ /*
+ 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)
+ {
+ /* 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 */;
+ }
+ }
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC)
+{
+ result->m.skip_result(result TSRMLS_CC);
+ MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL,
+ implicit == TRUE? STAT_FREE_RESULT_EXPLICIT:
+ STAT_FREE_RESULT_IMPLICIT);
+
+ result->m.free_result_internal(result TSRMLS_CC);
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_data_seek */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong 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;
+ }
+
+ 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)
+{
+ return result->meta? result->meta->m->fetch_field(result->meta):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)
+{
+ return result->meta? &result->meta->fields[fieldnr]: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;
+
+ if (!result->m.fetch_row) {
+ RETURN_NULL();
+ }
+ /*
+ 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");
+ RETURN_FALSE;
+ } else if (fetched_anything == FALSE) {
+ zval_dtor(return_value);
+ switch (extension) {
+ case MYSQLND_MYSQLI:
+ RETURN_NULL();
+ break;
+ case MYSQLND_MYSQL:
+ RETURN_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.
+ */
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_all */
+static void
+MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags,
+ zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ zval *row;
+ ulong i = 0;
+
+ /* mysqlnd_res::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)
+ {
+ RETURN_NULL();
+ }
+
+ 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);
+ }
+}
+/* }}} */
+
+
+/* {{{ 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;
+
+ if (!result->m.fetch_row) {
+ RETURN_NULL();
+ }
+ /*
+ 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);
+ RETURN_NULL();
+ }
+ 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);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_init */
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache)
+{
+ MYSQLND_RES *ret = ecalloc(1, sizeof(MYSQLND_RES));
+
+ 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;
+
+ 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/mysqli/mysqlnd/mysqlnd_result.h b/ext/mysqli/mysqlnd/mysqlnd_result.h
new file mode 100644
index 0000000000..dd07097b52
--- /dev/null
+++ b/ext/mysqli/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);
+
+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/mysqli/mysqlnd/mysqlnd_result_meta.c b/ext/mysqli/mysqlnd/mysqlnd_result_meta.c
new file mode 100644
index 0000000000..ba0b55a5a3
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_result_meta.c
@@ -0,0 +1,401 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "ext/standard/basic_functions.h"
+
+
+/* {{{ php_mysqlnd_free_field_metadata */
+static
+void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent)
+{
+ if (meta) {
+ if (meta->root) {
+ pefree(meta->root, persistent);
+ meta->root = NULL;
+ }
+ if (meta->def) {
+ 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;
+
+ 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 */
+ 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);
+ return FAIL;
+ }
+
+ if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
+ 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);
+ return FAIL;
+ }
+ if (meta->fields[i].type == MYSQL_TYPE_BIT) {
+ size_t field_len;
+ ++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;
+ 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);
+
+ 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;
+
+ if ((fields = meta->fields)) {
+ i = meta->field_count;
+ while (i--) {
+ php_mysqlnd_free_field_metadata(fields++, persistent);
+ }
+ pefree(meta->fields, persistent);
+ meta->fields = NULL;
+ }
+
+ if (meta->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) {
+ pefree(meta->zend_hash_keys[i].ustr.v, persistent);
+ }
+ }
+ }
+#endif
+ pefree(meta->zend_hash_keys, persistent);
+ meta->zend_hash_keys = NULL;
+ }
+ pefree(meta, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::clone_metadata */
+static MYSQLND_RES_METADATA *
+MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta,
+ zend_bool persistent)
+{
+ unsigned int i;
+ /* +1 is to have empty marker at the end */
+ MYSQLND_RES_METADATA *new_meta = pemalloc(sizeof(MYSQLND_RES_METADATA), persistent);
+ MYSQLND_FIELD *new_fields = 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);
+
+ new_meta->zend_hash_keys = 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 = 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 = 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;
+
+ return new_meta;
+}
+/* }}} */
+
+/* {{{ mysqlnd_res_meta::fetch_field */
+static MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta)
+{
+ if (meta->current_field >= meta->field_count)
+ return NULL;
+ return &meta->fields[meta->current_field++];
+}
+/* }}} */
+
+/* {{{ mysqlnd_res::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, 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)
+{
+ MYSQLND_RES_METADATA *ret;
+ /* +1 is to have empty marker at the end */
+ ret = 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;
+ 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/mysqli/mysqlnd/mysqlnd_result_meta.h b/ext/mysqli/mysqlnd/mysqlnd_result_meta.h
new file mode 100644
index 0000000000..1a7c308b29
--- /dev/null
+++ b/ext/mysqli/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);
+
+
+
+#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/mysqli/mysqlnd/mysqlnd_statistics.c b/ext/mysqli/mysqlnd/mysqlnd_statistics.c
new file mode 100644
index 0000000000..4c93e0f79e
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_statistics.c
@@ -0,0 +1,130 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+
+typedef struct st_mysqlnd_string
+{
+ char *s;
+ size_t l;
+} MYSQLND_STRING;
+
+
+#define STR_W_LEN(str) str, (sizeof(str) - 1)
+
+/* {{{ mysqlnd_stats_values_names
+ */
+static
+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("rows_fetched_from_server") },
+ { STR_W_LEN("rows_fetched_from_client") },
+ { STR_W_LEN("rows_skipped") },
+ { 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("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") },
+};
+/* }}} */
+
+
+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;
+
+ 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);
+}
+
+
+/*
+ * 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/mysqli/mysqlnd/mysqlnd_statistics.h b/ext/mysqli/mysqlnd/mysqlnd_statistics.h
new file mode 100644
index 0000000000..08b28f36ed
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_statistics.h
@@ -0,0 +1,129 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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;
+
+#ifdef ZTS
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, 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) \
+ { \
+ my_uint64 v = (value); \
+ \
+ 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) \
+ { \
+ my_uint64 v1 = (value1); \
+ my_uint64 v2 = (value2); \
+ my_uint64 v3 = (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_CONN_STATISTIC(conn_stats, 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 = (value); \
+ \
+ 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) \
+ { \
+ my_uint64 v1 = (value1); \
+ my_uint64 v2 = (value2); \
+ my_uint64 v3 = (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/mysqli/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
new file mode 100644
index 0000000000..8c7e780131
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
@@ -0,0 +1,1817 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "ext/standard/sha1.h"
+#include "php_network.h"
+#ifndef PHP_WIN32
+#include <netinet/tcp.h>
+#else
+
+#endif
+
+#define USE_CORK 0
+
+#define MYSQLND_SILENT
+#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)) {\
+ return FAIL;\
+ }\
+ if ((buf_size) < (packet)->header.size) { \
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "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)) { \
+ php_error(E_WARNING, "Empty %s packet body", (packet_type));\
+ return FAIL; \
+ } \
+ }
+
+
+extern mysqlnd_packet_methods packet_methods[];
+
+static const char *unknown_sqlstate= "HY000";
+
+char * const mysqlnd_empty_string = "";
+
+
+/* {{{ 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 */
+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);
+
+ 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
+ "consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ }
+ }
+ net->last_command = cmd;
+
+ return skipped_bytes;
+}
+/* }}} */
+
+
+/* {{{ 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)
+{
+ zend_uchar *p = buf;
+ int error_msg_len= 0;
+ 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';
+ 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));
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ 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_storage[MYSQLND_HEADER_SIZE];
+ 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;
+
+ 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);
+
+ 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;
+
+ 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_storage[MYSQLND_HEADER_SIZE + 1];
+ 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;
+
+ 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;
+
+ 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;
+
+ do {
+ if (!(ret= php_stream_read(net->stream, p, to_read))) {
+ php_error(E_WARNING, "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
+ php_printf("\nHEADER: packet_no=%d size=%3d\n", header->packet_no, header->size);
+#endif
+ return PASS;
+ }
+
+ php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d",
+ net->packet_no, header->packet_no, header->size);
+ 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;
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ int iter = 0;
+#endif
+ MYSQLND_NET *net = &conn->net;
+
+ do {
+ size -= (ret = php_stream_read(net->stream, p, size));
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ if (size || iter++) {
+ php_printf("read=%d buf=%p p=%p chunk_size=%d left=%d\n",
+ ret, buf, p , net->stream->chunk_size, size);
+ }
+#endif
+ p += ret;
+ } while (size > 0);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, p - (char*)buf);
+
+#ifdef MYSQLND_DUMP_HEADER_N_BODY_FULL
+ {
+ int i;
+ php_printf("\tBODY: requested=%d last_read=%3d\n\t", p - (char*)buf, ret);
+ for (i = 0 ; i < p - (char*)buf; i++) printf("[%c]", *(char *)(&(buf[i]))); php_printf("\n\t");
+ for (i = 0 ; i < p - (char*)buf; i++) printf("%.2X ", (int)*((char*)&(buf[i])));php_printf("\n-=-=-=-=-\n");
+ }
+#endif
+
+ 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;
+
+ 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);
+ /*
+ 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);
+ }
+ 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_greet_free_mem */
+static
+void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet;
+ if (p->server_version) {
+ efree(p->server_version);
+ p->server_version = NULL;
+ }
+ if (!alloca) {
+ 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;
+
+ 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 */
+
+ 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)
+{
+ if (!alloca) {
+ 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;
+
+ 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);
+ 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 = estrndup((char *)p, MIN(i, sizeof(buf) - (p - buf)));
+ packet->message_len = i;
+ } else {
+ packet->message = NULL;
+ }
+
+#ifndef MYSQLND_SILENT
+ php_printf("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d\n",
+ packet->affected_rows, packet->last_insert_id, packet->server_status,
+ packet->warning_count);
+#endif
+ if (p - begin > packet->header.size) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_ok_free_mem */
+static
+void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet;
+ if (p->message) {
+ efree(p->message);
+ p->message = NULL;
+ }
+ if (!alloca) {
+ 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;
+
+ 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);
+ 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+#ifndef MYSQLND_SILENT
+ php_printf("EOF packet: server_status=%d warnings=%d\n",
+ packet->server_status, packet->warning_count);
+#endif
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_eof_free_mem */
+static
+void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca)
+{
+ if (!alloca) {
+ 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;
+
+ /*
+ 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 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);
+ return mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC);
+ } else {
+#if USE_CORK && defined(TCP_CORK)
+ return 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)? 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);
+ efree(tmp);
+ }
+ return ret;
+#endif
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_cmd_free_mem */
+static
+void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca)
+{
+ if (!alloca) {
+ 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;
+
+ 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);
+ 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 = emalloc(len + 1);
+ 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 = emalloc(len + 1);
+ 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) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_header_free_mem */
+static
+void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet;
+ if (p->info_or_local_file) {
+ efree(p->info_or_local_file);
+ p->info_or_local_file = NULL;
+ }
+ if (!alloca) {
+ 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);
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field");
+
+ if (packet->skip_parsing) {
+ return PASS;
+ }
+
+ meta = packet->metadata;
+
+ for (i = 0; i < field_count; i += 2) {
+ switch ((len = php_mysqlnd_net_field_length(&p))) {
+ 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]) = 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 */
+ if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
+ meta->def = emalloc(len + 1);
+ memcpy(meta->def, p, len);
+ meta->def[len] = '\0';
+ meta->def_length = len;
+ p += len;
+ }
+
+ if (p - begin > packet->header.size) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes "
+ "shorter than expected", p - begin - packet->header.size);
+ }
+
+ root_ptr = meta->root = 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++;
+ }
+
+#ifndef MYSQLND_SILENT
+ php_printf("\tFIELD=[%s.%s.%s]\n", meta->db? meta->db:"*NA*",
+ meta->table? meta->table:"*NA*",
+ meta->name? meta->name:"*NA*");
+#endif
+
+ return PASS;
+
+faulty_fake:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH.",
+ " The server is faulty");
+ return FAIL;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_field_free_mem */
+static
+void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet;
+
+ /* p->metadata was passed to us as temporal buffer */
+ if (!alloca) {
+ 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;
+ MYSQLND_NET *net = &conn->net;
+
+ /*
+ 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 = pemalloc(*data_size + 1, persistent_alloc);
+ net->stream->chunk_size = header.size;
+ } 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 = 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)) {
+ php_error(E_WARNING, "Empty row packet body");
+ ret = FAIL;
+ break;
+ }
+
+ if (header.size < MYSQLND_MAX_PACKET_SIZE) {
+ break;
+ }
+ }
+ if (ret == PASS && new_buf) {
+ *buf = new_buf;
+ }
+ *data_size -= prealloc_more_bytes;
+ 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;
+
+ 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);
+ 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';
+}
+/* }}} */
+
+
+/* {{{ 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
+
+ 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);
+ 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 {
+ struct st_mysqlnd_perm_bind perm_bind =
+ mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type];
+
+#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, TRUE 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, start, bit_area - start - 1, 0);
+ }
+ } else if (Z_TYPE_PP(current_field) == IS_STRING){
+ memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
+ bit_area += Z_STRLEN_PP(current_field);
+ *bit_area++ = '\0';
+ zval_dtor(*current_field);
+#if PHP_MAJOR_VERSION >= 6
+ if (as_unicode) {
+ ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
+ } else
+#endif
+ {
+ ZVAL_STRINGL(*current_field, start, bit_area - start - 1, 0);
+ }
+ }
+ /*
+ IS_UNICODE should not be specially handled. In unicode mode
+ the buffers are not pointed - 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';
+ }
+}
+/* }}} */
+
+
+/* {{{ 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;
+
+ 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);
+ } 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, STAT_ROWS_FETCHED_FROM_SERVER);
+
+ packet->eof = FALSE;
+ /* packet->field_count is set by the user of the packet */
+
+ if (!packet->skip_extraction) {
+ if (!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 **) 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, STAT_ROWS_SKIPPED);
+ }
+ }
+
+end:
+ net->stream->chunk_size = old_chunk_size;
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_free_mem */
+static
+void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_row *p= (php_mysql_packet_row *) _packet;
+ if (p->row_buffer) {
+ 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) {
+ 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;
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "statistics");
+
+ packet->message = emalloc(packet->header.size + 1);
+ memcpy(packet->message, buf, packet->header.size);
+ packet->message[packet->header.size] = '\0';
+ packet->message_len = packet->header.size;
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_stats_free_mem */
+static
+void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet;
+ if (p->message) {
+ efree(p->message);
+ p->message = NULL;
+ }
+ if (!alloca) {
+ 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;
+
+ 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);
+ return PASS;
+ }
+
+ if (data_size != PREPARE_RESPONSE_SIZE_41 &&
+ data_size != PREPARE_RESPONSE_SIZE_50 &&
+ !(data_size > PREPARE_RESPONSE_SIZE_50)) {
+ php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size);
+ 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);
+ }
+
+#ifndef MYSQLND_SILENT
+ php_printf("\tPrepare packet read: stmt_id=%d fields=%d params=%d\n",
+ packet->stmt_id, packet->field_count, packet->param_count);
+#endif
+
+ if (p - begin > packet->header.size) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_prepare_free_mem */
+static
+void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca)
+{
+ php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet;
+ if (!alloca) {
+ 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;
+
+ 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 */
+ 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);
+ }
+ if (p - begin > packet->header.size) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet %d bytes shorter than expected",
+ p - begin - packet->header.size);
+ }
+
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_chg_user_free_mem */
+static
+void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca)
+{
+ if (!alloca) {
+ 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/mysqli/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h
new file mode 100644
index 0000000000..4c3f80caaa
--- /dev/null
+++ b/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h
@@ -0,0 +1,328 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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;
+
+
+/* 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)); \
+ } 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))
+
+/* 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);
+} 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;
+ unsigned long long affected_rows;
+ unsigned long long 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;
+} 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);
+
+size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC);
+
+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/mysqli/mysqlnd/php_mysqlnd.h b/ext/mysqli/mysqlnd/php_mysqlnd.h
new file mode 100644
index 0000000000..0958fcae1f
--- /dev/null
+++ b/ext/mysqli/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 */
diff --git a/ext/mysqli/mysqlnd/portability.h b/ext/mysqli/mysqlnd/portability.h
new file mode 100644
index 0000000000..79e519d454
--- /dev/null
+++ b/ext/mysqli/mysqlnd/portability.h
@@ -0,0 +1,514 @@
+/* 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/mysqli/mysqlnd/config-win.h>
+#elif PHP_MAJOR_VERSION < 6
+# include <main/php_config.h>
+#endif /* _WIN32... */
+
+
+#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)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 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/mysqli/php_mysqli.h b/ext/mysqli/php_mysqli.h
index d80fc31c42..334792e835 100644
--- a/ext/mysqli/php_mysqli.h
+++ b/ext/mysqli/php_mysqli.h
@@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2007 The PHP Group |
+----------------------------------------------------------------------+
@@ -12,312 +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
-
-#define MYSQLI_UC_UTF8 1
-#define MYSQLI_UC_UCS2 2
-
-#include <errmsg.h>
-
#ifndef PHP_MYSQLI_H
#define PHP_MYSQLI_H
-#define MYSQLI_VERSION_ID 101008
-
-enum mysqli_status {
- MYSQLI_STATUS_UNKNOWN=0,
- MYSQLI_STATUS_CLEARED,
- MYSQLI_STATUS_INITIALIZED,
- MYSQLI_STATUS_VALID
-};
-
-typedef struct {
- void *buf; /* buffer: binary or unicode data */
- unsigned int buflen; /* buffer length */
- zend_uchar buftype; /* buffer type */
- UErrorCode status; /* error code */
-} MYSQLI_STRING;
-
-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;
- UConverter *conv;
-} 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 {
- 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"
-#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 zend_module_entry mysqli_module_entry;
-extern zend_function_entry mysqli_functions[];
-extern zend_function_entry mysqli_link_methods[];
-extern zend_function_entry mysqli_stmt_methods[];
-extern zend_function_entry mysqli_result_methods[];
-extern zend_function_entry mysqli_driver_methods[];
-extern zend_function_entry mysqli_warning_methods[];
-extern 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 %v", 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 %v\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->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);
@@ -330,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);
@@ -347,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);
@@ -359,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);
@@ -421,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);
@@ -436,45 +142,15 @@ PHP_FUNCTION(mysqli_warning_count);
ZEND_FUNCTION(mysqli_stmt_construct);
ZEND_FUNCTION(mysqli_result_construct);
ZEND_FUNCTION(mysqli_driver_construct);
-ZEND_FUNCTION(mysqli_warning_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)
-
-ZEND_EXTERN_MODULE_GLOBALS(mysqli)
+#define phpext_mysqli_ptr &mysqli_module_entry
+extern zend_module_entry mysqli_module_entry;
#endif /* PHP_MYSQLI.H */
+#include "ext/mysqli/mysqlnd/php_mysqlnd.h"
/*
* Local variables:
diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h
new file mode 100644
index 0000000000..30dec64506
--- /dev/null
+++ b/ext/mysqli/php_mysqli_structs.h
@@ -0,0 +1,400 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 PHP_ATOM_INC
+#include "ext/mysqli/php_have_mysqlnd.h" /* HAVE_MYSQLND is defined here if enabled */
+#endif
+
+#if defined(HAVE_MYSQLND)
+#include "ext/mysqli/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_UC_UTF8 1
+#define MYSQLI_UC_UCS2 2
+
+#define MYSQLI_VERSION_ID 101009
+
+enum mysqli_status {
+ MYSQLI_STATUS_UNKNOWN=0,
+ MYSQLI_STATUS_CLEARED,
+ MYSQLI_STATUS_INITIALIZED,
+ MYSQLI_STATUS_VALID
+};
+
+typedef struct {
+ void *buf; /* buffer: binary or unicode data */
+ unsigned int buflen; /* buffer length */
+ zend_uchar buftype; /* buffer type */
+ UErrorCode status; /* error code */
+} MYSQLI_STRING;
+
+
+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;
+ zend_bool persistent;
+ unsigned int multi_query;
+ UConverter *conv;
+} 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
+
+#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 zend_function_entry mysqli_functions[];
+extern zend_function_entry mysqli_link_methods[];
+extern zend_function_entry mysqli_stmt_methods[];
+extern zend_function_entry mysqli_result_methods[];
+extern zend_function_entry mysqli_driver_methods[];
+extern zend_function_entry mysqli_warning_methods[];
+extern 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[];
+
+#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);
+
+#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 %v", 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 %v\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 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
+ */