diff options
| author | Andrey Hristov <andrey@php.net> | 2007-10-05 21:23:56 +0000 | 
|---|---|---|
| committer | Andrey Hristov <andrey@php.net> | 2007-10-05 21:23:56 +0000 | 
| commit | 8b9b553aa264d3c0afe611f49499e787004983c7 (patch) | |
| tree | 840ea2b1127ec97a63f36ffda689a1a1fe5fb975 | |
| parent | 9f9495a48483aac9f6bc92ceea5841a8d243e2aa (diff) | |
| download | php-git-8b9b553aa264d3c0afe611f49499e787004983c7.tar.gz | |
Import mysqlnd
Patch ext/mysql and ext/mysqli to support mysqlnd
53 files changed, 17238 insertions, 1225 deletions
diff --git a/ext/mysql/config.m4 b/ext/mysql/config.m4 index 5870be02e1..f5ed71c6c5 100644 --- a/ext/mysql/config.m4 +++ b/ext/mysql/config.m4 @@ -40,7 +40,8 @@ AC_DEFUN([PHP_MYSQL_SOCKET_SEARCH], [  PHP_ARG_WITH(mysql, for MySQL support, -[  --with-mysql[=DIR]      Include MySQL support. DIR is the MySQL base directory]) +[  --with-mysql[=DIR]        Include MySQL support. DIR is the MySQL base directory. +                            If mysqlnd is passed as DIR, the MySQL native driver will be used])  PHP_ARG_WITH(mysql-sock, for specified location of the MySQL UNIX socket,  [  --with-mysql-sock[=DIR]   MySQL: Location of the MySQL unix socket pointer. @@ -51,9 +52,11 @@ if test -z "$PHP_ZLIB_DIR"; then    [  --with-zlib-dir[=DIR]     MySQL: Set the path to libz install prefix], no, no)  fi +if test "$PHP_MYSQL" = "mysqlnd"; then +  dnl enables build of mysqnd library +  PHP_MYSQLND_ENABLED=yes -if test "$PHP_MYSQL" != "no"; then -  AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL]) +elif test "$PHP_MYSQL" != "no"; then    AC_MSG_CHECKING([for MySQL UNIX socket location])    if test "$PHP_MYSQL_SOCK" != "no" && test "$PHP_MYSQL_SOCK" != "yes"; then @@ -137,14 +140,22 @@ Note that the MySQL client library is not bundled anymore!])    PHP_ADD_LIBRARY_WITH_PATH($MYSQL_LIBNAME, $MYSQL_LIB_DIR, MYSQL_SHARED_LIBADD)    PHP_ADD_INCLUDE($MYSQL_INC_DIR) -  PHP_NEW_EXTENSION(mysql, php_mysql.c, $ext_shared) -    MYSQL_MODULE_TYPE=external    MYSQL_LIBS="-L$MYSQL_LIB_DIR -l$MYSQL_LIBNAME $MYSQL_LIBS"    MYSQL_INCLUDE=-I$MYSQL_INC_DIR -  PHP_SUBST(MYSQL_SHARED_LIBADD)    PHP_SUBST_OLD(MYSQL_MODULE_TYPE)    PHP_SUBST_OLD(MYSQL_LIBS)    PHP_SUBST_OLD(MYSQL_INCLUDE)  fi + +dnl Enable extension +if test "$PHP_MYSQL" != "no"; then +  AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL]) +  PHP_NEW_EXTENSION(mysql, php_mysql.c, $ext_shared) +  PHP_SUBST(MYSQL_SHARED_LIBADD) + +  if test "$PHP_MYSQLI" = "mysqlnd"; then +    PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) +  fi +fi diff --git a/ext/mysql/config.w32 b/ext/mysql/config.w32 index 95f689e1d6..45fc13c8d1 100644 --- a/ext/mysql/config.w32 +++ b/ext/mysql/config.w32 @@ -4,12 +4,17 @@  ARG_WITH("mysql", "MySQL support", "no");  if (PHP_MYSQL != "no") { -	if (CHECK_LIB("libmysql.lib", "mysql", PHP_MYSQL) && +	if (PHP_MYSQLI != "mysqlnd") { +		if (CHECK_LIB("libmysql.lib", "mysql", PHP_MYSQL) &&  			CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQL", -				PHP_MYSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQL)) { -		EXTENSION("mysql", "php_mysql.c"); -		AC_DEFINE('HAVE_MYSQL', 1, 'Have MySQL library'); +			PHP_MYSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQL)) { +		} else { +			WARNING("mysql not enabled; libraries and headers not found"); +		}  	} else { -		WARNING("mysql not enabled; libraries and headers not found"); +		AC_DEFINE('HAVE_MYSQLND', 1, 'MySQL native driver support enabled'); +		ADD_EXTENSION_DEP('mysql', 'mysqlnd', true);  	} +	EXTENSION("mysql", "php_mysql.c"); +	AC_DEFINE('HAVE_MYSQL', 1, 'Have MySQL library');  } diff --git a/ext/mysql/mysql_mysqlnd.h b/ext/mysql/mysql_mysqlnd.h new file mode 100644 index 0000000000..cc8d162a90 --- /dev/null +++ b/ext/mysql/mysql_mysqlnd.h @@ -0,0 +1,41 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 5                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ + +*/ + +#ifndef MYSQL_MYSQLND_H +#define MYSQL_MYSQLND_H + +#include "ext/mysqlnd/mysqlnd_libmysql_compat.h" + +/* Here comes non-libmysql API to have less ifdefs in mysqli*/ +#define MYSQLI_CLOSE_EXPLICIT                  MYSQLND_CLOSE_EXPLICIT +#define MYSQLI_CLOSE_IMPLICIT                  MYSQLND_CLOSE_IMPLICIT +#define MYSQLI_CLOSE_DISCONNECTED              MYSQLND_CLOSE_DISCONNECTED + +#define mysqli_result_is_unbuffered(r)	((r)->unbuf) +#define mysqli_server_status(c)			(c)->upsert_status.server_status +#define mysqli_stmt_warning_count(s)	mysqlnd_stmt_warning_count((s)) +#define mysqli_stmt_server_status(s)	(s)->upsert_status.server_status +#define mysqli_stmt_get_connection(s)	(s)->conn +#define mysqli_close(c, how)			mysqlnd_close((c), (how)) +#define mysqli_stmt_close(c, implicit)	mysqlnd_stmt_close((c), (implicit)) +#define mysqli_free_result(r, implicit)	mysqlnd_free_result((r), (implicit)) + +#endif diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c index 3edf6b9cb5..41a90c622d 100644 --- a/ext/mysql/php_mysql.c +++ b/ext/mysql/php_mysql.c @@ -33,6 +33,7 @@  #include "php_globals.h"  #include "ext/standard/info.h"  #include "ext/standard/php_string.h" +#include "ext/standard/basic_functions.h"  #ifdef ZEND_ENGINE_2  # include "zend_exceptions.h" @@ -64,7 +65,6 @@  # endif  #endif -#include <mysql.h>  #include "php_ini.h"  #include "php_mysql.h" @@ -79,7 +79,7 @@ static int le_result, le_link, le_plink;  #define SAFE_STRING(s) ((s)?(s):"") -#if MYSQL_VERSION_ID > 32199 +#if MYSQL_VERSION_ID > 32199 || defined(HAVE_MYSQLND)  # define mysql_row_length_type unsigned long  # define HAVE_MYSQL_ERRNO  #else @@ -89,7 +89,7 @@ static int le_result, le_link, le_plink;  # endif  #endif -#if MYSQL_VERSION_ID >= 32032 +#if MYSQL_VERSION_ID >= 32032 || defined(HAVE_MYSQLND)  #define HAVE_GETINFO_FUNCS  #endif @@ -101,10 +101,6 @@ static int le_result, le_link, le_plink;  #define MYSQL_HAS_YEAR  #endif -#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007 -#define MYSQL_HAS_SET_CHARSET -#endif -  #define MYSQL_ASSOC		1<<0  #define MYSQL_NUM		1<<1  #define MYSQL_BOTH		(MYSQL_ASSOC|MYSQL_NUM) @@ -124,13 +120,24 @@ ZEND_DECLARE_MODULE_GLOBALS(mysql)  static PHP_GINIT_FUNCTION(mysql);  typedef struct _php_mysql_conn { -	MYSQL conn; +	MYSQL *conn;  	int active_result_id; +	int multi_query;  } php_mysql_conn; +#ifdef HAVE_MYSQLND +static MYSQLND_ZVAL_PCACHE *mysql_mysqlnd_zval_cache; +static MYSQLND_QCACHE		*mysql_mysqlnd_qcache; +#endif + +#define MYSQL_DISABLE_MQ if (mysql->multi_query) { \ +	mysql_set_server_option(mysql->conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \ +	mysql->multi_query = 0; \ +}  +  /* {{{ mysql_functions[]   */ -const zend_function_entry mysql_functions[] = { +static const zend_function_entry mysql_functions[] = {  	PHP_FE(mysql_connect,								NULL)  	PHP_FE(mysql_pconnect,								NULL)  	PHP_FE(mysql_close,									NULL) @@ -184,10 +191,8 @@ const zend_function_entry mysql_functions[] = {  	PHP_FE(mysql_get_server_info,						NULL)  #endif -	PHP_FE(mysql_info,		            				NULL) -#ifdef MYSQL_HAS_SET_CHARSET -	PHP_FE(mysql_set_charset,							NULL) -#endif +	PHP_FE(mysql_info,									NULL) +	   	/* for downwards compatability */  	PHP_FALIAS(mysql,				mysql_db_query,		NULL)  	PHP_FALIAS(mysql_fieldname,		mysql_field_name,	NULL) @@ -216,10 +221,23 @@ const zend_function_entry mysql_functions[] = {  };  /* }}} */ +/* Dependancies */ +static const zend_module_dep mysql_deps[] = { +#if defined(HAVE_MYSQLND) +	ZEND_MOD_REQUIRED("mysqlnd") +#endif +	{NULL, NULL, NULL} +}; +  /* {{{ mysql_module_entry   */  zend_module_entry mysql_module_entry = { -	STANDARD_MODULE_HEADER, +#if ZEND_MODULE_API_NO >= 20050922 +	STANDARD_MODULE_HEADER_EX, NULL, +	mysql_deps, +#elif ZEND_MODULE_API_NO >= 20010901 + 	STANDARD_MODULE_HEADER, +#endif  	"mysql",  	mysql_functions,  	ZEND_MODULE_STARTUP_N(mysql), @@ -244,6 +262,26 @@ void timeout(int sig);  #define CHECK_LINK(link) { if (link==-1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "A link to the server could not be established"); RETURN_FALSE; } } +#if defined(HAVE_MYSQLND) +#define PHPMY_UNBUFFERED_QUERY_CHECK() \ +{\ +	if (mysql->active_result_id) { \ +		do {					\ +			int type;			\ +			MYSQL_RES *mysql_result;	\ +							\ +			mysql_result = (MYSQL_RES *) zend_list_find(mysql->active_result_id, &type);	\ +			if (mysql_result && type==le_result) {						\ +				if (mysqli_result_is_unbuffered(mysql_result) && !mysql_eof(mysql_result)) { \ +					php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Function called without first fetching all rows from a previous unbuffered query");	\ +				}						\ +				zend_list_delete(mysql->active_result_id);	\ +				mysql->active_result_id = 0;			\ +			} \ +		} while(0); \ +	}\ +} +#else  #define PHPMY_UNBUFFERED_QUERY_CHECK()			\  {							\  	if (mysql->active_result_id) {			\ @@ -262,7 +300,8 @@ void timeout(int sig);  			}							\  		} while(0);							\  	}									\ -}										\ +} +#endif  /* {{{ _free_mysql_result   * This wrapper is required since mysql_free_result() returns an integer, and @@ -295,7 +334,7 @@ static int php_mysql_select_db(php_mysql_conn *mysql, char *db TSRMLS_DC)  {  	PHPMY_UNBUFFERED_QUERY_CHECK(); -	if (mysql_select_db(&mysql->conn, db) != 0) { +	if (mysql_select_db(mysql->conn, db) != 0) {  		return 0;  	} else {  		return 1; @@ -311,7 +350,7 @@ static void _close_mysql_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)  	void (*handler) (int);   	handler = signal(SIGPIPE, SIG_IGN); -	mysql_close(&link->conn); +	mysql_close(link->conn);  	signal(SIGPIPE, handler);  	efree(link);  	MySG(num_links)--; @@ -326,7 +365,7 @@ static void _close_mysql_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC)  	void (*handler) (int);  	handler = signal(SIGPIPE, SIG_IGN); -	mysql_close(&link->conn); +	mysql_close(link->conn);  	signal(SIGPIPE, handler);  	free(link); @@ -361,6 +400,10 @@ PHP_INI_BEGIN()  	STD_PHP_INI_ENTRY("mysql.default_socket",		NULL,	PHP_INI_ALL,		OnUpdateStringUnempty,	default_socket,	zend_mysql_globals,		mysql_globals)  	STD_PHP_INI_ENTRY("mysql.connect_timeout",		"60",	PHP_INI_ALL,		OnUpdateLong,		connect_timeout, 	zend_mysql_globals,		mysql_globals)  	STD_PHP_INI_BOOLEAN("mysql.trace_mode",			"0",	PHP_INI_ALL,		OnUpdateLong,		trace_mode, 		zend_mysql_globals,		mysql_globals) +	STD_PHP_INI_BOOLEAN("mysql.allow_local_infile",	"1",	PHP_INI_SYSTEM,		OnUpdateLong,		allow_local_infile, zend_mysql_globals,		mysql_globals) +#ifdef HAVE_MYSQLND +	STD_PHP_INI_ENTRY("mysql.cache_size",			"2000",	PHP_INI_SYSTEM,		OnUpdateLong,		cache_size,			zend_mysql_globals,		mysql_globals) +#endif  PHP_INI_END()  /* }}} */ @@ -377,7 +420,12 @@ static PHP_GINIT_FUNCTION(mysql)  	mysql_globals->connect_error = NULL;  	mysql_globals->connect_timeout = 0;  	mysql_globals->trace_mode = 0; +	mysql_globals->allow_local_infile = 1;  	mysql_globals->result_allocated = 0; +#ifdef HAVE_MYSQLND +	mysql_globals->cache_size = 0; +	mysql_globals->mysqlnd_thd_zval_cache = NULL; +#endif  }  /* }}} */ @@ -401,11 +449,16 @@ ZEND_MODULE_STARTUP_D(mysql)  	REGISTER_LONG_CONSTANT("MYSQL_CLIENT_INTERACTIVE", CLIENT_INTERACTIVE, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQL_CLIENT_IGNORE_SPACE", CLIENT_IGNORE_SPACE, CONST_CS | CONST_PERSISTENT);  +#ifndef HAVE_MYSQLND  #if MYSQL_VERSION_ID >= 40000  	if (mysql_server_init(0, NULL, NULL)) {  		return FAILURE;  	}  #endif +#else +	mysql_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MySG(cache_size)); +	mysql_mysqlnd_qcache = mysqlnd_qcache_init_cache(); +#endif  	return SUCCESS;  } @@ -415,17 +468,25 @@ ZEND_MODULE_STARTUP_D(mysql)   */  PHP_MSHUTDOWN_FUNCTION(mysql)  { +#ifndef HAVE_MYSQLND  #if MYSQL_VERSION_ID >= 40000  #ifdef PHP_WIN32  	unsigned long client_ver = mysql_get_client_version(); -	/* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */ -	if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) { +	/* +	  Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows. +	  PHP bug#41350 MySQL bug#25621 +	*/ +	if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) {  		mysql_server_end();  	}  #else  	mysql_server_end();  #endif  #endif +#else +	mysqlnd_palloc_free_cache(mysql_mysqlnd_zval_cache); +	mysqlnd_qcache_free_cache_reference(&mysql_mysqlnd_qcache); +#endif  	UNREGISTER_INI_ENTRIES();  	return SUCCESS; @@ -436,7 +497,7 @@ PHP_MSHUTDOWN_FUNCTION(mysql)   */  PHP_RINIT_FUNCTION(mysql)  { -#if defined(ZTS) && MYSQL_VERSION_ID >= 40000 +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000  	if (mysql_thread_init()) {  		return FAILURE;  	} @@ -448,6 +509,10 @@ PHP_RINIT_FUNCTION(mysql)  	MySG(connect_errno) =0;  	MySG(result_allocated) = 0; +#ifdef HAVE_MYSQLND +	MySG(mysqlnd_thd_zval_cache) = mysqlnd_palloc_rinit(mysql_mysqlnd_zval_cache); +#endif +  	return SUCCESS;  }  /* }}} */ @@ -456,7 +521,7 @@ PHP_RINIT_FUNCTION(mysql)   */  PHP_RSHUTDOWN_FUNCTION(mysql)  { -#if defined(ZTS) && MYSQL_VERSION_ID >= 40000 +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000  	mysql_thread_end();  #endif @@ -469,6 +534,10 @@ PHP_RSHUTDOWN_FUNCTION(mysql)  	if (MySG(connect_error)!=NULL) {  		efree(MySG(connect_error));  	} +#ifdef HAVE_MYSQLND +	mysqlnd_palloc_rshutdown(MySG(mysqlnd_thd_zval_cache)); +#endif +  	return SUCCESS;  }  /* }}} */ @@ -486,12 +555,26 @@ PHP_MINFO_FUNCTION(mysql)  	snprintf(buf, sizeof(buf), "%ld", MySG(num_links));  	php_info_print_table_row(2, "Active Links", buf);  	php_info_print_table_row(2, "Client API version", mysql_get_client_info()); -#if !defined (PHP_WIN32) && !defined (NETWARE) +#if !defined (PHP_WIN32) && !defined (NETWARE) && !defined(HAVE_MYSQLND)  	php_info_print_table_row(2, "MYSQL_MODULE_TYPE", PHP_MYSQL_TYPE);  	php_info_print_table_row(2, "MYSQL_SOCKET", MYSQL_UNIX_ADDR);  	php_info_print_table_row(2, "MYSQL_INCLUDE", PHP_MYSQL_INCLUDE);  	php_info_print_table_row(2, "MYSQL_LIBS", PHP_MYSQL_LIBS);  #endif +#if defined(HAVE_MYSQLND) +	{ +		zval values; + +		php_info_print_table_header(2, "Persistent cache", mysql_mysqlnd_zval_cache? "enabled":"disabled"); +		 +		if (mysql_mysqlnd_zval_cache) { +			/* Now report cache status */ +			mysqlnd_palloc_stats(mysql_mysqlnd_zval_cache, &values); +			mysqlnd_minfo_print_hash(&values); +			zval_dtor(&values); +		} +	} +#endif  	php_info_print_table_end(); @@ -511,9 +594,14 @@ PHP_MINFO_FUNCTION(mysql)  	MYSQL_DO_CONNECT_CLEANUP();				\  	RETURN_FALSE; +#ifdef HAVE_MYSQLND +#define MYSQL_PORT 0 +#endif +  static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  {  	char *user=NULL, *passwd=NULL, *host_and_port=NULL, *socket=NULL, *tmp=NULL, *host=NULL; +	int  user_len, passwd_len, host_len;  	char *hashed_details=NULL;  	int hashed_details_length, port = MYSQL_PORT;  	int client_flags = 0; @@ -521,7 +609,6 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  #if MYSQL_VERSION_ID <= 32230  	void (*handler) (int);  #endif -	zval **z_host=NULL, **z_user=NULL, **z_passwd=NULL, **z_new_link=NULL, **z_client_flags=NULL;  	zend_bool free_host=0, new_link=0;  	long connect_timeout; @@ -560,93 +647,20 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  		user = MySG(default_user);  		passwd = MySG(default_password); -		switch(ZEND_NUM_ARGS()) { -			case 0: /* defaults */ -				break; -			case 1: {					 -					if (zend_get_parameters_ex(1, &z_host)==FAILURE) { -						MYSQL_DO_CONNECT_RETURN_FALSE(); -					} -				} -				break; -			case 2: { -					if (zend_get_parameters_ex(2, &z_host, &z_user)==FAILURE) { -						MYSQL_DO_CONNECT_RETURN_FALSE(); -					} -					convert_to_string_ex(z_user); -					user = Z_STRVAL_PP(z_user); -				} -				break; -			case 3: { -					if (zend_get_parameters_ex(3, &z_host, &z_user, &z_passwd) == FAILURE) { -						MYSQL_DO_CONNECT_RETURN_FALSE(); -					} -					convert_to_string_ex(z_user); -					convert_to_string_ex(z_passwd); -					user = Z_STRVAL_PP(z_user); -					passwd = Z_STRVAL_PP(z_passwd); -				} -				break; -			case 4: { -					if (!persistent) { -						if (zend_get_parameters_ex(4, &z_host, &z_user, &z_passwd, &z_new_link) == FAILURE) { -							MYSQL_DO_CONNECT_RETURN_FALSE(); -						} -						convert_to_string_ex(z_user); -						convert_to_string_ex(z_passwd); -						convert_to_boolean_ex(z_new_link); -						user = Z_STRVAL_PP(z_user); -						passwd = Z_STRVAL_PP(z_passwd); -						new_link = Z_BVAL_PP(z_new_link); -					} -					else { -						if (zend_get_parameters_ex(4, &z_host, &z_user, &z_passwd, &z_client_flags) == FAILURE) { -							MYSQL_DO_CONNECT_RETURN_FALSE(); -						} -						convert_to_string_ex(z_user); -						convert_to_string_ex(z_passwd); -						convert_to_long_ex(z_client_flags); -						user = Z_STRVAL_PP(z_user); -						passwd = Z_STRVAL_PP(z_passwd); -						client_flags = Z_LVAL_PP(z_client_flags); -					} -				} -				break; -			case 5: { -					if (zend_get_parameters_ex(5, &z_host, &z_user, &z_passwd, &z_new_link, &z_client_flags) == FAILURE) { -						MYSQL_DO_CONNECT_RETURN_FALSE(); -					} -					convert_to_string_ex(z_user); -					convert_to_string_ex(z_passwd); -					convert_to_boolean_ex(z_new_link); -					convert_to_long_ex(z_client_flags); -					user = Z_STRVAL_PP(z_user); -					passwd = Z_STRVAL_PP(z_passwd); -					new_link = Z_BVAL_PP(z_new_link); -					client_flags = Z_LVAL_PP(z_client_flags); -				} -				break; -			default: -				WRONG_PARAM_COUNT; -				break; +		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sssll", &host_and_port, &host_len, +									&user, &user_len, &passwd, &passwd_len,  +									&new_link, &client_flags)==FAILURE) { +			WRONG_PARAM_COUNT;  		} -		/* disable local infile option for open_basedir */ -		if (((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) && (client_flags & CLIENT_LOCAL_FILES)) { -                	client_flags ^= CLIENT_LOCAL_FILES; + +		/* mysql_pconnect does not support new_link parameter */ +		if (persistent) { +			client_flags= new_link;  		} -		if (z_host) { -			SEPARATE_ZVAL(z_host); /* We may modify z_host if it contains a port, separate */ -			convert_to_string_ex(z_host); -			host_and_port = Z_STRVAL_PP(z_host); -			if (z_user) { -				convert_to_string_ex(z_user); -				user = Z_STRVAL_PP(z_user); -				if (z_passwd) { -					convert_to_string_ex(z_passwd); -					passwd = Z_STRVAL_PP(z_passwd); -				} -			} +		/* disable local infile option for open_basedir */ +		if (((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) && (client_flags & CLIENT_LOCAL_FILES)) { +			client_flags ^= CLIENT_LOCAL_FILES;  		}  		hashed_details_length = spprintf(&hashed_details, 0, "mysql_%s_%s_%s_%d", SAFE_STRING(host_and_port), SAFE_STRING(user), SAFE_STRING(passwd), client_flags); @@ -687,12 +701,12 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  		if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) {  /* we don't */  			zend_rsrc_list_entry new_le; -			if (MySG(max_links)!=-1 && MySG(num_links)>=MySG(max_links)) { +			if (MySG(max_links) != -1 && MySG(num_links) >= MySG(max_links)) {  				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MySG(num_links));  				efree(hashed_details);  				MYSQL_DO_CONNECT_RETURN_FALSE();  			} -			if (MySG(max_persistent)!=-1 && MySG(num_persistent)>=MySG(max_persistent)) { +			if (MySG(max_persistent) != -1 && MySG(num_persistent) >= MySG(max_persistent)) {  				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open persistent links (%ld)", MySG(num_persistent));  				efree(hashed_details);  				MYSQL_DO_CONNECT_RETURN_FALSE(); @@ -700,28 +714,36 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  			/* create the link */  			mysql = (php_mysql_conn *) malloc(sizeof(php_mysql_conn));  			mysql->active_result_id = 0; -#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */ -			mysql_init(&mysql->conn); - -			if (connect_timeout != -1) { -				mysql_options(&mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout); -			} +			mysql->multi_query = 1; +#ifndef HAVE_MYSQLND +			mysql->conn = mysql_init(NULL); +#else +			mysql->conn = mysql_init(persistent); +#endif -			if (mysql_real_connect(&mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) { +			if (connect_timeout != -1) +				mysql_options(mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout); +#ifndef HAVE_MYSQLND +			if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)  #else -			if (mysql_connect(&mysql->conn, host, user, passwd)==NULL) { +			if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,  +								port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)  #endif +			{  				/* Populate connect error globals so that the error functions can read them */ -				if (MySG(connect_error)!=NULL) efree(MySG(connect_error)); -				MySG(connect_error)=estrdup(mysql_error(&mysql->conn)); +				if (MySG(connect_error) != NULL) { +					efree(MySG(connect_error)); +				} +				MySG(connect_error) = estrdup(mysql_error(mysql->conn));  				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", MySG(connect_error));  #if defined(HAVE_MYSQL_ERRNO) -				MySG(connect_errno)=mysql_errno(&mysql->conn); +				MySG(connect_errno) = mysql_errno(mysql->conn);  #endif  				free(mysql);  				efree(hashed_details);  				MYSQL_DO_CONNECT_RETURN_FALSE();  			} +			mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));  			/* hash it up */  			Z_TYPE(new_le) = le_plink; @@ -737,36 +759,31 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  			if (Z_TYPE_P(le) != le_plink) {  				MYSQL_DO_CONNECT_RETURN_FALSE();  			} +			mysql = (php_mysql_conn *) le->ptr; +			mysql->active_result_id = 0; +			mysql->multi_query = 1;  			/* ensure that the link did not die */ -#if MYSQL_VERSION_ID > 32230 /* Use mysql_ping to ensure link is alive (and to reconnect if needed) */ -			if (mysql_ping(le->ptr)) { -#else	/* Use mysql_stat() to check if server is alive */ -			handler=signal(SIGPIPE, SIG_IGN); -#if defined(HAVE_MYSQL_ERRNO) && defined(CR_SERVER_GONE_ERROR) -			mysql_stat(le->ptr); -			if (mysql_errno(&((php_mysql_conn *) le->ptr)->conn) == CR_SERVER_GONE_ERROR) { -#else -			if (!strcasecmp(mysql_stat(le->ptr), "mysql server has gone away")) { /* the link died */ -#endif -				signal(SIGPIPE, handler); -#endif /* end mysql_ping */ -#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */ -				if (mysql_real_connect(le->ptr, host, user, passwd, NULL, port, socket, client_flags)==NULL) { +			if (mysql_ping(mysql->conn)) { +				if (mysql_errno(mysql->conn) == 2006) { +#ifndef HAVE_MYSQLND +					if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)  #else -				if (mysql_connect(le->ptr, host, user, passwd)==NULL) { +					if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,  +										port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)  #endif -					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect"); -					zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1); -					efree(hashed_details); -					MYSQL_DO_CONNECT_RETURN_FALSE(); +					{ +						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect"); +						zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1); +						efree(hashed_details); +						MYSQL_DO_CONNECT_RETURN_FALSE(); +					} +					mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));  				} -			} -#if MYSQL_VERSION_ID < 32231 -			signal(SIGPIPE, handler); +			} else { +#ifdef HAVE_MYSQLND +				mysqlnd_restart_psession(mysql->conn);  #endif - -			mysql = (php_mysql_conn *) le->ptr; -			mysql->active_result_id = 0; +			}  		}  		ZEND_REGISTER_RESOURCE(return_value, mysql, le_plink);  	} else { /* non persistent */ @@ -799,7 +816,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  				zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);  			}  		} -		if (MySG(max_links)!=-1 && MySG(num_links)>=MySG(max_links)) { +		if (MySG(max_links) != -1 && MySG(num_links) >= MySG(max_links)) {  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MySG(num_links));  			efree(hashed_details);  			MYSQL_DO_CONNECT_RETURN_FALSE(); @@ -807,28 +824,41 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)  		mysql = (php_mysql_conn *) emalloc(sizeof(php_mysql_conn));  		mysql->active_result_id = 0; -#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */ -		mysql_init(&mysql->conn); +		mysql->multi_query = 1; +#ifndef HAVE_MYSQLND +		mysql->conn = mysql_init(NULL); +#else +		mysql->conn = mysql_init(persistent); +#endif -		if (connect_timeout != -1) { -			mysql_options(&mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout); -		} +		if (connect_timeout != -1) +				mysql_options(mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout); -		if (mysql_real_connect(&mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) { +#ifndef HAVE_MYSQLND +		if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)   #else -		if (mysql_connect(&mysql->conn, host, user, passwd)==NULL) { +		if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,  +							port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)  #endif +		{  			/* Populate connect error globals so that the error functions can read them */ -			if (MySG(connect_error)!=NULL) efree(MySG(connect_error)); -			MySG(connect_error)=estrdup(mysql_error(&mysql->conn)); +			if (MySG(connect_error) != NULL) { +				efree(MySG(connect_error)); +			} +			MySG(connect_error) = estrdup(mysql_error(mysql->conn));  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", MySG(connect_error));  #if defined(HAVE_MYSQL_ERRNO) -			MySG(connect_errno)=mysql_errno(&mysql->conn); +			MySG(connect_errno) = mysql_errno(mysql->conn); +#endif +			/* free mysql structure */ +#ifdef HAVE_MYSQLND +			mysqli_close(mysql->conn, MYSQLI_CLOSE_DISCONNECTED);  #endif  			efree(hashed_details);  			efree(mysql);  			MYSQL_DO_CONNECT_RETURN_FALSE();  		} +		mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));  		/* add it to the list */  		ZEND_REGISTER_RESOURCE(return_value, mysql, le_link); @@ -997,7 +1027,7 @@ PHP_FUNCTION(mysql_get_host_info)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_STRING((char *)mysql_get_host_info(&mysql->conn),1); +	RETURN_STRING((char *)mysql_get_host_info(mysql->conn),1);  }  /* }}} */ @@ -1027,7 +1057,7 @@ PHP_FUNCTION(mysql_get_proto_info)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_LONG(mysql_get_proto_info(&mysql->conn)); +	RETURN_LONG(mysql_get_proto_info(mysql->conn));  }  /* }}} */ @@ -1057,7 +1087,7 @@ PHP_FUNCTION(mysql_get_server_info)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_STRING((char *)mysql_get_server_info(&mysql->conn),1); +	RETURN_STRING((char *)mysql_get_server_info(mysql->conn),1);  }  /* }}} */ @@ -1081,7 +1111,7 @@ PHP_FUNCTION(mysql_info)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink); -	if ((str = (char *)mysql_info(&mysql->conn))) { +	if ((str = (char *)mysql_info(mysql->conn))) {  		RETURN_STRING(str,1);  	} else {  		RETURN_FALSE; @@ -1107,7 +1137,7 @@ PHP_FUNCTION(mysql_thread_id)  	}  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_LONG(mysql_thread_id(&mysql->conn)); +	RETURN_LONG(mysql_thread_id(mysql->conn));  }  /* }}} */ @@ -1118,6 +1148,10 @@ PHP_FUNCTION(mysql_stat)  	zval *mysql_link = NULL;  	int id = -1;  	php_mysql_conn *mysql; +	char *stat; +#ifdef HAVE_MYSQLND +	uint stat_len; +#endif  	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &mysql_link) == FAILURE) {  		return; @@ -1130,8 +1164,16 @@ PHP_FUNCTION(mysql_stat)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);  	PHPMY_UNBUFFERED_QUERY_CHECK(); - -	RETURN_STRING((char *)mysql_stat(&mysql->conn), 1); +#ifndef HAVE_MYSQLND +	if ((stat = (char *)mysql_stat(mysql->conn))) { +		RETURN_STRING(stat, 1); +#else +	if (mysqlnd_stat(mysql->conn, &stat, &stat_len) == PASS) { +		RETURN_STRINGL(stat, stat_len, 0); +#endif +	} else { +		RETURN_FALSE; +	}  }  /* }}} */ @@ -1153,38 +1195,7 @@ PHP_FUNCTION(mysql_client_encoding)  	}  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink); - -	RETURN_STRING((char *)mysql_character_set_name(&mysql->conn), 1); -} -/* }}} */ -#endif - -#ifdef MYSQL_HAS_SET_CHARSET -/* {{{ proto bool mysql_set_charset(string csname [, int link_identifier]) -   sets client character set */ -PHP_FUNCTION(mysql_set_charset) -{ -	zval *mysql_link = NULL; -	char *csname; -	int id = -1, csname_len; -	php_mysql_conn *mysql; - -	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &csname, &csname_len, &mysql_link) == FAILURE) { -		return; -	} - -	if (ZEND_NUM_ARGS() == 1) { -		id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU); -		CHECK_LINK(id); -	} - -	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink); - -	if (!mysql_set_character_set(&mysql->conn, csname)) { -		RETURN_TRUE; -	} else { -		RETURN_FALSE; -	} +	RETURN_STRING((char *)mysql_character_set_name(mysql->conn), 1);  }  /* }}} */  #endif @@ -1224,7 +1235,7 @@ PHP_FUNCTION(mysql_create_db)  	convert_to_string_ex(db); -	if (mysql_create_db(&mysql->conn, Z_STRVAL_PP(db))==0) { +	if (mysql_create_db(mysql->conn, Z_STRVAL_PP(db))==0) {  		RETURN_TRUE;  	} else {  		RETURN_FALSE; @@ -1263,7 +1274,7 @@ PHP_FUNCTION(mysql_drop_db)  	convert_to_string_ex(db); -	if (mysql_drop_db(&mysql->conn, Z_STRVAL_PP(db))==0) { +	if (mysql_drop_db(mysql->conn, Z_STRVAL_PP(db))==0) {  		RETURN_TRUE;  	} else {  		RETURN_FALSE; @@ -1291,8 +1302,11 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link  	PHPMY_UNBUFFERED_QUERY_CHECK(); +	MYSQL_DISABLE_MQ; +  	convert_to_string_ex(query); +#ifndef HAVE_MYSQLND  	/* check explain */  	if (MySG(trace_mode)) {  		if (!strncasecmp("select", Z_STRVAL_PP(query), 6)){ @@ -1300,14 +1314,14 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link  			char *newquery;  			int newql = spprintf (&newquery, 0, "EXPLAIN %s", Z_STRVAL_PP(query)); -			mysql_real_query(&mysql->conn, newquery, newql); +			mysql_real_query(mysql->conn, newquery, newql);  			efree (newquery); -			if (mysql_errno(&mysql->conn)) { -				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn)); +			if (mysql_errno(mysql->conn)) { +				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));  				RETURN_FALSE;  			}  			else { -    			mysql_result = mysql_use_result(&mysql->conn); +    			mysql_result = mysql_use_result(mysql->conn);  				while ((row = mysql_fetch_row(mysql_result))) {  					if (!strcmp("ALL", row[1])) {  						php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "Your query requires a full tablescan (table %s, %s rows affected). Use EXPLAIN to optimize your query.", row[0], row[6]); @@ -1319,36 +1333,37 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link  			}  		}	  	} /* end explain */ +#endif  	/* mysql_query is binary unsafe, use mysql_real_query */  #if MYSQL_VERSION_ID > 32199 -	if (mysql_real_query(&mysql->conn, Z_STRVAL_PP(query), Z_STRLEN_PP(query))!=0) { +	if (mysql_real_query(mysql->conn, Z_STRVAL_PP(query), Z_STRLEN_PP(query))!=0) {  		/* check possible error */  		if (MySG(trace_mode)){ -			if (mysql_errno(&mysql->conn)){ -				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn));  +			if (mysql_errno(mysql->conn)){ +				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));   			}  		}  		RETURN_FALSE;  	}  #else -	if (mysql_query(&mysql->conn, Z_STRVAL_PP(query))!=0) { +	if (mysql_query(mysql->conn, Z_STRVAL_PP(query))!=0) {  		/* check possible error */  		if (MySG(trace_mode)){ -			if (mysql_errno(&mysql->conn)){ -				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn));  +			if (mysql_errno(mysql->conn)){ +				php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));   			}  		}  		RETURN_FALSE;  	}  #endif  	if(use_store == MYSQL_USE_RESULT) { -		mysql_result=mysql_use_result(&mysql->conn); +		mysql_result=mysql_use_result(mysql->conn);  	} else { -		mysql_result=mysql_store_result(&mysql->conn); +		mysql_result=mysql_store_result(mysql->conn);  	}  	if (!mysql_result) { -		if (PHP_MYSQL_VALID_RESULT(&mysql->conn)) { /* query should have returned rows */ +		if (PHP_MYSQL_VALID_RESULT(mysql->conn)) { /* query should have returned rows */  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save result set");  			RETURN_FALSE;  		} else { @@ -1474,7 +1489,7 @@ PHP_FUNCTION(mysql_list_dbs)  	PHPMY_UNBUFFERED_QUERY_CHECK(); -	if ((mysql_result=mysql_list_dbs(&mysql->conn, NULL))==NULL) { +	if ((mysql_result=mysql_list_dbs(mysql->conn, NULL))==NULL) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");  		RETURN_FALSE;  	} @@ -1519,7 +1534,7 @@ PHP_FUNCTION(mysql_list_tables)  	PHPMY_UNBUFFERED_QUERY_CHECK(); -	if ((mysql_result=mysql_list_tables(&mysql->conn, NULL))==NULL) { +	if ((mysql_result=mysql_list_tables(mysql->conn, NULL))==NULL) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");  		RETURN_FALSE;  	} @@ -1566,7 +1581,7 @@ PHP_FUNCTION(mysql_list_fields)  	PHPMY_UNBUFFERED_QUERY_CHECK();  	convert_to_string_ex(table); -	if ((mysql_result=mysql_list_fields(&mysql->conn, Z_STRVAL_PP(table), NULL))==NULL) { +	if ((mysql_result=mysql_list_fields(mysql->conn, Z_STRVAL_PP(table), NULL))==NULL) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");  		RETURN_FALSE;  	} @@ -1596,7 +1611,7 @@ PHP_FUNCTION(mysql_list_processes)  	PHPMY_UNBUFFERED_QUERY_CHECK(); -	mysql_result = mysql_list_processes(&mysql->conn); +	mysql_result = mysql_list_processes(mysql->conn);  	if (mysql_result == NULL) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");  		RETURN_FALSE; @@ -1639,7 +1654,7 @@ PHP_FUNCTION(mysql_error)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_STRING((char *)mysql_error(&mysql->conn), 1); +	RETURN_STRING((char *)mysql_error(mysql->conn), 1);  }  /* }}} */ @@ -1677,7 +1692,7 @@ PHP_FUNCTION(mysql_errno)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink); -	RETURN_LONG(mysql_errno(&mysql->conn)); +	RETURN_LONG(mysql_errno(mysql->conn));  }  #endif  /* }}} */ @@ -1710,7 +1725,7 @@ PHP_FUNCTION(mysql_affected_rows)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);  	/* conversion from int64 to long happing here */ -	Z_LVAL_P(return_value) = (long) mysql_affected_rows(&mysql->conn); +	Z_LVAL_P(return_value) = (long) mysql_affected_rows(mysql->conn);  	Z_TYPE_P(return_value) = IS_LONG;  }  /* }}} */ @@ -1722,8 +1737,9 @@ PHP_FUNCTION(mysql_escape_string)  {  	zval **str; +  	if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &str) == FAILURE) { -		ZEND_WRONG_PARAM_COUNT(); +		WRONG_PARAM_COUNT;  	}  	convert_to_string_ex(str);  	/* assume worst case situation, which is 2x of the original string. @@ -1738,7 +1754,6 @@ PHP_FUNCTION(mysql_escape_string)  	if (MySG(trace_mode)){  		php_error_docref("function.mysql-real-escape-string" TSRMLS_CC, E_WARNING, "This function is deprecated; use mysql_real_escape_string() instead.");  	} -  }  /* }}} */ @@ -1765,7 +1780,7 @@ PHP_FUNCTION(mysql_real_escape_string)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);  	new_str = safe_emalloc(str_len, 2, 1); -	new_str_len = mysql_real_escape_string(&mysql->conn, new_str, str, str_len); +	new_str_len = mysql_real_escape_string(mysql->conn, new_str, str, str_len);  	new_str = erealloc(new_str, new_str_len + 1);  	RETURN_STRINGL(new_str, new_str_len, 0); @@ -1799,7 +1814,7 @@ PHP_FUNCTION(mysql_insert_id)  	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);  	/* conversion from int64 to long happing here */ -	Z_LVAL_P(return_value) = (long) mysql_insert_id(&mysql->conn); +	Z_LVAL_P(return_value) = (long) mysql_insert_id(mysql->conn);  	Z_TYPE_P(return_value) = IS_LONG;  }  /* }}} */ @@ -1811,8 +1826,10 @@ PHP_FUNCTION(mysql_result)  {  	zval **result, **row, **field=NULL;  	MYSQL_RES *mysql_result; +#ifndef HAVE_MYSQLND  	MYSQL_ROW sql_row;  	mysql_row_length_type *sql_row_lengths; +#endif  	int field_offset=0;  	switch (ZEND_NUM_ARGS()) { @@ -1839,10 +1856,6 @@ PHP_FUNCTION(mysql_result)  		RETURN_FALSE;  	}  	mysql_data_seek(mysql_result, Z_LVAL_PP(row)); -	if ((sql_row=mysql_fetch_row(mysql_result))==NULL  -		|| (sql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) { /* shouldn't happen? */ -		RETURN_FALSE; -	}  	if (field) {  		switch(Z_TYPE_PP(field)) { @@ -1892,6 +1905,11 @@ PHP_FUNCTION(mysql_result)  		}  	} +#ifndef HAVE_MYSQLND +	if ((sql_row=mysql_fetch_row(mysql_result))==NULL  +		|| (sql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) { /* shouldn't happen? */ +		RETURN_FALSE; +	}  	if (sql_row[field_offset]) {  		Z_TYPE_P(return_value) = IS_STRING; @@ -1904,6 +1922,9 @@ PHP_FUNCTION(mysql_result)  	} else {  		Z_TYPE_P(return_value) = IS_NULL;  	} +#else +	mysqlnd_result_fetch_field_data(mysql_result, field_offset, return_value); +#endif  }  /* }}} */ @@ -1951,12 +1972,14 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,  {  	zval **result, **arg2;  	MYSQL_RES *mysql_result; -	MYSQL_ROW mysql_row; -	MYSQL_FIELD *mysql_field; -	mysql_row_length_type *mysql_row_lengths; -	int i;  	zval            *res, *ctor_params = NULL;  	zend_class_entry *ce = NULL; +#ifndef HAVE_MYSQLND +	int i; +	MYSQL_FIELD *mysql_field; +	MYSQL_ROW mysql_row; +	mysql_row_length_type *mysql_row_lengths; +#endif  #ifdef ZEND_ENGINE_2  	if (into_object) { @@ -2007,20 +2030,24 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,  	}  	if ((result_type & MYSQL_BOTH) == 0) { -		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQL_NUM, MYSQL_ASSOC or MYSQL_BOTH."); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQL_NUM, MYSQL_ASSOC or MYSQL_BOTH");  	}  	ZEND_FETCH_RESOURCE(mysql_result, MYSQL_RES *, result, -1, "MySQL result", le_result); -	if ((mysql_row=mysql_fetch_row(mysql_result))==NULL  -		|| (mysql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) { +#ifndef HAVE_MYSQLND +	if ((mysql_row = mysql_fetch_row(mysql_result)) == NULL  || +		(mysql_row_lengths = mysql_fetch_lengths(mysql_result)) == NULL) {  		RETURN_FALSE;  	}  	array_init(return_value);  	mysql_field_seek(mysql_result, 0); -	for (mysql_field=mysql_fetch_field(mysql_result), i=0; mysql_field; mysql_field=mysql_fetch_field(mysql_result), i++) { +	for (mysql_field = mysql_fetch_field(mysql_result), i = 0; +		 mysql_field; +		 mysql_field = mysql_fetch_field(mysql_result), i++) +	{  		if (mysql_row[i]) {  			zval *data; @@ -2053,9 +2080,13 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,  			}  		}  	} +#else +	mysqlnd_fetch_into(mysql_result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQL); +#endif  #ifdef ZEND_ENGINE_2 -	if (into_object) { +	/* mysqlnd might return FALSE if no more rows */ +	if (into_object && Z_TYPE_P(return_value) != IS_BOOL) {  		zval dataset = *return_value;  		zend_fcall_info fci;  		zend_fcall_info_cache fcc; @@ -2127,7 +2158,19 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,     Gets a result row as an enumerated array */  PHP_FUNCTION(mysql_fetch_row)  { +#ifdef HAVE_MYSQLND +	MYSQL_RES		*result; +	zval			*mysql_result; + +	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &mysql_result) == FAILURE) { +		return; +	} +	ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result); + +	mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, return_value, MYSQLND_MYSQL);  +#else  	php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_NUM, 1, 0); +#endif  }  /* }}} */ @@ -2149,7 +2192,20 @@ PHP_FUNCTION(mysql_fetch_object)     Fetch a result row as an array (associative, numeric or both) */  PHP_FUNCTION(mysql_fetch_array)  { +#ifndef HAVE_MYSQLND  	php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 2, 0); +#else +	MYSQL_RES		*result; +	zval			*mysql_result; +	long			mode = MYSQLND_FETCH_BOTH; + +	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &mysql_result, &mode) == FAILURE) { +		return; +	} +	ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result); + +	mysqlnd_fetch_into(result, mode, return_value, MYSQLND_MYSQL); +#endif  }  /* }}} */ @@ -2158,7 +2214,19 @@ PHP_FUNCTION(mysql_fetch_array)     Fetch a result row as an associative array */  PHP_FUNCTION(mysql_fetch_assoc)  { +#ifndef HAVE_MYSQLND  	php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_ASSOC, 1, 0); +#else +	MYSQL_RES		*result; +	zval			*mysql_result; + +	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &mysql_result) == FAILURE) { +		return; +	} +	ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result); + +	mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQL); +#endif  }  /* }}} */ @@ -2223,6 +2291,9 @@ static char *php_mysql_get_field_name(int field_type)  		case FIELD_TYPE_VAR_STRING:  			return "string";  			break; +#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND) +		case MYSQL_TYPE_BIT: +#endif  #ifdef MYSQL_HAS_TINY  		case FIELD_TYPE_TINY:  #endif @@ -2585,7 +2656,7 @@ PHP_FUNCTION(mysql_ping)  	PHPMY_UNBUFFERED_QUERY_CHECK(); -	RETURN_BOOL(! mysql_ping(&mysql->conn)); +	RETURN_BOOL(! mysql_ping(mysql->conn));  }  /* }}} */ diff --git a/ext/mysql/php_mysql.h b/ext/mysql/php_mysql.h index a088208a4b..fe21af15c5 100644 --- a/ext/mysql/php_mysql.h +++ b/ext/mysql/php_mysql.h @@ -34,6 +34,25 @@  #include "TSRM.h"  #endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#if defined(HAVE_MYSQLND) +#include "ext/mysqlnd/mysqlnd.h" +#include "ext/mysql/mysql_mysqlnd.h" +#else +#include <mysql.h> +#endif + +#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007 || HAVE_MYSQLND +#define MYSQL_HAS_SET_CHARSET +#endif +  extern zend_module_entry mysql_module_entry;  #define mysql_module_ptr &mysql_module_entry @@ -91,9 +110,6 @@ PHP_FUNCTION(mysql_stat);  PHP_FUNCTION(mysql_thread_id);  PHP_FUNCTION(mysql_client_encoding);  PHP_FUNCTION(mysql_ping); -#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007 -PHP_FUNCTION(mysql_set_charset); -#endif  ZEND_BEGIN_MODULE_GLOBALS(mysql)  	long default_link; @@ -108,6 +124,12 @@ ZEND_BEGIN_MODULE_GLOBALS(mysql)  	long connect_timeout;  	long result_allocated;  	long trace_mode; +	long allow_local_infile; +#ifdef HAVE_MYSQLND +	MYSQLND_THD_ZVAL_PCACHE *mysqlnd_thd_zval_cache; +	MYSQLND_QCACHE			*mysqlnd_qcache; +	long					cache_size; +#endif  ZEND_END_MODULE_GLOBALS(mysql)  #ifdef ZTS diff --git a/ext/mysqli/config.m4 b/ext/mysqli/config.m4 index 19bc252bb0..7302e66c42 100644 --- a/ext/mysqli/config.m4 +++ b/ext/mysqli/config.m4 @@ -3,18 +3,17 @@ dnl $Id$  dnl config.m4 for extension mysqli  PHP_ARG_WITH(mysqli, for MySQLi support, -[  --with-mysqli[=FILE]    Include MySQLi support.  FILE is the optional pathname  -                          to mysql_config [mysql_config]]) +[  --with-mysqli[=FILE]    Include MySQLi support. FILE is the optional pathname to mysql_config [mysql_config]. +                          If mysqlnd is passed as FILE, the MySQL native driver will be used])  PHP_ARG_ENABLE(embedded_mysqli, whether to enable embedded MySQLi support,  [  --enable-embedded-mysqli  MYSQLi: Enable embedded support], no, no) -if test "$PHP_MYSQLI" != "no"; then +if test "$PHP_MYSQLI" = "mysqlnd"; then +  dnl This needs to be set in any extension which wishes to use mysqlnd +  PHP_MYSQLND_ENABLED=yes -dnl there are no mysql libs currently bundled with PHP.. --Jani -dnl  if test "$PHP_MYSQL" = "yes"; then -dnl    AC_MSG_ERROR([--with-mysql (using bundled libs) can not be used together with --with-mysqli.]) -dnl  fi +elif test "$PHP_MYSQLI" != "no"; then    if test "$PHP_MYSQLI" = "yes"; then      MYSQL_CONFIG=`$php_shtool path mysql_config` @@ -26,6 +25,8 @@ dnl  fi    if test "$PHP_EMBEDDED_MYSQLI" = "yes"; then      AC_DEFINE(HAVE_EMBEDDED_MYSQLI, 1, [embedded MySQL support enabled])      MYSQL_LIB_CFG='--libmysqld-libs' +    dnl mysqlnd doesn't support embedded, so we have to add some extra stuff +    mysqli_extra_sources="mysqli_embedded.c"    elif test "$enable_maintainer_zts" = "yes"; then      MYSQL_LIB_CFG='--libs_r'      MYSQL_LIB_NAME='mysqlclient_r' @@ -48,17 +49,29 @@ dnl  fi    [      PHP_EVAL_INCLINE($MYSQLI_INCLINE)      PHP_EVAL_LIBLINE($MYSQLI_LIBLINE, MYSQLI_SHARED_LIBADD) -    AC_DEFINE(HAVE_MYSQLILIB,1,[ ]) -    PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_stmt_field_count, +    AC_DEFINE(HAVE_MYSQLILIB, 1, [ ]) +    PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_character_set,      [ ],[ -		AC_MSG_ERROR([MySQLI doesn't support versions < 4.1.3 (for MySQL 4.1.x) and < 5.0.1 for (MySQL 5.0.x) anymore. Please update your libraries.]) -	],[$MYSQLI_LIBLINE]) +      AC_MSG_ERROR([MySQLI doesn't support versions < 4.1.13 (for MySQL 4.1.x) and < 5.0.7 for (MySQL 5.0.x) anymore. Please update your libraries.]) +    ],[$MYSQLI_LIBLINE])    ],[      AC_MSG_ERROR([wrong mysql library version or lib not found. Check config.log for more information.])    ],[      $MYSQLI_LIBLINE    ]) -  PHP_NEW_EXTENSION(mysqli, mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c mysqli_fe.c mysqli_report.c mysqli_repl.c mysqli_driver.c mysqli_warning.c mysqli_exception.c mysqli_embedded.c, $ext_shared) +  mysqli_extra_sources="$mysqli_extra_sources mysqli_repl.c" +fi + +dnl Build extension +if test "$PHP_MYSQLI" != "no"; then +  mysqli_sources="mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c \ +                  mysqli_fe.c mysqli_report.c mysqli_driver.c mysqli_warning.c \ +                  mysqli_exception.c $mysqli_extra_sources" +  PHP_NEW_EXTENSION(mysqli, $mysqli_sources, $ext_shared)    PHP_SUBST(MYSQLI_SHARED_LIBADD) + +  if test "$PHP_MYSQLI" = "mysqlnd"; then +    PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) +  fi  fi diff --git a/ext/mysqli/config.w32 b/ext/mysqli/config.w32 index 789112ea14..0f418f0cba 100644 --- a/ext/mysqli/config.w32 +++ b/ext/mysqli/config.w32 @@ -1,14 +1,42 @@  // $Id$  // vim:ft=javascript +// Note: The extension name is "mysqli", you enable it with "--with-mysqli". +// Passing value "mysqlnd" to it enables the bundled +// client library to connect to the MySQL server, i.e. no external MySQL +// client library is needed to perform the build. +  ARG_WITH("mysqli", "MySQLi support", "no");  if (PHP_MYSQLI != "no") { -	if (CHECK_LIB("libmysql.lib", "mysqli", PHP_MYSQLI) && -			CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQLI", PHP_MYSQLI + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQLI)) { -		EXTENSION("mysqli", "mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c mysqli_fe.c mysqli_report.c mysqli_repl.c mysqli_driver.c mysqli_warning.c mysqli_exception.c mysqli_embedded.c"); -		AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); +	mysqli_source = +		"mysqli.c " + +		"mysqli_api.c " + +		"mysqli_driver.c " + +		"mysqli_embedded.c " + +		"mysqli_exception.c " + +		"mysqli_fe.c " + +		"mysqli_nonapi.c " + +		"mysqli_prop.c " + +		"mysqli_report.c " + +		"mysqli_warning.c"; + +	if (PHP_MYSQLI != "mysqlnd") { +		if (CHECK_LIB("libmysql.lib", "mysqli", PHP_MYSQLI) && +			CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQLI", PHP_MYSQLI +  +										"\\include;" + PHP_PHP_BUILD + +										"\\include\\mysql;" + PHP_MYSQLI)) { +			// No "mysqli_repl.c" when using "mysqlnd" +			mysqli_extra_sources = "mysqli_repl.c"; +			EXTENSION("mysqli", mysqli_source + " " + mysqli_extra_sources); +			AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); +		} else { +			WARNING("mysqli not enabled; libraries and headers not found"); +		}  	} else { -		WARNING("mysqli not enabled; libraries and headers not found"); +		EXTENSION("mysqli", mysqli_source); +		AC_DEFINE('HAVE_MYSQLND', 1, 'MySQLi with native driver support enabled'); +		AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); +		ADD_EXTENSION_DEP('mysqli', 'mysqlnd', true);  	}  } diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index ff4f206b7a..8de80f5e05 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -28,7 +28,7 @@  #include "php_ini.h"  #include "ext/standard/info.h"  #include "ext/standard/php_string.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  #include "zend_exceptions.h"  #define MYSQLI_STORE_RESULT 0 @@ -52,6 +52,12 @@ zend_class_entry *mysqli_driver_class_entry;  zend_class_entry *mysqli_warning_class_entry;  zend_class_entry *mysqli_exception_class_entry; +#ifdef HAVE_MYSQLND +MYSQLND_ZVAL_PCACHE *mysqli_mysqlnd_zval_cache; +MYSQLND_QCACHE		*mysqli_mysqlnd_qcache; +#endif + +  extern void php_mysqli_connect(INTERNAL_FUNCTION_PARAMETERS);  typedef int (*mysqli_read_t)(mysqli_object *obj, zval **retval TSRMLS_DC); @@ -62,6 +68,63 @@ typedef struct _mysqli_prop_handler {  	mysqli_write_t write_func;  } mysqli_prop_handler; +static int le_pmysqli; + +static int php_mysqli_persistent_on_rshut(zend_rsrc_list_entry *le TSRMLS_DC) +{ +	if (le->type == le_pmysqli) { +		mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr; +		HashPosition pos; +		MYSQL **mysql; +		ulong idx; +		dtor_func_t pDestructor = plist->used_links.pDestructor; +		plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */ + +		zend_hash_internal_pointer_reset_ex(&plist->used_links, &pos); +		while (SUCCESS == zend_hash_get_current_data_ex(&plist->used_links, (void **)&mysql, &pos)) { +			zend_hash_get_current_key_ex(&plist->used_links, NULL, NULL, &idx, FALSE, &pos); +			/* Make it free */ +			zend_hash_next_index_insert(&plist->free_links, mysql, sizeof(MYSQL *), NULL); +			/* First move forward */			 +			zend_hash_move_forward_ex(&plist->used_links, &pos); +			/* The delete, because del will free memory, but we need it's ->nextItem */ +			zend_hash_index_del(&plist->used_links, idx); +		} + +		/* restore pDestructor, which should be php_mysqli_dtor_p_elements() */ +		plist->used_links.pDestructor = pDestructor; +	} +	return ZEND_HASH_APPLY_KEEP; +} + +/* Destructor for mysqli entries in free_links/used_links */ +void php_mysqli_dtor_p_elements(void *data) +{ +	MYSQL **mysql = (MYSQL **) data; +	TSRMLS_FETCH(); +#if defined(HAVE_MYSQLND) +	mysqlnd_end_psession(*mysql); +#endif +	mysqli_close(*mysql, MYSQLI_CLOSE_IMPLICIT); +} + +ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor) +{ +	if (rsrc->ptr) { +		mysqli_plist_entry *plist = (mysqli_plist_entry *) rsrc->ptr; +		zend_hash_destroy(&plist->free_links); +		zend_hash_destroy(&plist->used_links); +		free(plist); +	} +} + + +int php_le_pmysqli(void) +{ +	return le_pmysqli; +} + +#ifndef HAVE_MYSQLND  /* {{{ php_free_stmt_bind_buffer */  void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)  { @@ -80,7 +143,7 @@ void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)  		if (bbuf.vars[i]) {  			zval_ptr_dtor(&bbuf.vars[i]); -		}	 +		}  	}  	if (bbuf.vars) { @@ -100,30 +163,44 @@ void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)  	}  	bbuf.var_cnt = 0; -	return;  }  /* }}} */ +#endif  /* {{{ php_clear_stmt_bind */ -void php_clear_stmt_bind(MY_STMT *stmt) +void php_clear_stmt_bind(MY_STMT *stmt TSRMLS_DC)  {  	if (stmt->stmt) { -		mysql_stmt_close(stmt->stmt); +		if (mysqli_stmt_close(stmt->stmt, TRUE)) { +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error occured while closing statement"); +			return; +		}  	} +	/* +	  mysqlnd keeps track of the binding and has freed its +	  structures in stmt_close() above +	*/ +#ifndef HAVE_MYSQLND +	/* Clean param bind */  	php_free_stmt_bind_buffer(stmt->param, FETCH_SIMPLE); +	/* Clean output bind */  	php_free_stmt_bind_buffer(stmt->result, FETCH_RESULT); +#endif  	if (stmt->query) {  		efree(stmt->query);  	}  	efree(stmt); -	return;  }  /* }}} */  /* {{{ php_clear_mysql */  void php_clear_mysql(MY_MYSQL *mysql) { +	if (mysql->hash_key) { +		efree(mysql->hash_key); +		mysql->hash_key = NULL; +	}  	if (mysql->li_read) {  		efree(Z_STRVAL_P(mysql->li_read));  		FREE_ZVAL(mysql->li_read); @@ -140,7 +217,7 @@ static void mysqli_objects_free_storage(void *object TSRMLS_DC)  	mysqli_object 	*intern = (mysqli_object *)zo;  	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr; -	my_efree(my_res);	 +	my_efree(my_res);  	zend_object_std_dtor(&intern->zo TSRMLS_CC);  	efree(intern);  } @@ -157,7 +234,9 @@ static void mysqli_link_free_storage(void *object TSRMLS_DC)  	if (my_res && my_res->ptr) {  		MY_MYSQL *mysql = (MY_MYSQL *)my_res->ptr;  		if (mysql->mysql) { -			mysql_close(mysql->mysql); +			if (!mysql->persistent) { +				mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT); +			}  		}  		php_clear_mysql(mysql);  		efree(mysql); @@ -166,6 +245,13 @@ static void mysqli_link_free_storage(void *object TSRMLS_DC)  }  /* }}} */ +/* {{{ mysql_driver_free_storage */ +static void mysqli_driver_free_storage(void *object TSRMLS_DC) +{ +	mysqli_objects_free_storage(object TSRMLS_CC); +} +/* }}} */ +  /* {{{ mysqli_stmt_free_storage   */  static void mysqli_stmt_free_storage(void *object TSRMLS_DC) @@ -176,7 +262,7 @@ static void mysqli_stmt_free_storage(void *object TSRMLS_DC)  	if (my_res && my_res->ptr) {  		MY_STMT *stmt = (MY_STMT *)my_res->ptr; -		php_clear_stmt_bind(stmt); +		php_clear_stmt_bind(stmt TSRMLS_CC);  	}  	mysqli_objects_free_storage(object TSRMLS_CC);  } @@ -243,7 +329,7 @@ zval *mysqli_read_property(zval *object, zval *member, int type TSRMLS_DC)  	ret = FAILURE;  	obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC); - 	if (member->type != IS_STRING) { +	if (member->type != IS_STRING) {  		tmp_member = *member;  		zval_copy_ctor(&tmp_member);  		convert_to_string(&tmp_member); @@ -256,7 +342,8 @@ zval *mysqli_read_property(zval *object, zval *member, int type TSRMLS_DC)  	if (ret == SUCCESS) {  		if (strcmp(obj->zo.ce->name, "mysqli_driver") && -            (!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED)) { +			(!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED)) +		{  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", obj->zo.ce->name );  			retval = EG(uninitialized_zval_ptr);  			return(retval); @@ -290,7 +377,7 @@ void mysqli_write_property(zval *object, zval *member, zval *value TSRMLS_DC)  	zend_object_handlers *std_hnd;  	int ret; - 	if (member->type != IS_STRING) { +	if (member->type != IS_STRING) {  		tmp_member = *member;  		zval_copy_ctor(&tmp_member);  		convert_to_string(&tmp_member); @@ -333,7 +420,6 @@ void mysqli_add_property(HashTable *h, char *pname, mysqli_read_t r_func, mysqli  static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)  { -	mysqli_object *obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC);  	zend_class_entry * ce = Z_OBJCE_P(object);  	if (ce != mysqli_link_class_entry && ce != mysqli_stmt_class_entry && @@ -342,6 +428,7 @@ static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)  		return zend_std_get_constructor(object TSRMLS_CC);  	} else {  		static zend_internal_function f; +		mysqli_object *obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC);  		f.function_name = obj->zo.ce->name;  		f.scope = obj->zo.ce; @@ -361,7 +448,7 @@ static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)  		} else if (obj->zo.ce == mysqli_warning_class_entry) {  			f.handler = ZEND_MN(mysqli_warning___construct);  		} -	 +  		return (union _zend_function*)&f;  	}  } @@ -382,8 +469,7 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_  	intern->prop_handler = NULL;  	mysqli_base_class = class_type; -	while (mysqli_base_class->type != ZEND_INTERNAL_CLASS && mysqli_base_class->parent != NULL) -	{ +	while (mysqli_base_class->type != ZEND_INTERNAL_CLASS && mysqli_base_class->parent != NULL) {  		mysqli_base_class = mysqli_base_class->parent;  	}  	zend_hash_find(&classes, mysqli_base_class->name, mysqli_base_class->name_length + 1,  @@ -396,6 +482,8 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_  	/* link object */  	if (instanceof_function(class_type, mysqli_link_class_entry TSRMLS_CC)) {  		free_storage = mysqli_link_free_storage; +	} else if (instanceof_function(class_type, mysqli_driver_class_entry TSRMLS_CC)) { /* driver object */ +		free_storage = mysqli_driver_free_storage;  	} else if (instanceof_function(class_type, mysqli_stmt_class_entry TSRMLS_CC)) { /* stmt object */  		free_storage = mysqli_stmt_free_storage;  	} else if (instanceof_function(class_type, mysqli_result_class_entry TSRMLS_CC)) { /* result object */ @@ -412,17 +500,21 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_  	return retval;  }  /* }}} */ -	 -/* {{{ mysqli_module_entry - */ + +  /* Dependancies */ -static const zend_module_dep mysqli_deps[] = { +const static zend_module_dep mysqli_deps[] = {  #if defined(HAVE_SPL) && ((PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 1))  	ZEND_MOD_REQUIRED("spl")  #endif +#if defined(HAVE_MYSQLND) +	ZEND_MOD_REQUIRED("mysqlnd") +#endif  	{NULL, NULL, NULL}  }; +/* {{{ mysqli_module_entry + */  zend_module_entry mysqli_module_entry = {  #if ZEND_MODULE_API_NO >= 20050922  	STANDARD_MODULE_HEADER_EX, NULL, @@ -454,22 +546,33 @@ ZEND_GET_MODULE(mysqli)  */  PHP_INI_BEGIN()  	STD_PHP_INI_ENTRY_EX("mysqli.max_links",			"-1",	PHP_INI_SYSTEM,		OnUpdateLong,		max_links,			zend_mysqli_globals,		mysqli_globals, display_link_numbers) +	STD_PHP_INI_ENTRY_EX("mysqli.max_persistent",		"-1",	PHP_INI_SYSTEM,		OnUpdateLong,		max_persistent,		zend_mysqli_globals,		mysqli_globals,	display_link_numbers) +	STD_PHP_INI_BOOLEAN("mysqli.allow_persistent",		"1",	PHP_INI_SYSTEM,		OnUpdateLong,		allow_persistent,	zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_ENTRY("mysqli.default_host",			NULL,	PHP_INI_ALL,		OnUpdateString,		default_host,		zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_ENTRY("mysqli.default_user",			NULL,	PHP_INI_ALL,		OnUpdateString,		default_user,		zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_ENTRY("mysqli.default_pw",				NULL,	PHP_INI_ALL,		OnUpdateString,		default_pw,			zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_ENTRY("mysqli.default_port",			"3306",	PHP_INI_ALL,		OnUpdateLong,		default_port,		zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_ENTRY("mysqli.default_socket",			NULL,	PHP_INI_ALL,		OnUpdateStringUnempty,	default_socket,	zend_mysqli_globals,		mysqli_globals)  	STD_PHP_INI_BOOLEAN("mysqli.reconnect",				"0",	PHP_INI_SYSTEM,		OnUpdateLong,		reconnect,			zend_mysqli_globals,		mysqli_globals) +	STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile",	"1",	PHP_INI_SYSTEM,		OnUpdateLong,		allow_local_infile,	zend_mysqli_globals,		mysqli_globals) +#ifdef HAVE_MYSQLND +	STD_PHP_INI_ENTRY("mysqli.cache_size",				"2000",	PHP_INI_SYSTEM,		OnUpdateLong,		cache_size,			zend_mysqli_globals,		mysqli_globals) +#endif  PHP_INI_END() -  /* }}} */ +  /* {{{ PHP_GINIT_FUNCTION   */  static PHP_GINIT_FUNCTION(mysqli)  {  	mysqli_globals->num_links = 0; -	mysqli_globals->max_links = 0; +	mysqli_globals->num_active_persistent = 0; +	mysqli_globals->num_inactive_persistent = 0; +	mysqli_globals->max_links = -1; +	mysqli_globals->max_links = -1; +	mysqli_globals->max_persistent = -1; +	mysqli_globals->allow_persistent = 1;  	mysqli_globals->default_port = 0;  	mysqli_globals->default_host = NULL;  	mysqli_globals->default_user = NULL; @@ -478,11 +581,16 @@ static PHP_GINIT_FUNCTION(mysqli)  	mysqli_globals->reconnect = 0;  	mysqli_globals->report_mode = 0;  	mysqli_globals->report_ht = 0; +	mysqli_globals->allow_local_infile = 1;  #ifdef HAVE_EMBEDDED_MYSQLI  	mysqli_globals->embedded = 1;  #else  	mysqli_globals->embedded = 0;  #endif +#ifdef HAVE_MYSQLND +	mysqli_globals->cache_size = 0; +	mysqli_globals->mysqlnd_thd_zval_cache = NULL; +#endif  }  /* }}} */ @@ -494,6 +602,16 @@ PHP_MINIT_FUNCTION(mysqli)  	zend_object_handlers *std_hnd = zend_get_std_object_handlers();  	REGISTER_INI_ENTRIES(); +#ifndef HAVE_MYSQLND +#if MYSQL_VERSION_ID >= 40000 +	if (mysql_server_init(0, NULL, NULL)) { +		return FAILURE; +	} +#endif +#else +	mysqli_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MyG(cache_size)); +	mysqli_mysqlnd_qcache = mysqlnd_qcache_init_cache(); +#endif  	memcpy(&mysqli_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));  	mysqli_object_handlers.clone_obj = NULL; @@ -504,6 +622,10 @@ PHP_MINIT_FUNCTION(mysqli)  	zend_hash_init(&classes, 0, NULL, NULL, 1); +	/* persistent connections */ +	le_pmysqli = zend_register_list_destructors_ex(NULL, php_mysqli_dtor, +		"MySqli persistent connection", module_number); +  	INIT_CLASS_ENTRY(cex, "mysqli_sql_exception", mysqli_exception_methods);  #ifdef HAVE_SPL  	mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException, NULL TSRMLS_CC); @@ -552,6 +674,13 @@ PHP_MINIT_FUNCTION(mysqli)  	REGISTER_LONG_CONSTANT("MYSQLI_OPT_CONNECT_TIMEOUT", MYSQL_OPT_CONNECT_TIMEOUT, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_OPT_LOCAL_INFILE", MYSQL_OPT_LOCAL_INFILE, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_INIT_COMMAND", MYSQL_INIT_COMMAND, CONST_CS | CONST_PERSISTENT); +#if defined(HAVE_MYSQLND) +	REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_CMD_BUFFER_SIZE", MYSQLND_OPT_NET_CMD_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT); +	REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_READ_BUFFER_SIZE", MYSQLND_OPT_NET_READ_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +	REGISTER_LONG_CONSTANT("MYSQLI_OPT_INT_AND_YEAR_AS_INT", MYSQLND_OPT_INT_AND_YEAR_AS_INT, CONST_CS | CONST_PERSISTENT); +#endif  	/* mysqli_real_connect flags */	  	REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_SSL", CLIENT_SSL, CONST_CS | CONST_PERSISTENT); @@ -573,7 +702,7 @@ PHP_MINIT_FUNCTION(mysqli)  	/* for mysqli_stmt_set_attr */  	REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH", STMT_ATTR_UPDATE_MAX_LENGTH, CONST_CS | CONST_PERSISTENT); -#if MYSQL_VERSION_ID > 50003 +#if MYSQL_VERSION_ID > 50003 || defined(HAVE_MYSQLND)  	REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_CURSOR_TYPE", STMT_ATTR_CURSOR_TYPE, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_NO_CURSOR", CURSOR_TYPE_NO_CURSOR, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_READ_ONLY", CURSOR_TYPE_READ_ONLY, CONST_CS | CONST_PERSISTENT); @@ -581,7 +710,7 @@ PHP_MINIT_FUNCTION(mysqli)  	REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_SCROLLABLE", CURSOR_TYPE_SCROLLABLE, CONST_CS | CONST_PERSISTENT);  #endif -#if MYSQL_VERSION_ID > 50007 +#if MYSQL_VERSION_ID > 50007 || defined(HAVE_MYSQLND)  	REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_PREFETCH_ROWS", STMT_ATTR_PREFETCH_ROWS, CONST_CS | CONST_PERSISTENT);  #endif @@ -627,17 +756,19 @@ PHP_MINIT_FUNCTION(mysqli)  	REGISTER_LONG_CONSTANT("MYSQLI_TYPE_INTERVAL", FIELD_TYPE_INTERVAL, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_TYPE_GEOMETRY", FIELD_TYPE_GEOMETRY, CONST_CS | CONST_PERSISTENT); -#if MYSQL_VERSION_ID > 50002 +#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)  	REGISTER_LONG_CONSTANT("MYSQLI_TYPE_NEWDECIMAL", FIELD_TYPE_NEWDECIMAL, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_TYPE_BIT", FIELD_TYPE_BIT, CONST_CS | CONST_PERSISTENT);  #endif - +	REGISTER_LONG_CONSTANT("MYSQLI_SET_CHARSET_NAME", MYSQL_SET_CHARSET_NAME, CONST_CS | CONST_PERSISTENT);  	/* replication */ +#if !defined(HAVE_MYSQLND)  	REGISTER_LONG_CONSTANT("MYSQLI_RPL_MASTER", MYSQL_RPL_MASTER, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_RPL_SLAVE", MYSQL_RPL_SLAVE, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_RPL_ADMIN", MYSQL_RPL_ADMIN, CONST_CS | CONST_PERSISTENT); +#endif  	/* bind support */  	REGISTER_LONG_CONSTANT("MYSQLI_NO_DATA", MYSQL_NO_DATA, CONST_CS | CONST_PERSISTENT); @@ -652,10 +783,6 @@ PHP_MINIT_FUNCTION(mysqli)  	REGISTER_LONG_CONSTANT("MYSQLI_REPORT_ALL", MYSQLI_REPORT_ALL, CONST_CS | CONST_PERSISTENT);  	REGISTER_LONG_CONSTANT("MYSQLI_REPORT_OFF", 0, CONST_CS | CONST_PERSISTENT); -	if (mysql_server_init(0, NULL, NULL)) { -		return FAILURE; -	} -  	return SUCCESS;  }  /* }}} */ @@ -664,15 +791,25 @@ PHP_MINIT_FUNCTION(mysqli)   */  PHP_MSHUTDOWN_FUNCTION(mysqli)  { +#ifndef HAVE_MYSQLND +#if MYSQL_VERSION_ID >= 40000  #ifdef PHP_WIN32  	unsigned long client_ver = mysql_get_client_version(); -	/* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */ -	if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) { +	/* +	  Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows. +	  PHP bug#41350 MySQL bug#25621 +	*/ +	if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) {  		mysql_server_end();  	}  #else  	mysql_server_end();  #endif +#endif +#else +	mysqlnd_palloc_free_cache(mysqli_mysqlnd_zval_cache); +	mysqlnd_qcache_free_cache_reference(&mysqli_mysqlnd_qcache); +#endif  	zend_hash_destroy(&mysqli_driver_properties);  	zend_hash_destroy(&mysqli_result_properties); @@ -690,13 +827,16 @@ PHP_MSHUTDOWN_FUNCTION(mysqli)   */  PHP_RINIT_FUNCTION(mysqli)  { -#ifdef ZTS +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000  	if (mysql_thread_init()) {  		return FAILURE;  	}  #endif  	MyG(error_msg) = NULL;  	MyG(error_no) = 0; +#ifdef HAVE_MYSQLND +	MyG(mysqlnd_thd_zval_cache) = mysqlnd_palloc_rinit(mysqli_mysqlnd_zval_cache); +#endif  	return SUCCESS;  } @@ -706,27 +846,55 @@ PHP_RINIT_FUNCTION(mysqli)   */  PHP_RSHUTDOWN_FUNCTION(mysqli)  { -#ifdef ZTS +	/* check persistent connections, move used to free */ +	zend_hash_apply(&EG(persistent_list), (apply_func_t) php_mysqli_persistent_on_rshut TSRMLS_CC); + +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000  	mysql_thread_end();  #endif  	if (MyG(error_msg)) {  		efree(MyG(error_msg));  	} +#ifdef HAVE_MYSQLND +	mysqlnd_palloc_rshutdown(MyG(mysqlnd_thd_zval_cache)); +#endif  	return SUCCESS;  }  /* }}} */ +  /* {{{ PHP_MINFO_FUNCTION   */  PHP_MINFO_FUNCTION(mysqli)  { +	char buf[32]; +  	php_info_print_table_start();  	php_info_print_table_header(2, "MysqlI Support", "enabled");  	php_info_print_table_row(2, "Client API library version", mysql_get_client_info()); +	snprintf(buf, sizeof(buf), "%ld", MyG(num_active_persistent)); +	php_info_print_table_row(2, "Active Persistent Links", buf); +	snprintf(buf, sizeof(buf), "%ld", MyG(num_inactive_persistent)); +	php_info_print_table_row(2, "Inactive Persistent Links", buf); +	snprintf(buf, sizeof(buf), "%ld", MyG(num_links)); +	php_info_print_table_row(2, "Active Links", buf); +#if !defined(HAVE_MYSQLND)  	php_info_print_table_row(2, "Client API header version", MYSQL_SERVER_VERSION);  	php_info_print_table_row(2, "MYSQLI_SOCKET", MYSQL_UNIX_ADDR); -	 -	 +#else +	{ +		zval values; + +		php_info_print_table_header(2, "Persistent cache", mysqli_mysqlnd_zval_cache? "enabled":"disabled"); +		 +		if (mysqli_mysqlnd_zval_cache) { +			/* Now report cache status */ +			mysqlnd_palloc_stats(mysqli_mysqlnd_zval_cache, &values); +			mysqlnd_minfo_print_hash(&values); +			zval_dtor(&values); +		} +	} +#endif  	php_info_print_table_end();  	DISPLAY_INI_ENTRIES(); @@ -742,16 +910,16 @@ Parameters:  ZEND_FUNCTION(mysqli_stmt_construct)  {  	MY_MYSQL			*mysql; -	zval  				*mysql_link; +	zval				*mysql_link;  	MY_STMT				*stmt; -	MYSQLI_RESOURCE 	*mysqli_resource; +	MYSQLI_RESOURCE		*mysqli_resource;  	char				*statement; -	int					stmt_len; +	int					statement_len;  	switch (ZEND_NUM_ARGS())  	{  		case 1:  /* mysql_stmt_init */ -	        if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) { +			if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {  				return;  			}  			MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); @@ -761,15 +929,15 @@ ZEND_FUNCTION(mysqli_stmt_construct)  			stmt->stmt = mysql_stmt_init(mysql->mysql);  		break;  		case 2: -	        if (zend_parse_parameters(2 TSRMLS_CC, "Os", &mysql_link, mysqli_link_class_entry, &statement, &stmt_len)==FAILURE) { +			if (zend_parse_parameters(2 TSRMLS_CC, "Os", &mysql_link, mysqli_link_class_entry, &statement, &statement_len)==FAILURE) {  				return;  			}  			MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);  			stmt = (MY_STMT *)ecalloc(1,sizeof(MY_STMT)); -	 +  			if ((stmt->stmt = mysql_stmt_init(mysql->mysql))) { -				mysql_stmt_prepare(stmt->stmt, statement, stmt_len); +				mysql_stmt_prepare(stmt->stmt, statement, statement_len);  			}  		break;  		default: @@ -800,20 +968,24 @@ ZEND_FUNCTION(mysqli_result_construct)  	MY_MYSQL			*mysql;  	MYSQL_RES			*result;  	zval				*mysql_link; -	MYSQLI_RESOURCE 	*mysqli_resource; +	MYSQLI_RESOURCE		*mysqli_resource;  	long				resmode = MYSQLI_STORE_RESULT;  	switch (ZEND_NUM_ARGS()) {  		case 1: -	        if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) { +			if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {  				return;  			} -		break; +			break;  		case 2: -	        if (zend_parse_parameters(2 TSRMLS_CC, "Ol", &mysql_link, mysqli_link_class_entry, &resmode)==FAILURE) { +			if (zend_parse_parameters(2 TSRMLS_CC, "Ol", &mysql_link, mysqli_link_class_entry, &resmode)==FAILURE) {  				return;  			} -		break; +			if (resmode != MYSQLI_USE_RESULT && resmode != MYSQLI_STORE_RESULT) { +				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); +				RETURN_FALSE; +			} +			break;  		default:  			WRONG_PARAM_COUNT;  	} @@ -830,7 +1002,7 @@ ZEND_FUNCTION(mysqli_result_construct)  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = (void *)result;  	mysqli_resource->status = MYSQLI_STATUS_VALID; -	 +  	((mysqli_object *) zend_object_store_get_object(getThis() TSRMLS_CC))->ptr = mysqli_resource;  } @@ -843,12 +1015,14 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags  	MYSQL_RES		*result;  	zval			*mysql_result;  	long			fetchtype; +	zval			*ctor_params = NULL; +	zend_class_entry *ce = NULL; +#if !defined(HAVE_MYSQLND)  	unsigned int	i;  	MYSQL_FIELD		*fields;  	MYSQL_ROW		row;  	unsigned long	*field_len; -	zval            *ctor_params = NULL; -	zend_class_entry *ce = NULL; +#endif  	if (into_object) {  		char *class_name; @@ -882,11 +1056,12 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags  	}  	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);  -	if ((fetchtype & MYSQLI_BOTH) == 0) { +	if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH");  		RETURN_FALSE;  	} +#if !defined(HAVE_MYSQLND)  	if (!(row = mysql_fetch_row(result))) {  		RETURN_NULL();  	} @@ -930,16 +1105,19 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags  			}  		}  	} +#else +	mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQLI); +#endif -	if (into_object) { +	if (into_object && Z_TYPE_P(return_value) != IS_NULL) {  		zval dataset = *return_value;  		zend_fcall_info fci;  		zend_fcall_info_cache fcc;  		zval *retval_ptr;  -	 +  		object_and_properties_init(return_value, ce, NULL);  		zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC); -	 +  		if (ce->constructor) {  			fci.size = sizeof(fci);  			fci.function_table = &ce->function_table; @@ -951,7 +1129,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags  				if (Z_TYPE_P(ctor_params) == IS_ARRAY) {  					HashTable *ht = Z_ARRVAL_P(ctor_params);  					Bucket *p; -	 +  					fci.param_count = 0;  					fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);  					p = ht->pListHead; @@ -979,7 +1157,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags  			fcc.function_handler = ce->constructor;  			fcc.calling_scope = EG(scope);  			fcc.object_pp = &return_value; -		 +  			if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {  				zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);  			} else { @@ -1009,6 +1187,8 @@ PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRML  }  /* }}} */ +#if !defined(HAVE_MYSQLND) +  #define ALLOC_CALLBACK_ARGS(a, b, c)\  if (c) {\  	a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ @@ -1029,7 +1209,7 @@ if (a) {\  #define LOCAL_INFILE_ERROR_MSG(source,dest)\  memset(source, 0, LOCAL_INFILE_ERROR_LEN);\ -memcpy(source, dest, LOCAL_INFILE_ERROR_LEN-1); +memcpy(source, dest, MIN(strlen(dest), LOCAL_INFILE_ERROR_LEN-1));  /* {{{ void php_set_local_infile_handler_default   */ @@ -1037,7 +1217,10 @@ void php_set_local_infile_handler_default(MY_MYSQL *mysql) {  	/* register internal callback functions */  	mysql_set_local_infile_handler(mysql->mysql, &php_local_infile_init, &php_local_infile_read,  				&php_local_infile_end, &php_local_infile_error, (void *)mysql); -	mysql->li_read = NULL; +	if (mysql->li_read) { +		zval_ptr_dtor(&mysql->li_read); +		mysql->li_read = NULL; +	}  }  /* }}} */ @@ -1047,7 +1230,7 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)  {  	mysqli_local_infile			*data;  	MY_MYSQL 					*mysql; -	php_stream_context 			*context = NULL; +	php_stream_context			*context = NULL;  	TSRMLS_FETCH(); @@ -1086,7 +1269,7 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)  int php_local_infile_read(void *ptr, char *buf, uint buf_len)  {  	mysqli_local_infile 		*data; -	MY_MYSQL 					*mysql; +	MY_MYSQL					*mysql;  	zval						***callback_args;  	zval						*retval;  	zval						*fp; @@ -1101,9 +1284,7 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)  	/* default processing */  	if (!mysql->li_read) { -		int			count; - -		count = (int)php_stream_read(mysql->li_stream, buf, buf_len); +		int count = (int)php_stream_read(mysql->li_stream, buf, buf_len);  		if (count < 0) {  			LOCAL_INFILE_ERROR_MSG(data->error_msg, ER(2)); @@ -1113,21 +1294,21 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)  	}  	ALLOC_CALLBACK_ARGS(callback_args, 1, argc); -	 +  	/* set parameters: filepointer, buffer, buffer_len, errormsg */  	MAKE_STD_ZVAL(fp);  	php_stream_to_zval(mysql->li_stream, fp);  	callback_args[0] = &fp; -	ZVAL_STRING(*callback_args[1], "", 1);	 -	ZVAL_LONG(*callback_args[2], buf_len);	 -	ZVAL_STRING(*callback_args[3], "", 1);	 +	ZVAL_STRING(*callback_args[1], "", 1); +	ZVAL_LONG(*callback_args[2], buf_len); +	ZVAL_STRING(*callback_args[3], "", 1);  	if (call_user_function_ex(EG(function_table),   						NULL,  						mysql->li_read,  						&retval, -						argc,	 	 +						argc,  						callback_args,  						0,  						NULL TSRMLS_CC) == SUCCESS) { @@ -1136,22 +1317,36 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)  		zval_ptr_dtor(&retval);  		if (rc > 0) { -			if (rc > buf_len) { +			if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) { +				LOCAL_INFILE_ERROR_MSG(data->error_msg, +							"Mismatch between the return value of the callback and the content " +							"length of the buffer."); +				rc = -1; +			} else if (rc > buf_len) {  				/* check buffer overflow */ -				LOCAL_INFILE_ERROR_MSG(data->error_msg, "Read buffer too large"); +				LOCAL_INFILE_ERROR_MSG(data->error_msg, "Too much data returned");  				rc = -1;  			} else { -				memcpy(buf, Z_STRVAL_P(*callback_args[1]), rc); +				memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1])));  			} -		} -		if (rc < 0) { +		} else if (rc < 0) {  			LOCAL_INFILE_ERROR_MSG(data->error_msg, Z_STRVAL_P(*callback_args[3]));  		}  	} else {  		LOCAL_INFILE_ERROR_MSG(data->error_msg, "Can't execute load data local init callback function");  		rc = -1;  	} -	 +	/* +	  If the (ab)user has closed the file handle we should +	  not try to use it anymore or even close it +	*/ +	if (!zend_rsrc_list_get_rsrc_type(Z_LVAL_P(fp) TSRMLS_CC)) { +		LOCAL_INFILE_ERROR_MSG(data->error_msg, "File handle closed"); +		rc = -1; +		/* Thus the end handler won't try to free already freed memory */ +		mysql->li_stream = NULL; +	} +  	FREE_CALLBACK_ARGS(callback_args, 1, argc);  	efree(fp);  	return rc; @@ -1167,7 +1362,7 @@ int php_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)  	if (data) {  		strlcpy(error_msg, data->error_msg, error_msg_len);  		return 2000; -	}  +	}  	strlcpy(error_msg, ER(CR_OUT_OF_MEMORY), error_msg_len);  	return CR_OUT_OF_MEMORY;  } @@ -1175,10 +1370,10 @@ int php_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)  /* {{{ php_local_infile_end   */ -void php_local_infile_end(void *ptr)  +void php_local_infile_end(void *ptr)  { -	mysqli_local_infile			*data; -	MY_MYSQL 					*mysql; +	mysqli_local_infile		*data; +	MY_MYSQL				*mysql;  	TSRMLS_FETCH(); @@ -1193,9 +1388,10 @@ void php_local_infile_end(void *ptr)  	php_stream_close(mysql->li_stream);  	free(data); -	return;	 +	return;  }  /* }}} */ +#endif  /*   * Local variables: diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index fd01ba9ffa..5e55be7d51 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -12,7 +12,9 @@    | obtain it through the world-wide-web, please send a note to          |    | license@php.net so we can mail you a copy immediately.               |    +----------------------------------------------------------------------+ -  | Author: Georg Richter <georg@php.net>                                | +  | Authors: Georg Richter <georg@php.net>                               | +  |          Andrey Hristov <andrey@php.net>                             | +  |          Ulf Wendel <uw@php.net>                                     |    +----------------------------------------------------------------------+    $Id$  @@ -26,8 +28,9 @@  #include "php.h"  #include "php_ini.h" +#include "php_globals.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  /* {{{ proto mixed mysqli_affected_rows(object link)     Get number of affected rows in previous MySQL operation */ @@ -51,16 +54,17 @@ PHP_FUNCTION(mysqli_affected_rows)  }  /* }}} */ +  /* {{{ proto bool mysqli_autocommit(object link, bool mode)     Turn auto commit on or of */  PHP_FUNCTION(mysqli_autocommit)  { -	MY_MYSQL 		*mysql; -	zval  			*mysql_link; -	zend_bool		automode; +	MY_MYSQL	*mysql; +	zval		*mysql_link; +	zend_bool	automode;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ob", &mysql_link, mysqli_link_class_entry, &automode) == FAILURE) { -		return;	 +		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); @@ -71,83 +75,38 @@ PHP_FUNCTION(mysqli_autocommit)  }  /* }}} */ -/* {{{ proto bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....]) -   Bind variables to a prepared statement as parameters */ -PHP_FUNCTION(mysqli_stmt_bind_param) +/* {{{ mysqli_stmt_bind_param_do_bind */ +#ifndef HAVE_MYSQLND +static +int mysqli_stmt_bind_param_do_bind(MY_STMT *stmt, unsigned int argc, unsigned int num_vars, +								   zval ***args, unsigned int start, const char * const types TSRMLS_DC)  { -	zval 			***args; -	int     		argc = ZEND_NUM_ARGS(); -	int 	    	i; -	int				num_vars; -	int				start = 2; -	int				ofs; -	MY_STMT			*stmt; -	zval 			*mysql_stmt; +	int				i, ofs;  	MYSQL_BIND		*bind; -	char			*types; -	int				typelen;  	unsigned long	rc; -	/* calculate and check number of parameters */ -	if (argc < 2) { -		/* there has to be at least one pair */ -		WRONG_PARAM_COUNT; -	} - -	if (zend_parse_method_parameters((getThis()) ? 1:2 TSRMLS_CC, getThis(), "Os", &mysql_stmt, mysqli_stmt_class_entry, &types, &typelen) == FAILURE) { -		return;	 -	} - -	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);  - -	num_vars = argc - 1; -	if (getThis()) { -		start = 1; -	} else { -		/* ignore handle parameter in procedural interface*/ -		--num_vars; -	} - -	if (typelen != argc - start) { -		/* number of bind variables doesn't match number of elements in type definition string */ -		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements in type definition string doesn't match number of bind variables"); -		RETURN_FALSE; -	} - -	if (typelen != stmt->stmt->param_count) { -		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of variables doesn't match number of parameters in prepared statement"); -		RETURN_FALSE; -	} -  	/* prevent leak if variables are already bound */  	if (stmt->param.var_cnt) {  		php_free_stmt_bind_buffer(stmt->param, FETCH_SIMPLE);  	} -	args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0); - -	if (zend_get_parameters_array_ex(argc, args) == FAILURE) { -		efree(args); -		WRONG_PARAM_COUNT; -	} -  	stmt->param.is_null = ecalloc(num_vars, sizeof(char)); -	bind = (MYSQL_BIND *)ecalloc(num_vars, sizeof(MYSQL_BIND)); +	bind = (MYSQL_BIND *) ecalloc(num_vars, sizeof(MYSQL_BIND));  	ofs = 0; -	for (i=start; i < argc; i++) { +	for (i = start; i < argc; i++) {  		/* set specified type */  		switch (types[ofs]) {  			case 'd': /* Double */  				bind[ofs].buffer_type = MYSQL_TYPE_DOUBLE; -				bind[ofs].buffer = (char*)&Z_DVAL_PP(args[i]); +				bind[ofs].buffer = &Z_DVAL_PP(args[i]);  				bind[ofs].is_null = &stmt->param.is_null[ofs];  				break;  			case 'i': /* Integer */ -				bind[ofs].buffer_type = MYSQL_TYPE_LONG; -				bind[ofs].buffer = (char*)&Z_LVAL_PP(args[i]); +				bind[ofs].buffer_type = (sizeof(long) > 4) ? MYSQL_TYPE_LONGLONG : MYSQL_TYPE_LONG; +				bind[ofs].buffer = &Z_LVAL_PP(args[i]);  				bind[ofs].is_null = &stmt->param.is_null[ofs];  				break; @@ -164,85 +123,162 @@ PHP_FUNCTION(mysqli_stmt_bind_param)  			default:  				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Undefined fieldtype %c (parameter %d)", types[ofs], i+1); -				RETVAL_FALSE; -				goto end; +				rc = 1; +				goto end_1;  		}  		ofs++;  	}  	rc = mysql_stmt_bind_param(stmt->stmt, bind); -	MYSQLI_REPORT_STMT_ERROR(stmt->stmt); +end_1:  	if (rc) { -		RETVAL_FALSE; -		goto end; +		efree(stmt->param.is_null); +	} else { +		stmt->param.var_cnt = num_vars; +		stmt->param.vars = (zval **)safe_emalloc(num_vars, sizeof(zval), 0); +		for (i = 0; i < num_vars; i++) { +			if (bind[i].buffer_type  != MYSQL_TYPE_LONG_BLOB) { +				ZVAL_ADDREF(*args[i+start]); +				stmt->param.vars[i] = *args[i+start]; +			} else { +				stmt->param.vars[i] = NULL; +			} +		}  	} +	efree(bind); -	stmt->param.var_cnt = num_vars; -	stmt->param.vars = (zval **)safe_emalloc(num_vars, sizeof(zval), 0); -	for (i = 0; i < num_vars; i++) { -		if (bind[i].buffer_type  != MYSQL_TYPE_LONG_BLOB) { -			ZVAL_ADDREF(*args[i+start]); -			stmt->param.vars[i] = *args[i+start]; -		} else { -			stmt->param.vars[i] = NULL; +	return rc; +} +#else +static +int mysqli_stmt_bind_param_do_bind(MY_STMT *stmt, unsigned int argc, unsigned int num_vars, +								   zval ***args, unsigned int start, const char * const types TSRMLS_DC) +{ +	int					i; +	MYSQLND_PARAM_BIND	*params; +	enum_func_status	ret = FAIL; + +	/* If no params -> skip binding and return directly */ +	if (argc == start) { +		return PASS; +	} +	params = emalloc((argc - start) * sizeof(MYSQLND_PARAM_BIND)); +	for (i = 0; i < (argc - start); i++) { +		zend_uchar type; +		switch (types[i]) { +			case 'd': /* Double */ +				type = MYSQL_TYPE_DOUBLE; +				break; +			case 'i': /* Integer */ +#if SIZEOF_LONG==8 +				type = MYSQL_TYPE_LONGLONG; +#elif SIZEOF_LONG==4 +				type = MYSQL_TYPE_LONG; +#endif +				break; +			case 'b': /* Blob (send data) */ +				type = MYSQL_TYPE_LONG_BLOB; +				break; +			case 's': /* string */ +				type = MYSQL_TYPE_VAR_STRING; +				break; +			default: +				/* We count parameters from 1 */ +				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Undefined fieldtype %c (parameter %d)", types[i], i + start + 1); +				ret = FAIL; +				efree(params); +				goto end;  		} +		params[i].zv = *(args[i + start]); +		params[i].type = type;  	} -	RETVAL_TRUE; +	ret = mysqlnd_stmt_bind_param(stmt->stmt, params); +  end: -	efree(args); -	efree(bind); +	return ret;  } +#endif  /* }}} */ -/* {{{ proto bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...]) -   Bind variables to a prepared statement for result storage */ - -/* TODO: -   do_alloca, free_alloca -*/ - -PHP_FUNCTION(mysqli_stmt_bind_result) +/* {{{ proto bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....]) U +   Bind variables to a prepared statement as parameters */ +PHP_FUNCTION(mysqli_stmt_bind_param)  { -	zval 		***args; -	int     	argc = ZEND_NUM_ARGS(); -	int     	i; -	int			start = 1; -	int			var_cnt; -	int			ofs; -	long		col_type; -	ulong		rc; -	MY_STMT 	*stmt; -	zval 		*mysql_stmt; -	MYSQL_BIND 	*bind; +	zval			***args; +	int				argc = ZEND_NUM_ARGS(); +	int				num_vars; +	int				start = 2; +	MY_STMT			*stmt; +	zval			*mysql_stmt; +	char			*types; +	int				types_len; +	unsigned long	rc; -	if (getThis()) { -		start = 0; +	/* calculate and check number of parameters */ +	if (argc < 2) { +		/* there has to be at least one pair */ +		WRONG_PARAM_COUNT;  	} -	if (zend_parse_method_parameters((getThis()) ? 0:1 TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { +	if (zend_parse_method_parameters((getThis()) ? 1:2 TSRMLS_CC, getThis(), "Os", &mysql_stmt, mysqli_stmt_class_entry, +									&types, &types_len) == FAILURE) {  		return;	  	} -	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);  +	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); -	if (argc < (getThis() ? 1 : 2))  { -		WRONG_PARAM_COUNT; +	num_vars = argc - 1; +	if (getThis()) { +		start = 1; +	} else { +		/* ignore handle parameter in procedural interface*/ +		--num_vars; +	} +	if (!types_len) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type or no types specified"); +		RETURN_FALSE; +	} + +	if (types_len != argc - start) { +		/* number of bind variables doesn't match number of elements in type definition string */ +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements in type definition string doesn't match number of bind variables"); +		RETURN_FALSE; +	} + +	if (types_len != mysql_stmt_param_count(stmt->stmt)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of variables doesn't match number of parameters in prepared statement"); +		RETURN_FALSE;  	}  	args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);  	if (zend_get_parameters_array_ex(argc, args) == FAILURE) { -		efree(args); -		WRONG_PARAM_COUNT; +		zend_wrong_param_count(TSRMLS_C);  +		rc = 1; +	} else { +		rc = mysqli_stmt_bind_param_do_bind(stmt, argc, num_vars, args, start, types TSRMLS_CC); +		MYSQLI_REPORT_STMT_ERROR(stmt->stmt);  	} -	var_cnt = argc - start; +	efree(args); -	if (var_cnt != mysql_stmt_field_count(stmt->stmt)) { -		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of bind variables doesn't match number of fields in prepared statement"); -		efree(args); -		RETURN_FALSE; -	} +	RETURN_BOOL(!rc); +} +/* }}} */ + +/* {{{ mysqli_stmt_bind_result_do_bind */ +#ifndef HAVE_MYSQLND +/* TODO: +   do_alloca, free_alloca +*/ +static int +mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc, unsigned int start TSRMLS_DC) +{ +	MYSQL_BIND	*bind; +	int			i, ofs; +	int			var_cnt = argc - start; +	long		col_type; +	ulong		rc;  	/* prevent leak if variables are already bound */  	if (stmt->result.var_cnt) { @@ -268,7 +304,7 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  				convert_to_double_ex(args[i]);  				stmt->result.buf[ofs].type = IS_DOUBLE;  				stmt->result.buf[ofs].buflen = sizeof(double); -				 +  				/* allocate buffer for double */  				stmt->result.buf[ofs].val = (char *)emalloc(sizeof(double));  				bind[ofs].buffer_type = MYSQL_TYPE_DOUBLE; @@ -305,7 +341,7 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  				break;  			case MYSQL_TYPE_LONGLONG: -#if MYSQL_VERSION_ID > 50002 +#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)  			case MYSQL_TYPE_BIT:  #endif  				stmt->result.buf[ofs].type = IS_STRING;  @@ -324,8 +360,8 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  			case MYSQL_TYPE_NEWDATE:  			case MYSQL_TYPE_VAR_STRING:  			case MYSQL_TYPE_STRING: -			case MYSQL_TYPE_BLOB:  			case MYSQL_TYPE_TINY_BLOB: +			case MYSQL_TYPE_BLOB:  			case MYSQL_TYPE_MEDIUM_BLOB:  			case MYSQL_TYPE_LONG_BLOB:  			case MYSQL_TYPE_TIMESTAMP: @@ -385,7 +421,6 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  		}  		/* Don't free stmt->result.is_null because is_null & buf are one block of memory  */  		efree(stmt->result.buf); -		RETVAL_FALSE;  	} else {  		stmt->result.var_cnt = var_cnt;  		stmt->result.vars = (zval **)safe_emalloc((var_cnt), sizeof(zval), 0); @@ -394,10 +429,69 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  			ZVAL_ADDREF(*args[i]);  			stmt->result.vars[ofs] = *args[i];  		} -		RETVAL_TRUE;  	} -	efree(args);  	efree(bind); + +	return rc; +} +#else +static int +mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc, unsigned int start TSRMLS_DC) +{ +	unsigned int i; +	MYSQLND_RESULT_BIND *params; + +	params = emalloc((argc - start) * sizeof(MYSQLND_RESULT_BIND)); +	for (i = 0; i < (argc - start); i++) { +		params[i].zv = *(args[i + start]); +	} +	return mysqlnd_stmt_bind_result(stmt->stmt, params); +} +#endif +/* }}} */ + +/* {{{ proto bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...]) U +   Bind variables to a prepared statement for result storage */ +PHP_FUNCTION(mysqli_stmt_bind_result) +{ +	zval		***args; +	int			argc = ZEND_NUM_ARGS(); +	int			start = 1; +	ulong		rc; +	MY_STMT		*stmt; +	zval		*mysql_stmt; + +	if (getThis()) { +		start = 0; +	} + +	if (zend_parse_method_parameters((getThis()) ? 0:1 TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { +		return;	 +	} + +	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); + +	if (argc < (getThis() ? 1 : 2)) { +		WRONG_PARAM_COUNT; +	} + +	if ((argc - start) != mysql_stmt_field_count(stmt->stmt)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of bind variables doesn't match number of fields in prepared statement"); +		RETURN_FALSE; +	} + +	args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0); + +	if (zend_get_parameters_array_ex(argc, args) == FAILURE) { +		efree(args); +		WRONG_PARAM_COUNT; +	} + +	rc = mysqli_stmt_bind_result_do_bind(stmt, args, argc, start TSRMLS_CC); + +	efree(args); + +	RETURN_BOOL(!rc);  }  /* }}} */ @@ -406,9 +500,9 @@ PHP_FUNCTION(mysqli_stmt_bind_result)  PHP_FUNCTION(mysqli_change_user)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link = NULL; -	char  		*user, *password, *dbname; -	int   		user_len, password_len, dbname_len; +	zval		*mysql_link = NULL; +	char		*user, *password, *dbname; +	int			user_len, password_len, dbname_len;  	ulong		rc;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss", &mysql_link, mysqli_link_class_entry, &user, &user_len, &password, &password_len, &dbname, &dbname_len) == FAILURE) { @@ -431,8 +525,8 @@ PHP_FUNCTION(mysqli_change_user)     Returns the name of the character set used for this connection */  PHP_FUNCTION(mysqli_character_set_name)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -440,7 +534,7 @@ PHP_FUNCTION(mysqli_character_set_name)  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	RETURN_STRING((char *) mysql_character_set_name(mysql->mysql), 1); +	RETURN_STRING((char *)mysql_character_set_name(mysql->mysql), 1);  }  /* }}} */ @@ -448,8 +542,8 @@ PHP_FUNCTION(mysqli_character_set_name)     Close connection */  PHP_FUNCTION(mysqli_close)  { -	zval 		*mysql_link; -	MY_MYSQL 	*mysql; +	zval		*mysql_link; +	MY_MYSQL	*mysql;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -457,11 +551,32 @@ PHP_FUNCTION(mysqli_close)  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED); -	mysql_close(mysql->mysql); -	mysql->mysql = NULL; +	if (!mysql->persistent) { +		mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT); +		mysql->mysql = NULL; +	} else { +		zend_rsrc_list_entry *le; +		if (zend_hash_find(&EG(persistent_list), mysql->hash_key, strlen(mysql->hash_key) + 1, (void **)&le) == SUCCESS) { +			if (Z_TYPE_P(le) == php_le_pmysqli()) { +				mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr; +				dtor_func_t pDestructor = plist->used_links.pDestructor; + +				plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */ +				zend_hash_index_del(&plist->used_links, mysql->hash_index); +				plist->used_links.pDestructor = pDestructor; /* Restore the destructor */ + +				zend_hash_next_index_insert(&plist->free_links, &mysql->mysql, sizeof(MYSQL *), NULL); +				MyG(num_links)--; +				MyG(num_active_persistent)--; +				MyG(num_inactive_persistent)++; +			} +		} +	} +  	php_clear_mysql(mysql); + +	MYSQLI_CLEAR_RESOURCE(&mysql_link);  	efree(mysql); -	MYSQLI_CLEAR_RESOURCE(&mysql_link);	  	RETURN_TRUE;  }  /* }}} */ @@ -470,8 +585,8 @@ PHP_FUNCTION(mysqli_close)     Commit outstanding actions and close transaction */  PHP_FUNCTION(mysqli_commit)  { -	MY_MYSQL 	*mysql; -	zval 		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -489,8 +604,8 @@ PHP_FUNCTION(mysqli_commit)  PHP_FUNCTION(mysqli_data_seek)  {  	MYSQL_RES	*result; -	zval  		*mysql_result; -	long  		offset; +	zval		*mysql_result; +	long		offset;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_result, mysqli_result_class_entry, &offset) == FAILURE) {  		return; @@ -498,13 +613,13 @@ PHP_FUNCTION(mysqli_data_seek)  	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); -	if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) { +	if (mysqli_result_is_unbuffered(result)) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function cannot be used with MYSQL_USE_RESULT");  		RETURN_FALSE;  	} -	if (offset < 0 || offset >= result->row_count) { -	   RETURN_FALSE; +	if (offset < 0 || offset >= mysql_num_rows(result)) { +		RETURN_FALSE;  	}  	mysql_data_seek(result, offset); @@ -512,12 +627,12 @@ PHP_FUNCTION(mysqli_data_seek)  }  /* }}} */ -/* {{{ proto void mysqli_debug(string debug) +/* {{{ proto void mysqli_debug(string debug) U  */  PHP_FUNCTION(mysqli_debug)  { -	char		*debug; -	int			debug_len; +	char	*debug; +	int		debug_len;  	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &debug, &debug_len) == FAILURE) {  		return; @@ -528,25 +643,20 @@ PHP_FUNCTION(mysqli_debug)  }  /* }}} */ +  /* {{{ proto bool mysqli_dump_debug_info(object link)  */  PHP_FUNCTION(mysqli_dump_debug_info)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; -	ulong		rc; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	rc = mysql_dump_debug_info(mysql->mysql); - -	if (rc) { -		RETURN_FALSE; -	} -	RETURN_TRUE;	 +	RETURN_BOOL(!mysql_dump_debug_info(mysql->mysql))  }  /* }}} */ @@ -554,8 +664,8 @@ PHP_FUNCTION(mysqli_dump_debug_info)     Returns the numerical value of the error message from previous MySQL operation */  PHP_FUNCTION(mysqli_errno)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -569,8 +679,8 @@ PHP_FUNCTION(mysqli_errno)     Returns the text of the error message from previous MySQL operation */  PHP_FUNCTION(mysqli_error)   { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -584,16 +694,19 @@ PHP_FUNCTION(mysqli_error)     Execute a prepared statement */  PHP_FUNCTION(mysqli_stmt_execute)  { -	MY_STMT 		*stmt; -	zval 			*mysql_stmt; -	unsigned int 	i; +	MY_STMT		*stmt; +	zval		*mysql_stmt; +#ifndef HAVE_MYSQLND +	unsigned int	i; +#endif  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); -	 -	for (i = 0; i < stmt->param.var_cnt; i++) {		 + +#ifndef HAVE_MYSQLND +	for (i = 0; i < stmt->param.var_cnt; i++) {  		if (stmt->param.vars[i]) {  			if ( !(stmt->param.is_null[i] = (stmt->param.vars[i]->type == IS_NULL)) ) {  				switch (stmt->stmt->params[i].buffer_type) { @@ -604,40 +717,43 @@ PHP_FUNCTION(mysqli_stmt_execute)  						break;  					case MYSQL_TYPE_DOUBLE:  						convert_to_double_ex(&stmt->param.vars[i]); -						stmt->stmt->params[i].buffer = (char*)&Z_LVAL_PP(&stmt->param.vars[i]); +						stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);  						break;  					case MYSQL_TYPE_LONG:  						convert_to_long_ex(&stmt->param.vars[i]); -						stmt->stmt->params[i].buffer = (char*)&Z_LVAL_PP(&stmt->param.vars[i]); +						stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);  						break;  					default:  						break;  				} -			}	 +			}  		}  	} +#endif +  	if (mysql_stmt_execute(stmt->stmt)) {  		MYSQLI_REPORT_STMT_ERROR(stmt->stmt); -		RETURN_FALSE; +		RETVAL_FALSE; +	} else { +		RETVAL_TRUE;  	}  	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -		php_mysqli_report_index(stmt->query, stmt->stmt->mysql->server_status TSRMLS_CC); +		php_mysqli_report_index(stmt->query, mysqli_stmt_server_status(stmt->stmt) TSRMLS_CC);  	} -	 -	RETURN_TRUE;  }  /* }}} */ -/* {{{ proto mixed mysqli_stmt_fetch(object stmt) +#ifndef HAVE_MYSQLND +/* {{{ void mysqli_stmt_fetch_libmysql     Fetch results from a prepared statement into the bound variables */ -PHP_FUNCTION(mysqli_stmt_fetch) +void mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAMETERS)  { -	MY_STMT 		*stmt; -	zval 			*mysql_stmt; -	unsigned int 	i; -	ulong 			ret; -	unsigned int    uval; +	MY_STMT		*stmt; +	zval			*mysql_stmt; +	unsigned int	i; +	ulong			ret; +	unsigned int	uval;  	my_ulonglong	llval; @@ -647,8 +763,6 @@ PHP_FUNCTION(mysqli_stmt_fetch)  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);  	/* reset buffers */ - -  	for (i = 0; i < stmt->result.var_cnt; i++) {  		if (stmt->result.buf[i].type == IS_STRING) {  			memset(stmt->result.buf[i].val, 0, stmt->result.buf[i].buflen); @@ -661,6 +775,11 @@ PHP_FUNCTION(mysqli_stmt_fetch)  	if (!ret) {  #endif  		for (i = 0; i < stmt->result.var_cnt; i++) { +			/* +			  QQ: Isn't it quite better to call zval_dtor(). What if the user has +			  assigned a resource, or an array to the bound variable? We are going +			  to leak probably. zval_dtor() will handle also Unicode/Non-unicode mode. +			*/  			/* Even if the string is of length zero there is one byte alloced so efree() in all cases */  			if (Z_TYPE_P(stmt->result.vars[i]) == IS_STRING) {  				efree(stmt->result.vars[i]->value.str.val); @@ -669,11 +788,11 @@ PHP_FUNCTION(mysqli_stmt_fetch)  				switch (stmt->result.buf[i].type) {  					case IS_LONG:  						if ((stmt->stmt->fields[i].type == MYSQL_TYPE_LONG)  -						    && (stmt->stmt->fields[i].flags & UNSIGNED_FLAG))  +						    && (stmt->stmt->fields[i].flags & UNSIGNED_FLAG))  						{  							/* unsigned int (11) */  							uval= *(unsigned int *) stmt->result.buf[i].val; - +#if SIZEOF_LONG==4  							if (uval > INT_MAX) {  								char *tmp, *p;  								int j=10; @@ -681,13 +800,14 @@ PHP_FUNCTION(mysqli_stmt_fetch)  								p= &tmp[9];  								do {   									*p-- = (uval % 10) + 48; -									uval = uval / 10;							 +									uval = uval / 10;  								} while (--j > 0);  								tmp[10]= '\0'; -								/* unsigned int > INT_MAX is 10 digis - ALWAYS */ +								/* unsigned int > INT_MAX is 10 digits - ALWAYS */  								ZVAL_STRINGL(stmt->result.vars[i], tmp, 10, 0);  								break;  							} +#endif  						}  						if (stmt->stmt->fields[i].flags & UNSIGNED_FLAG) {   							ZVAL_LONG(stmt->result.vars[i], *(unsigned int *)stmt->result.buf[i].val); @@ -702,11 +822,12 @@ PHP_FUNCTION(mysqli_stmt_fetch)  						if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_LONGLONG) {  							my_bool uns= (stmt->stmt->fields[i].flags & UNSIGNED_FLAG)? 1:0;  							llval= *(my_ulonglong *) stmt->result.buf[i].val; -#if SIZEOF_LONG==8   +#if SIZEOF_LONG==8  							if (uns && llval > 9223372036854775807L) {  #elif SIZEOF_LONG==4  							if ((uns && llval > L64(2147483647)) ||  -							    (!uns && (( L64(2147483647) < (my_longlong) llval) || (L64(-2147483648) > (my_longlong) llval)))) +								(!uns && (( L64(2147483647) < (my_longlong) llval) || +								(L64(-2147483648) > (my_longlong) llval))))  							{  #endif  								char tmp[22]; @@ -719,7 +840,7 @@ PHP_FUNCTION(mysqli_stmt_fetch)  							} else {  								ZVAL_LONG(stmt->result.vars[i], llval);  							} -						}  +						}  #if MYSQL_VERSION_ID > 50002  						else if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_BIT) {  							llval = *(my_ulonglong *)stmt->result.buf[i].val; @@ -728,19 +849,21 @@ PHP_FUNCTION(mysqli_stmt_fetch)  #endif  						else {  #if defined(MYSQL_DATA_TRUNCATED) && MYSQL_VERSION_ID > 50002 -							if(ret == MYSQL_DATA_TRUNCATED && *(stmt->stmt->bind[i].error) != 0) { +							if (ret == MYSQL_DATA_TRUNCATED && *(stmt->stmt->bind[i].error) != 0) {  								/* result was truncated */ -								ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->stmt->bind[i].buffer_length, 1); +								ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, +											 stmt->stmt->bind[i].buffer_length, 1);  							} else {  #else  							{  #endif -								ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->result.buf[i].buflen, 1); +								ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, +											 stmt->result.buf[i].buflen, 1);  							}  						}  						break;  					default: -						break;	 +						break;  				}  			} else {  				ZVAL_NULL(stmt->result.vars[i]); @@ -770,14 +893,68 @@ PHP_FUNCTION(mysqli_stmt_fetch)  	}  }  /* }}} */ +#else +/* {{{ mixed mysqli_stmt_fetch_mysqlnd */ +void mysqli_stmt_fetch_mysqlnd(INTERNAL_FUNCTION_PARAMETERS) +{ +	MY_STMT		*stmt; +	zval		*mysql_stmt; +	zend_bool	fetched_anything; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); + +	if (FAIL  == mysqlnd_stmt_fetch(stmt->stmt, &fetched_anything)) { +		RETURN_BOOL(FALSE); +	} else if (fetched_anything == TRUE) { +		RETURN_BOOL(TRUE); +	} else { +		RETURN_NULL(); +	} +} +#endif +/* }}} */ + + +/* {{{ proto mixed mysqli_stmt_fetch(object stmt) U +   Fetch results from a prepared statement into the bound variables */ +PHP_FUNCTION(mysqli_stmt_fetch) +{ +#if !defined(HAVE_MYSQLND) +	mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAM_PASSTHRU); +#else +	mysqli_stmt_fetch_mysqlnd(INTERNAL_FUNCTION_PARAM_PASSTHRU); +#endif +} +/* }}} */ + +/* {{{  php_add_field_properties */ +static void php_add_field_properties(zval *value, MYSQL_FIELD *field TSRMLS_DC) +{ +	add_property_string(value, "name",(field->name ? field->name : ""), 1); +	add_property_string(value, "orgname",(field->org_name ? field->org_name : ""), 1); +	add_property_string(value, "table",(field->table ? field->table : ""), 1); +	add_property_string(value, "orgtable",(field->org_table ? field->org_table : ""), 1); +	add_property_string(value, "def",(field->def ? field->def : ""), 1); + +	add_property_long(value, "max_length", field->max_length); +	add_property_long(value, "length", field->length); +	add_property_long(value, "charsetnr", field->charsetnr); +	add_property_long(value, "flags", field->flags); +	add_property_long(value, "type", field->type); +	add_property_long(value, "decimals", field->decimals); +} +/* }}} */  /* {{{ proto mixed mysqli_fetch_field (object result)     Get column information from a result and return as an object */  PHP_FUNCTION(mysqli_fetch_field)   { -	MYSQL_RES 	*result; -	zval  		*mysql_result; -	MYSQL_FIELD *field; +	MYSQL_RES	*result; +	zval		*mysql_result; +	MYSQL_FIELD	*field;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {  		return; @@ -790,18 +967,7 @@ PHP_FUNCTION(mysqli_fetch_field)  	}  	object_init(return_value); - -	add_property_string(return_value, "name",(field->name ? field->name : ""), 1); -	add_property_string(return_value, "orgname",(field->org_name ? field->org_name : ""), 1); -	add_property_string(return_value, "table",(field->table ? field->table : ""), 1); -	add_property_string(return_value, "orgtable",(field->org_table ? field->org_table : ""), 1); -	add_property_string(return_value, "def",(field->def ? field->def : ""), 1); -	add_property_long(return_value, "max_length", field->max_length); -	add_property_long(return_value, "length", field->length); -	add_property_long(return_value, "charsetnr", field->charsetnr); -	add_property_long(return_value, "flags", field->flags); -	add_property_long(return_value, "type", field->type); -	add_property_long(return_value, "decimals", field->decimals); +	php_add_field_properties(return_value, field TSRMLS_CC);  }  /* }}} */ @@ -810,9 +976,9 @@ PHP_FUNCTION(mysqli_fetch_field)  PHP_FUNCTION(mysqli_fetch_fields)   {  	MYSQL_RES	*result; -	zval  		*mysql_result; +	zval		*mysql_result;  	MYSQL_FIELD	*field; -	zval 		*obj; +	zval		*obj;  	unsigned int i; @@ -827,22 +993,10 @@ PHP_FUNCTION(mysqli_fetch_fields)  	for (i = 0; i < mysql_num_fields(result); i++) {  		field = mysql_fetch_field_direct(result, i); -  		MAKE_STD_ZVAL(obj);  		object_init(obj); -		add_property_string(obj, "name",(field->name ? field->name : ""), 1); -		add_property_string(obj, "orgname",(field->org_name ? field->org_name : ""), 1); -		add_property_string(obj, "table",(field->table ? field->table : ""), 1); -		add_property_string(obj, "orgtable",(field->org_table ? field->org_table : ""), 1); -		add_property_string(obj, "def",(field->def ? field->def : ""), 1); -		add_property_long(obj, "max_length", field->max_length); -		add_property_long(obj, "length", field->length); -		add_property_long(obj, "charsetnr", field->charsetnr); -		add_property_long(obj, "flags", field->flags); -		add_property_long(obj, "type", field->type); -		add_property_long(obj, "decimals", field->decimals); - +		php_add_field_properties(obj, field TSRMLS_CC);  		add_index_zval(return_value, i, obj);  	}  } @@ -854,8 +1008,8 @@ PHP_FUNCTION(mysqli_fetch_field_direct)  {  	MYSQL_RES	*result;  	zval		*mysql_result; -	MYSQL_FIELD *field; -	long 		offset; +	MYSQL_FIELD	*field; +	long		offset;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_result, mysqli_result_class_entry, &offset) == FAILURE) {  		return; @@ -873,18 +1027,7 @@ PHP_FUNCTION(mysqli_fetch_field_direct)  	}  	object_init(return_value); - -	add_property_string(return_value, "name",(field->name ? field->name : ""), 1); -	add_property_string(return_value, "orgname",(field->org_name ? field->org_name : ""), 1); -	add_property_string(return_value, "table",(field->table ? field->table : ""), 1); -	add_property_string(return_value, "orgtable",(field->org_table ? field->org_table : ""), 1); -	add_property_string(return_value, "def",(field->def ? field->def : ""), 1); -	add_property_long(return_value, "max_length", field->max_length); -	add_property_long(return_value, "length", field->length); -	add_property_long(return_value, "charsetnr", field->charsetnr); -	add_property_long(return_value, "flags", field->flags); -	add_property_long(return_value, "type", field->type); -	add_property_long(return_value, "decimals", field->decimals); +	php_add_field_properties(return_value, field TSRMLS_CC);  }  /* }}} */ @@ -896,7 +1039,7 @@ PHP_FUNCTION(mysqli_fetch_lengths)  	zval			*mysql_result;  	unsigned int	i;  	unsigned long	*ret; -	 +  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {  		return;  	} @@ -910,7 +1053,7 @@ PHP_FUNCTION(mysqli_fetch_lengths)  	array_init(return_value);  	for (i = 0; i < mysql_num_fields(result); i++) { -		add_index_long(return_value, i, ret[i]);	 +		add_index_long(return_value, i, ret[i]);  	}  }  /* }}} */ @@ -919,17 +1062,28 @@ PHP_FUNCTION(mysqli_fetch_lengths)     Get a result row as an enumerated array */  PHP_FUNCTION(mysqli_fetch_row)   { +#if !defined(HAVE_MYSQLND)  	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_NUM, 0); +#else +	MYSQL_RES	*result; +	zval		*mysql_result; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); +	mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, return_value, MYSQLND_MYSQLI);  +#endif  }  /* }}} */  /* {{{ proto int mysqli_field_count(object link)     Fetch the number of fields returned by the last query for the given link  */ -PHP_FUNCTION(mysqli_field_count)  +PHP_FUNCTION(mysqli_field_count)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -958,7 +1112,7 @@ PHP_FUNCTION(mysqli_field_seek)  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid field offset");  		RETURN_FALSE;   	} -	 +  	mysql_field_seek(result, fieldnr);  	RETURN_TRUE;  } @@ -975,7 +1129,7 @@ PHP_FUNCTION(mysqli_field_tell)  		return;  	}  	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); -	 +  	RETURN_LONG(mysql_field_tell(result));  }  /* }}} */ @@ -992,12 +1146,12 @@ PHP_FUNCTION(mysqli_free_result)  	}  	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); -	mysql_free_result(result); -	MYSQLI_CLEAR_RESOURCE(&mysql_result);	 +	mysqli_free_result(result, FALSE); +	MYSQLI_CLEAR_RESOURCE(&mysql_result);  }  /* }}} */ -/* {{{ proto string mysqli_get_client_info(void)  +/* {{{ proto string mysqli_get_client_info(void)     Get MySQL client info */  PHP_FUNCTION(mysqli_get_client_info)  { @@ -1005,7 +1159,7 @@ PHP_FUNCTION(mysqli_get_client_info)  }  /* }}} */ -/* {{{ proto int mysqli_get_client_version(void)  +/* {{{ proto int mysqli_get_client_version(void)     Get MySQL client info */  PHP_FUNCTION(mysqli_get_client_version)  { @@ -1018,7 +1172,7 @@ PHP_FUNCTION(mysqli_get_client_version)  PHP_FUNCTION(mysqli_get_host_info)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link = NULL; +	zval		*mysql_link = NULL;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1033,24 +1187,23 @@ PHP_FUNCTION(mysqli_get_host_info)     Get MySQL protocol information */  PHP_FUNCTION(mysqli_get_proto_info)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link = NULL; +	MY_MYSQL	*mysql; +	zval		*mysql_link = NULL;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -  	RETURN_LONG(mysql_get_proto_info(mysql->mysql));  }  /* }}} */ -/* {{{ proto string mysqli_get_server_info(object link)  +/* {{{ proto string mysqli_get_server_info(object link)     Get MySQL server info */  PHP_FUNCTION(mysqli_get_server_info)  {  	MY_MYSQL	*mysql; -	zval	  	*mysql_link = NULL; +	zval		*mysql_link = NULL;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1083,14 +1236,16 @@ PHP_FUNCTION(mysqli_get_server_version)  PHP_FUNCTION(mysqli_info)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link = NULL; +	zval		*mysql_link = NULL; +	const char	*info;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	RETURN_STRING((mysql->mysql->info) ? mysql->mysql->info : "", 1); +	info = mysql_info(mysql->mysql); +	RETURN_STRING((info) ? (char *)info : "", 1);  }  /* }}} */ @@ -1101,7 +1256,12 @@ PHP_FUNCTION(mysqli_init)  	MYSQLI_RESOURCE *mysqli_resource;  	MY_MYSQL *mysql = (MY_MYSQL *)ecalloc(1, sizeof(MY_MYSQL)); -	if (!(mysql->mysql = mysql_init(NULL))) { +#if !defined(HAVE_MYSQLND) +	if (!(mysql->mysql = mysql_init(NULL))) +#else +	if (!(mysql->mysql = mysql_init(FALSE))) +#endif +	{  		efree(mysql);  		RETURN_FALSE;  	} @@ -1123,8 +1283,8 @@ PHP_FUNCTION(mysqli_init)  PHP_FUNCTION(mysqli_insert_id)  {  	MY_MYSQL		*mysql; -	my_ulonglong 	rc; -	zval  			*mysql_link; +	my_ulonglong	rc; +	zval			*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1139,15 +1299,20 @@ PHP_FUNCTION(mysqli_insert_id)     Kill a mysql process on the server */  PHP_FUNCTION(mysqli_kill)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; -	long   		processid; +	MY_MYSQL	*mysql; +	zval		*mysql_link; +	long		processid;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_link, mysqli_link_class_entry, &processid) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	 + +	if (processid <= 0) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "processid should have positive value"); +		RETURN_FALSE; +	} +  	if (mysql_kill(mysql->mysql, processid)) {  		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);  		RETURN_FALSE; @@ -1158,6 +1323,7 @@ PHP_FUNCTION(mysqli_kill)  /* {{{ proto void mysqli_set_local_infile_default(object link)     unsets user defined handler for load local infile command */ +#if !defined(HAVE_MYSQLND)  PHP_FUNCTION(mysqli_set_local_infile_default)  {  	MY_MYSQL	*mysql; @@ -1182,7 +1348,7 @@ PHP_FUNCTION(mysqli_set_local_infile_default)  PHP_FUNCTION(mysqli_set_local_infile_handler)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link; +	zval		*mysql_link;  	char		*callback_name;  	zval		*callback_func; @@ -1197,24 +1363,29 @@ PHP_FUNCTION(mysqli_set_local_infile_handler)  	if (!zend_is_callable(callback_func, 0, &callback_name)) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback function %s", callback_name);  		efree(callback_name); -		RETURN_FALSE;		 +		RETURN_FALSE;  	}  	efree(callback_name);  	/* save callback function */ -	ALLOC_ZVAL(mysql->li_read);	 -	ZVAL_STRING(mysql->li_read, callback_func->value.str.val, 1); +	if (!mysql->li_read) { +		MAKE_STD_ZVAL(mysql->li_read); +	} else { +		zval_dtor(mysql->li_read); +	} +	ZVAL_STRINGL(mysql->li_read, Z_STRVAL_P(callback_func), Z_STRLEN_P(callback_func), 1);  	RETURN_TRUE;  } +#endif  /* }}} */  /* {{{ proto bool mysqli_more_results(object link)     check if there any more query results from a multi query */  PHP_FUNCTION(mysqli_more_results)  { -	MY_MYSQL		*mysql; -	zval			*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1228,14 +1399,20 @@ PHP_FUNCTION(mysqli_more_results)  /* {{{ proto bool mysqli_next_result(object link)     read next result from multi_query */  PHP_FUNCTION(mysqli_next_result) { -	MY_MYSQL		*mysql; -	zval			*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); +	if (!mysql_more_results(mysql->mysql)) { +		php_error_docref(NULL TSRMLS_CC, E_STRICT, "There is no next result set. " +						"Please, call mysqli_more_results()/mysqli::more_results() to check " +						"whether to call this function/method"); +	} +  	RETURN_BOOL(!mysql_next_result(mysql->mysql));  }  /* }}} */ @@ -1268,7 +1445,7 @@ PHP_FUNCTION(mysqli_num_rows)  	}  	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); -	if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) { +	if (mysqli_result_is_unbuffered(result)) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function cannot be used with MYSQL_USE_RESULT");  		RETURN_LONG(0);  	} @@ -1281,12 +1458,12 @@ PHP_FUNCTION(mysqli_num_rows)     Set options */  PHP_FUNCTION(mysqli_options)  { -	MY_MYSQL 		*mysql; -	zval  			*mysql_link = NULL; -	zval  			*mysql_value; -	long  			mysql_option; -	unsigned int 	l_value; -	long  			ret; +	MY_MYSQL		*mysql; +	zval			*mysql_link = NULL; +	zval			*mysql_value; +	long			mysql_option; +	unsigned int	l_value; +	long			ret;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olz", &mysql_link, mysqli_link_class_entry, &mysql_option, &mysql_value) == FAILURE) {  		return; @@ -1311,7 +1488,7 @@ PHP_FUNCTION(mysqli_options)  	}  	RETURN_BOOL(!ret); -}    +}  /* }}} */ @@ -1319,8 +1496,8 @@ PHP_FUNCTION(mysqli_options)     Ping a server connection or reconnect if there is no connection */  PHP_FUNCTION(mysqli_ping)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	long		rc;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { @@ -1339,60 +1516,71 @@ PHP_FUNCTION(mysqli_ping)  PHP_FUNCTION(mysqli_prepare)  {  	MY_MYSQL		*mysql; -	MY_STMT 		*stmt; +	MY_STMT			*stmt;  	char			*query = NULL;  	unsigned int	query_len;  	zval			*mysql_link; -	MYSQLI_RESOURCE *mysqli_resource;  +	MYSQLI_RESOURCE	*mysqli_resource;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",&mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); + +#if !defined(HAVE_MYSQLND)  	if (mysql->mysql->status == MYSQL_STATUS_GET_RESULT) {  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "All data must be fetched before a new statement prepare takes place");  		RETURN_FALSE;  	} +#endif  	stmt = (MY_STMT *)ecalloc(1,sizeof(MY_STMT));  	if ((stmt->stmt = mysql_stmt_init(mysql->mysql))) {  		if (mysql_stmt_prepare(stmt->stmt, query, query_len)) { -  			char  last_error[MYSQL_ERRMSG_SIZE]; -  			char  sqlstate[SQLSTATE_LENGTH+1];	 +			/* mysql_stmt_close() clears errors, so we have to store them temporarily */ +#if !defined(HAVE_MYSQLND) +			char  last_error[MYSQL_ERRMSG_SIZE]; +			char  sqlstate[SQLSTATE_LENGTH+1];  			unsigned int last_errno; -			/* mysql_stmt_close clears errors, so we have to store them temporarily */  			last_errno = stmt->stmt->last_errno;  			memcpy(last_error, stmt->stmt->last_error, MYSQL_ERRMSG_SIZE);  			memcpy(sqlstate, mysql->mysql->net.sqlstate, SQLSTATE_LENGTH+1); - -			mysql_stmt_close(stmt->stmt); +#else +			mysqlnd_error_info error_info = mysql->mysql->error_info; +#endif +			mysqli_stmt_close(stmt->stmt, FALSE);  			stmt->stmt = NULL;  			/* restore error messages */ +#if !defined(HAVE_MYSQLND)  			mysql->mysql->net.last_errno = last_errno;  			memcpy(mysql->mysql->net.last_error, last_error, MYSQL_ERRMSG_SIZE);  			memcpy(mysql->mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH+1); +#else +			mysql->mysql->error_info = error_info; +#endif  		}  	} -	/* don't joing to the previous if because it won't work if mysql_stmt_prepare_fails */ + +	/* don't initialize stmt->query with NULL, we ecalloc()-ed the memory */ +	/* Get performance boost if reporting is switched off */ +	if (stmt->stmt && query_len && (MyG(report_mode) & MYSQLI_REPORT_INDEX)) { +		stmt->query = (char *)emalloc(query_len + 1); +		memcpy(stmt->query, query, query_len); +		stmt->query[query_len] = '\0'; +	} + +	/* don't join to the previous if because it won't work if mysql_stmt_prepare_fails */  	if (!stmt->stmt) {  		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);  		efree(stmt);  		RETURN_FALSE;  	} -  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = (void *)stmt; -	/* don't initialize stmt->query with NULL, we ecalloc()-ed the memory */ -	/* Get performance boost if reporting is switched off */ -	if (query_len && (MyG(report_mode) & MYSQLI_REPORT_INDEX)) { -		stmt->query = (char *)emalloc(query_len + 1); -		memcpy(stmt->query, query, query_len); -		stmt->query[query_len] = '\0'; -	}  	/* change status */  	mysqli_resource->status = MYSQLI_STATUS_VALID; @@ -1404,10 +1592,10 @@ PHP_FUNCTION(mysqli_prepare)     Open a connection to a mysql server */   PHP_FUNCTION(mysqli_real_connect)  { -	MY_MYSQL 		*mysql; -	char 			*hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; -	unsigned int 	hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; -	unsigned long 	port=0, flags=0; +	MY_MYSQL		*mysql; +	char			*hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; +	unsigned int	hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; +	unsigned long	port=0, flags=0;  	zval			*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sssslsl", &mysql_link, mysqli_link_class_entry, @@ -1419,37 +1607,38 @@ PHP_FUNCTION(mysqli_real_connect)  	if (!socket_len) {  		socket = NULL;  	} - -	/* TODO: safe mode handling */ -	if (PG(sql_safe_mode)) { -	} else { -		if (!passwd) { -			passwd = MyG(default_pw); -			if (!username){ -				username = MyG(default_user); -				if (!hostname) { -					hostname = MyG(default_host); -				} -			} -		} +	if (!socket) { +		socket = MyG(default_socket);  	}	 +	if (!passwd) { +		passwd = MyG(default_pw); +		passwd_len = strlen(passwd); +	} +	if (!username){ +		username = MyG(default_user); +	} +	if (!hostname) { +		hostname = MyG(default_host); +	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED);  	/* remove some insecure options */  	flags &= ~CLIENT_MULTI_STATEMENTS;   /* don't allow multi_queries via connect parameter */ -	if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) { -		flags &= ~CLIENT_LOCAL_FILES; -	} - -	if (!socket) { -		socket = MyG(default_socket); +	if (PG(open_basedir) && PG(open_basedir)[0] != '\0') { +		flags ^= CLIENT_LOCAL_FILES;  	} -	if (mysql_real_connect(mysql->mysql,hostname,username,passwd,dbname,port,socket,flags) == NULL) { +#if !defined(HAVE_MYSQLND) +	if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname ,port, socket ,flags) == NULL)  +#else +	if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len, +						port, socket, flags, MyG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL) +#endif +	{  		php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC); -		php_mysqli_throw_sql_exception( mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC, -										"%s", mysql->mysql->net.last_error); +		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC, +										"%s", mysql_error(mysql->mysql));  		/* change status */  		MYSQLI_SET_STATUS(&mysql_link, MYSQLI_STATUS_INITIALIZED); @@ -1458,10 +1647,14 @@ PHP_FUNCTION(mysqli_real_connect)  	php_mysqli_set_error(mysql_errno(mysql->mysql), (char *)mysql_error(mysql->mysql) TSRMLS_CC); +#if !defined(HAVE_MYSQLND)  	mysql->mysql->reconnect = MyG(reconnect);  	/* set our own local_infile handler */  	php_set_local_infile_handler_default(mysql); +#endif + +	mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&MyG(allow_local_infile));  	/* change status */  	MYSQLI_SET_STATUS(&mysql_link, MYSQLI_STATUS_VALID); @@ -1477,7 +1670,7 @@ PHP_FUNCTION(mysqli_real_query)  	MY_MYSQL		*mysql;  	zval			*mysql_link;  	char			*query = NULL; -	unsigned int 	query_len; +	unsigned int	query_len;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) {  		return; @@ -1493,7 +1686,7 @@ PHP_FUNCTION(mysqli_real_query)  	if (!mysql_field_count(mysql->mysql)) {  		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -			php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC); +			php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);  		}  	} @@ -1517,7 +1710,7 @@ PHP_FUNCTION(mysqli_real_escape_string) {  	newstr = safe_emalloc(2, escapestr_len, 1);  	newstr_len = mysql_real_escape_string(mysql->mysql, newstr, escapestr, escapestr_len);  	newstr = erealloc(newstr, newstr_len + 1); -		 +  	RETURN_STRINGL(newstr, newstr_len, 0);  }  /* }}} */ @@ -1527,7 +1720,7 @@ PHP_FUNCTION(mysqli_real_escape_string) {  PHP_FUNCTION(mysqli_rollback)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1546,12 +1739,11 @@ PHP_FUNCTION(mysqli_rollback)  PHP_FUNCTION(mysqli_stmt_send_long_data)  {  	MY_STMT *stmt; -	zval  	*mysql_stmt; +	zval	*mysql_stmt;  	char	*data;  	long	param_nr;  	int		data_len; -  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", &mysql_stmt, mysqli_stmt_class_entry, ¶m_nr, &data, &data_len) == FAILURE) {  		return;  	} @@ -1573,8 +1765,8 @@ PHP_FUNCTION(mysqli_stmt_send_long_data)     Return the number of rows affected in the last query for the given link */  PHP_FUNCTION(mysqli_stmt_affected_rows)  { -	MY_STMT 		*stmt; -	zval    		*mysql_stmt; +	MY_STMT			*stmt; +	zval			*mysql_stmt;  	my_ulonglong	rc;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { @@ -1590,21 +1782,21 @@ PHP_FUNCTION(mysqli_stmt_affected_rows)  }  /* }}} */ -/* {{{ proto bool mysqli_stmt_close(object stmt)  +/* {{{ proto bool mysqli_stmt_close(object stmt)     Close statement */  PHP_FUNCTION(mysqli_stmt_close)  { -	MY_STMT 	*stmt; -	zval    	*mysql_stmt; +	MY_STMT		*stmt; +	zval		*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); -	mysql_stmt_close(stmt->stmt); +	mysqli_stmt_close(stmt->stmt, FALSE);  	stmt->stmt = NULL; -	php_clear_stmt_bind(stmt);  +	php_clear_stmt_bind(stmt TSRMLS_CC);  	MYSQLI_CLEAR_RESOURCE(&mysql_stmt);  	RETURN_TRUE;  } @@ -1614,9 +1806,9 @@ PHP_FUNCTION(mysqli_stmt_close)     Move internal result pointer */  PHP_FUNCTION(mysqli_stmt_data_seek)  { -	MY_STMT 	*stmt; -	zval  		*mysql_stmt; -	long  		offset; +	MY_STMT		*stmt; +	zval		*mysql_stmt; +	long		offset;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_stmt, mysqli_stmt_class_entry, &offset) == FAILURE) {  		return; @@ -1636,7 +1828,7 @@ PHP_FUNCTION(mysqli_stmt_data_seek)     Return the number of result columns for the given statement */  PHP_FUNCTION(mysqli_stmt_field_count)  { -	MY_STMT 	*stmt; +	MY_STMT		*stmt;  	zval		*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { @@ -1652,8 +1844,8 @@ PHP_FUNCTION(mysqli_stmt_field_count)     Free stored result memory for the given statement handle */  PHP_FUNCTION(mysqli_stmt_free_result)   { -	MY_STMT 		*stmt; -	zval    		*mysql_stmt; +	MY_STMT		*stmt; +	zval		*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return; @@ -1669,9 +1861,9 @@ PHP_FUNCTION(mysqli_stmt_free_result)     Get the ID generated from the previous INSERT operation */  PHP_FUNCTION(mysqli_stmt_insert_id)  { -	MY_STMT		 	*stmt; -	my_ulonglong 	rc; -	zval  			*mysql_stmt; +	MY_STMT			*stmt; +	my_ulonglong	rc; +	zval			*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return; @@ -1682,13 +1874,13 @@ PHP_FUNCTION(mysqli_stmt_insert_id)  }  /* }}} */ -/* {{{ proto int mysqli_stmt_param_count(object stmt) { +/* {{{ proto int mysqli_stmt_param_count(object stmt)     Return the number of parameter for the given statement */  PHP_FUNCTION(mysqli_stmt_param_count)  { -	MY_STMT 	*stmt; +	MY_STMT		*stmt;  	zval		*mysql_stmt; -	 +  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return;  	} @@ -1700,10 +1892,10 @@ PHP_FUNCTION(mysqli_stmt_param_count)  /* {{{ proto bool mysqli_stmt_reset(object stmt)     reset a prepared statement */ -PHP_FUNCTION(mysqli_stmt_reset)  +PHP_FUNCTION(mysqli_stmt_reset)  { -	MY_STMT 		*stmt; -	zval    		*mysql_stmt; +	MY_STMT		*stmt; +	zval		*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return; @@ -1722,8 +1914,8 @@ PHP_FUNCTION(mysqli_stmt_reset)     Return the number of rows in statements result set */  PHP_FUNCTION(mysqli_stmt_num_rows)  { -	MY_STMT 		*stmt; -	zval    		*mysql_stmt; +	MY_STMT			*stmt; +	zval			*mysql_stmt;  	my_ulonglong	rc;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { @@ -1737,27 +1929,25 @@ PHP_FUNCTION(mysqli_stmt_num_rows)  }  /* }}} */ -/* {{{ proto string mysqli_select_db(object link, string dbname) +/* {{{ proto bool mysqli_select_db(object link, string dbname)     Select a MySQL database */  PHP_FUNCTION(mysqli_select_db)   {  	MY_MYSQL	*mysql; -	zval  		*mysql_link; -	char  		*dbname; -	int   		dbname_len; - +	zval		*mysql_link; +	char		*dbname; +	int			dbname_len;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &dbname, &dbname_len) == FAILURE) {  		return;  	}   	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	 -	if (!mysql_select_db(mysql->mysql, dbname)) { -		RETURN_TRUE; + +	if (mysql_select_db(mysql->mysql, dbname)) { +		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); +		RETURN_FALSE;  	} -	 -	MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); -	RETURN_FALSE; +	RETURN_TRUE;  }  /* }}} */ @@ -1765,8 +1955,8 @@ PHP_FUNCTION(mysqli_select_db)     Returns the SQLSTATE error from previous MySQL operation */  PHP_FUNCTION(mysqli_sqlstate)   { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1776,21 +1966,22 @@ PHP_FUNCTION(mysqli_sqlstate)  }  /* }}} */ -/* {{{ proto bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher]) +/* {{{ proto bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher]) U  */ +#if !defined(HAVE_MYSQLND)  PHP_FUNCTION(mysqli_ssl_set)  {  	MY_MYSQL	*mysql; -	zval  		*mysql_link; -	char  		*ssl_parm[5]; +	zval		*mysql_link; +	char		*ssl_parm[5];  	int			ssl_parm_len[5], i;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osssss", &mysql_link, mysqli_link_class_entry, &ssl_parm[0], &ssl_parm_len[0], &ssl_parm[1], &ssl_parm_len[1], &ssl_parm[2], &ssl_parm_len[2], &ssl_parm[3], &ssl_parm_len[3], &ssl_parm[4], &ssl_parm_len[4])   == FAILURE) {  		return;  	} -	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED); +	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	for (i=0; i < 5; i++) { +	for (i = 0; i < 5; i++) {  		if (!ssl_parm_len[i]) {  			ssl_parm[i] = NULL;  		} @@ -1800,25 +1991,37 @@ PHP_FUNCTION(mysqli_ssl_set)  	RETURN_TRUE;  } +#endif  /* }}} */  /* {{{ proto mixed mysqli_stat(object link)      Get current system status */  PHP_FUNCTION(mysqli_stat)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	char		*stat; +#if defined(HAVE_MYSQLND) +	uint		stat_len; +#endif  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -	if ((stat = (char *)mysql_stat(mysql->mysql))) { +#if !defined(HAVE_MYSQLND) +	if ((stat = (char *)mysql_stat(mysql->mysql))) +	{  		RETURN_STRING(stat, 1); +#else +	if (mysqlnd_stat(mysql->mysql, &stat, &stat_len) == PASS) +	{ +		RETURN_STRINGL(stat, stat_len, 0); +#endif +	} else { +		RETURN_FALSE;  	} -	RETURN_FALSE;  }  /* }}} */ @@ -1828,16 +2031,23 @@ PHP_FUNCTION(mysqli_stat)  PHP_FUNCTION(mysqli_stmt_attr_set)  {  	MY_STMT	*stmt; -	zval 	*mysql_stmt; -	ulong   mode; +	zval	*mysql_stmt; +	long	mode_in; +	ulong	mode;  	ulong	attr;  	int		rc; -	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", &mysql_stmt, mysqli_stmt_class_entry, &attr, &mode) == FAILURE) { +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", &mysql_stmt, mysqli_stmt_class_entry, &attr, &mode_in) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); +	if (mode_in < 0) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "mode should be non-negative, %ld passed", mode_in); +		RETURN_FALSE; +	} + +	mode = mode_in;  	if ((rc = mysql_stmt_attr_set(stmt->stmt, attr, (void *)&mode))) {  		RETURN_FALSE;  	} @@ -1850,8 +2060,8 @@ PHP_FUNCTION(mysqli_stmt_attr_set)  PHP_FUNCTION(mysqli_stmt_attr_get)  {  	MY_STMT	*stmt; -	zval 	*mysql_stmt; -#if MYSQL_VERSION_ID > 50099 +	zval	*mysql_stmt; +#if !defined(HAVE_MYSQLND) && MYSQL_VERSION_ID > 50099  	my_bool value;  #else  	ulong	value = 0; @@ -1876,7 +2086,7 @@ PHP_FUNCTION(mysqli_stmt_attr_get)  PHP_FUNCTION(mysqli_stmt_errno)  {  	MY_STMT	*stmt; -	zval 	*mysql_stmt; +	zval	*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return; @@ -1909,9 +2119,9 @@ PHP_FUNCTION(mysqli_stmt_error)  PHP_FUNCTION(mysqli_stmt_init)  {  	MY_MYSQL		*mysql; -	MY_STMT 		*stmt; +	MY_STMT			*stmt;  	zval			*mysql_link; -	MYSQLI_RESOURCE *mysqli_resource;  +	MYSQLI_RESOURCE	*mysqli_resource;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",&mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -1961,9 +2171,9 @@ PHP_FUNCTION(mysqli_stmt_prepare)     return result set from statement */  PHP_FUNCTION(mysqli_stmt_result_metadata)  { -	MY_STMT		*stmt; +	MY_STMT			*stmt;  	MYSQL_RES		*result; -	zval 			*mysql_stmt; +	zval			*mysql_stmt;  	MYSQLI_RESOURCE	*mysqli_resource;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { @@ -1979,7 +2189,7 @@ PHP_FUNCTION(mysqli_stmt_result_metadata)  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = (void *)result;  	mysqli_resource->status = MYSQLI_STATUS_VALID; -	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);	 +	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);  }  /* }}} */ @@ -1987,32 +2197,37 @@ PHP_FUNCTION(mysqli_stmt_result_metadata)  */  PHP_FUNCTION(mysqli_stmt_store_result)  { -	MY_STMT *stmt; -	zval 	*mysql_stmt; -	int		i=0; +	MY_STMT	*stmt; +	zval	*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); -	/* -	  If the user wants to store the data and we have BLOBs/TEXTs we try to allocate -	  not the maximal length of the type (which is 16MB even for LONGBLOB) but -	  the maximal length of the field in the result set. If he/she has quite big -	  BLOB/TEXT columns after calling store_result() the memory usage of PHP will -	  double - but this is a known problem of the simple MySQL API ;) -	*/ -	for (i = mysql_stmt_field_count(stmt->stmt) - 1; i >=0; --i) { -		if (stmt->stmt->fields && (stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB || -			stmt->stmt->fields[i].type == MYSQL_TYPE_MEDIUM_BLOB || -			stmt->stmt->fields[i].type == MYSQL_TYPE_LONG_BLOB)) -		{ -			my_bool	tmp=1; -			mysql_stmt_attr_set(stmt->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &tmp); -			break; +#if !defined(HAVE_MYSQLND) +	{ +		/* +		  If the user wants to store the data and we have BLOBs/TEXTs we try to allocate +		  not the maximal length of the type (which is 16MB even for LONGBLOB) but +		  the maximal length of the field in the result set. If he/she has quite big +		  BLOB/TEXT columns after calling store_result() the memory usage of PHP will +		  double - but this is a known problem of the simple MySQL API ;) +		*/ +		int	i = 0; + +		for (i = mysql_stmt_field_count(stmt->stmt) - 1; i >=0; --i) { +			if (stmt->stmt->fields && (stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB || +				stmt->stmt->fields[i].type == MYSQL_TYPE_MEDIUM_BLOB || +				stmt->stmt->fields[i].type == MYSQL_TYPE_LONG_BLOB)) +			{ +				my_bool	tmp=1; +				mysql_stmt_attr_set(stmt->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &tmp); +				break; +			}  		}  	} +#endif  	if (mysql_stmt_store_result(stmt->stmt)){  		MYSQLI_REPORT_STMT_ERROR(stmt->stmt); @@ -2024,16 +2239,16 @@ PHP_FUNCTION(mysqli_stmt_store_result)  /* {{{ proto string mysqli_stmt_sqlstate(object stmt)  */ -PHP_FUNCTION(mysqli_stmt_sqlstate)  +PHP_FUNCTION(mysqli_stmt_sqlstate)  { -	MY_STMT *stmt; -	zval 	*mysql_stmt; +	MY_STMT	*stmt; +	zval	*mysql_stmt;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {  		return;  	}  	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); -	 +  	RETURN_STRING((char *)mysql_stmt_sqlstate(stmt->stmt),1);  }  /* }}} */ @@ -2042,9 +2257,9 @@ PHP_FUNCTION(mysqli_stmt_sqlstate)     Buffer result set on client */  PHP_FUNCTION(mysqli_store_result)  { -	MY_MYSQL 		*mysql; -	MYSQL_RES 		*result; -	zval  			*mysql_link; +	MY_MYSQL		*mysql; +	MYSQL_RES		*result; +	zval			*mysql_link;  	MYSQLI_RESOURCE	*mysqli_resource;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { @@ -2057,22 +2272,23 @@ PHP_FUNCTION(mysqli_store_result)  		RETURN_FALSE;  	}  	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -		php_mysqli_report_index("from previous query", mysql->mysql->server_status TSRMLS_CC); +		php_mysqli_report_index("from previous query", mysqli_server_status(mysql->mysql) TSRMLS_CC);  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = (void *)result;  	mysqli_resource->status = MYSQLI_STATUS_VALID; -	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);	 +	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);  }  /* }}} */ +  /* {{{ proto int mysqli_thread_id(object link)     Return the current thread ID */  PHP_FUNCTION(mysqli_thread_id)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -2089,17 +2305,16 @@ PHP_FUNCTION(mysqli_thread_safe)  {  	RETURN_BOOL(mysql_thread_safe());  } -  /* }}} */  /* {{{ proto mixed mysqli_use_result(object link)     Directly retrieve query results - do not buffer results on client side */  PHP_FUNCTION(mysqli_use_result)  { -	MY_MYSQL 		*mysql; -	MYSQL_RES 		*result; -	zval  			*mysql_link; -	MYSQLI_RESOURCE *mysqli_resource; +	MY_MYSQL		*mysql; +	MYSQL_RES		*result; +	zval			*mysql_link; +	MYSQLI_RESOURCE	*mysqli_resource;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -2112,12 +2327,12 @@ PHP_FUNCTION(mysqli_use_result)  	}  	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -		php_mysqli_report_index("from previous query", mysql->mysql->server_status TSRMLS_CC); +		php_mysqli_report_index("from previous query", mysqli_server_status(mysql->mysql) TSRMLS_CC);  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = (void *)result;  	mysqli_resource->status = MYSQLI_STATUS_VALID; -	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);	 +	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);  }  /* }}} */ @@ -2125,8 +2340,8 @@ PHP_FUNCTION(mysqli_use_result)     Return number of warnings from the last query for the given link */  PHP_FUNCTION(mysqli_warning_count)  { -	MY_MYSQL 	*mysql; -	zval  		*mysql_link; +	MY_MYSQL	*mysql; +	zval		*mysql_link;  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; diff --git a/ext/mysqli/mysqli_driver.c b/ext/mysqli/mysqli_driver.c index 494c2a554b..e8a9f71806 100644 --- a/ext/mysqli/mysqli_driver.c +++ b/ext/mysqli/mysqli_driver.c @@ -25,7 +25,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  #include "zend_exceptions.h" @@ -110,7 +110,7 @@ static int driver_client_version_read(mysqli_object *obj, zval **retval TSRMLS_D  static int driver_client_info_read(mysqli_object *obj, zval **retval TSRMLS_DC)  {  	ALLOC_ZVAL(*retval); -	ZVAL_STRING(*retval, MYSQL_SERVER_VERSION, 1); +	ZVAL_STRING(*retval, (char *)mysql_get_client_info(), 1);  	return SUCCESS;  }  /* }}} */ @@ -130,9 +130,17 @@ MAP_PROPERTY_MYG_LONG_READ(driver_report_read, report_mode);  ZEND_FUNCTION(mysqli_driver_construct)  { +#if G0 +	MYSQLI_RESOURCE 	*mysqli_resource; + +	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); +	mysqli_resource->ptr = 1; +	mysqli_resource->status = (ZEND_NUM_ARGS() == 1) ? MYSQLI_STATUS_INITIALIZED : MYSQLI_STATUS_VALID; +	((mysqli_object *) zend_object_store_get_object(getThis() TSRMLS_CC))->ptr = mysqli_resource; +#endif  } -mysqli_property_entry mysqli_driver_property_entries[] = { +const mysqli_property_entry mysqli_driver_property_entries[] = {  	{"client_info", driver_client_info_read, NULL},  	{"client_version", driver_client_version_read, NULL},  	{"driver_version", driver_driver_version_read, NULL}, @@ -145,8 +153,10 @@ mysqli_property_entry mysqli_driver_property_entries[] = {  /* {{{ mysqli_driver_methods[]   */  const zend_function_entry mysqli_driver_methods[] = { +#if defined(HAVE_EMBEDDED_MYSQLI)  	PHP_FALIAS(embedded_server_start, mysqli_embedded_server_start, NULL)  	PHP_FALIAS(embedded_server_end, mysqli_embedded_server_end, NULL) +#endif  	{NULL, NULL, NULL}  };  /* }}} */ diff --git a/ext/mysqli/mysqli_embedded.c b/ext/mysqli/mysqli_embedded.c index 12affc87c2..328025af21 100644 --- a/ext/mysqli/mysqli_embedded.c +++ b/ext/mysqli/mysqli_embedded.c @@ -25,7 +25,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  /* {{{ proto bool mysqli_embedded_server_start(bool start, array arguments, array groups)     initialize and start embedded server */ diff --git a/ext/mysqli/mysqli_exception.c b/ext/mysqli/mysqli_exception.c index b6b1d8903e..7b8a1d63e1 100644 --- a/ext/mysqli/mysqli_exception.c +++ b/ext/mysqli/mysqli_exception.c @@ -25,7 +25,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  #include "zend_exceptions.h"  /* {{{ mysqli_exception_methods[] diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index f82d31c405..623042e576 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -27,7 +27,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  static @@ -61,14 +61,24 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_connect_errno,						NULL)  	PHP_FE(mysqli_connect_error,						NULL)  	PHP_FE(mysqli_data_seek,							NULL) +	PHP_FE(mysqli_dump_debug_info,						NULL)  	PHP_FE(mysqli_debug,								NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FE(mysqli_disable_reads_from_master,			NULL)  	PHP_FE(mysqli_disable_rpl_parse,					NULL) -	PHP_FE(mysqli_dump_debug_info,						NULL)  	PHP_FE(mysqli_enable_reads_from_master,				NULL)  	PHP_FE(mysqli_enable_rpl_parse,						NULL) +	PHP_FE(mysqli_send_query,							NULL) +	PHP_FE(mysqli_slave_query,							NULL) +	PHP_FE(mysqli_master_query,							NULL) +	PHP_FE(mysqli_rpl_parse_enabled,					NULL) +	PHP_FE(mysqli_rpl_probe,							NULL) +	PHP_FE(mysqli_rpl_query_type,						NULL) +#endif +#if defined(HAVE_EMBEDDED_MYSQLI)  	PHP_FE(mysqli_embedded_server_end,					NULL)  	PHP_FE(mysqli_embedded_server_start,				NULL) +#endif  	PHP_FE(mysqli_errno,								NULL)  	PHP_FE(mysqli_error,								NULL)  	PHP_FE(mysqli_stmt_execute,							NULL) @@ -77,14 +87,22 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_fetch_fields,							NULL)  	PHP_FE(mysqli_fetch_field_direct,					NULL)  	PHP_FE(mysqli_fetch_lengths,						NULL) +#ifdef HAVE_MYSQLND +	PHP_FE(mysqli_fetch_all,							NULL) +#endif  	PHP_FE(mysqli_fetch_array,							NULL)  	PHP_FE(mysqli_fetch_assoc,							NULL)  -	PHP_FE(mysqli_fetch_object,							NULL)  +	PHP_FE(mysqli_fetch_object,							NULL)  	PHP_FE(mysqli_fetch_row,							NULL)  	PHP_FE(mysqli_field_count,							NULL)  	PHP_FE(mysqli_field_seek,							NULL)  	PHP_FE(mysqli_field_tell,							NULL)  	PHP_FE(mysqli_free_result,							NULL) +#if defined(HAVE_MYSQLND) +	PHP_FE(mysqli_get_cache_stats,						NULL) +	PHP_FE(mysqli_get_connection_stats,					NULL) +	PHP_FE(mysqli_get_client_stats,						NULL) +#endif  #ifdef HAVE_MYSQLI_GET_CHARSET   	PHP_FE(mysqli_get_charset,							NULL)  #endif @@ -99,9 +117,10 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_info,									NULL)  	PHP_FE(mysqli_insert_id,							NULL)  	PHP_FE(mysqli_kill,									NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FE(mysqli_set_local_infile_default,				NULL)  	PHP_FE(mysqli_set_local_infile_handler,				NULL) -	PHP_FE(mysqli_master_query,							NULL) +#endif  	PHP_FE(mysqli_more_results,							NULL)  	PHP_FE(mysqli_multi_query, 							NULL)  	PHP_FE(mysqli_next_result,							NULL) @@ -116,9 +135,6 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_real_escape_string,					NULL)  	PHP_FE(mysqli_real_query,							NULL)  	PHP_FE(mysqli_rollback,								NULL) -	PHP_FE(mysqli_rpl_parse_enabled,					NULL) -	PHP_FE(mysqli_rpl_probe,							NULL) -	PHP_FE(mysqli_rpl_query_type,						NULL)  	PHP_FE(mysqli_select_db,							NULL)  #ifdef HAVE_MYSQLI_SET_CHARSET  	PHP_FE(mysqli_set_charset,							NULL) @@ -134,14 +150,17 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_stmt_bind_result,						second_arg_force_by_ref_rest)  	PHP_FE(mysqli_stmt_fetch,							NULL)  	PHP_FE(mysqli_stmt_free_result,						NULL) +#if defined(HAVE_MYSQLND) +	PHP_FE(mysqli_stmt_get_result,						NULL) +#endif  	PHP_FE(mysqli_stmt_get_warnings,					NULL)  	PHP_FE(mysqli_stmt_insert_id,						NULL)  	PHP_FE(mysqli_stmt_reset,							NULL)  	PHP_FE(mysqli_stmt_param_count,						NULL) -	PHP_FE(mysqli_send_query,							NULL) -	PHP_FE(mysqli_slave_query,							NULL)  	PHP_FE(mysqli_sqlstate,   							NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FE(mysqli_ssl_set,								NULL) +#endif  	PHP_FE(mysqli_stat,									NULL)  	PHP_FE(mysqli_stmt_affected_rows,					NULL)  	PHP_FE(mysqli_stmt_close,							NULL) @@ -150,8 +169,8 @@ const zend_function_entry mysqli_functions[] = {  	PHP_FE(mysqli_stmt_error,							NULL)  	PHP_FE(mysqli_stmt_num_rows,						NULL)  	PHP_FE(mysqli_stmt_sqlstate,   						NULL) -	PHP_FE(mysqli_store_result,							NULL)  	PHP_FE(mysqli_stmt_store_result,					NULL) +	PHP_FE(mysqli_store_result,							NULL)  	PHP_FE(mysqli_thread_id,							NULL)  	PHP_FE(mysqli_thread_safe,							NULL)  	PHP_FE(mysqli_use_result,							NULL) @@ -184,23 +203,34 @@ const zend_function_entry mysqli_link_methods[] = {  	PHP_FALIAS(close,mysqli_close,NULL)  	PHP_FALIAS(commit,mysqli_commit,NULL)  	PHP_FALIAS(connect,mysqli_connect,NULL) +	PHP_FALIAS(dump_debug_info,mysqli_dump_debug_info,NULL)  	PHP_FALIAS(debug,mysqli_debug,NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FALIAS(disable_reads_from_master,mysqli_disable_reads_from_master,NULL)  	PHP_FALIAS(disable_rpl_parse,mysqli_disable_rpl_parse,NULL) -	PHP_FALIAS(dump_debug_info,mysqli_dump_debug_info,NULL)  	PHP_FALIAS(enable_reads_from_master,mysqli_enable_reads_from_master,NULL)  	PHP_FALIAS(enable_rpl_parse,mysqli_enable_rpl_parse,NULL) +	PHP_FALIAS(rpl_parse_enabled,mysqli_rpl_parse_enabled,NULL) +	PHP_FALIAS(rpl_probe,mysqli_rpl_probe,NULL) +	PHP_FALIAS(rpl_query_type,mysqli_rpl_query_type,NULL) +	PHP_FALIAS(master_query,mysqli_master_query,NULL) +	PHP_FALIAS(slave_query,mysqli_slave_query,NULL) +#endif  #ifdef HAVE_MYSQLI_GET_CHARSET   	PHP_FALIAS(get_charset,mysqli_get_charset,NULL)  #endif  	PHP_FALIAS(get_client_info,mysqli_get_client_info,NULL) +#if defined(HAVE_MYSQLND) +	PHP_FALIAS(get_connection_stats,mysqli_get_connection_stats,NULL) +#endif  	PHP_FALIAS(get_server_info,mysqli_get_server_info,NULL)  	PHP_FALIAS(get_warnings, mysqli_get_warnings, NULL)  	PHP_FALIAS(init,mysqli_init,NULL)  	PHP_FALIAS(kill,mysqli_kill,NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FALIAS(set_local_infile_default,mysqli_set_local_infile_default,NULL)  	PHP_FALIAS(set_local_infile_handler,mysqli_set_local_infile_handler,NULL) -	PHP_FALIAS(master_query,mysqli_master_query,NULL) +#endif  	PHP_FALIAS(multi_query,mysqli_multi_query,NULL)  	PHP_FALIAS(mysqli,mysqli_connect,NULL)  	PHP_FALIAS(more_results,mysqli_more_results, NULL) @@ -214,16 +244,14 @@ const zend_function_entry mysqli_link_methods[] = {  	PHP_FALIAS(escape_string, mysqli_real_escape_string,NULL)  	PHP_FALIAS(real_query,mysqli_real_query,NULL)  	PHP_FALIAS(rollback,mysqli_rollback,NULL) -	PHP_FALIAS(rpl_parse_enabled,mysqli_rpl_parse_enabled,NULL) -	PHP_FALIAS(rpl_probe,mysqli_rpl_probe,NULL) -	PHP_FALIAS(rpl_query_type,mysqli_rpl_query_type,NULL)  	PHP_FALIAS(select_db,mysqli_select_db,NULL)  #ifdef HAVE_MYSQLI_SET_CHARSET  	PHP_FALIAS(set_charset,mysqli_set_charset,NULL)  #endif  	PHP_FALIAS(set_opt, mysqli_options,NULL) -	PHP_FALIAS(slave_query,mysqli_slave_query,NULL) +#if !defined(HAVE_MYSQLND)  	PHP_FALIAS(ssl_set,mysqli_ssl_set,NULL) +#endif  	PHP_FALIAS(stat,mysqli_stat,NULL)  	PHP_FALIAS(stmt_init,mysqli_stmt_init, NULL)  	PHP_FALIAS(store_result,mysqli_store_result,NULL) @@ -238,18 +266,20 @@ const zend_function_entry mysqli_link_methods[] = {   * Every user visible function must have an entry in mysqli_result_functions[].   */  const zend_function_entry mysqli_result_methods[] = { -	PHP_FALIAS(mysqli_result, mysqli_result_construct, NULL) +	PHP_FALIAS(__construct, mysqli_result_construct, NULL)  	PHP_FALIAS(close,mysqli_free_result,NULL)  	PHP_FALIAS(free,mysqli_free_result,NULL)  	PHP_FALIAS(data_seek,mysqli_data_seek,NULL)  	PHP_FALIAS(fetch_field,mysqli_fetch_field,NULL)  	PHP_FALIAS(fetch_fields,mysqli_fetch_fields,NULL)  	PHP_FALIAS(fetch_field_direct,mysqli_fetch_field_direct,NULL) +#if defined(HAVE_MYSQLND) +	PHP_FALIAS(fetch_all,mysqli_fetch_all,NULL) +#endif  	PHP_FALIAS(fetch_array,mysqli_fetch_array,NULL)  	PHP_FALIAS(fetch_assoc,mysqli_fetch_assoc,NULL)   	PHP_FALIAS(fetch_object,mysqli_fetch_object,NULL)   	PHP_FALIAS(fetch_row,mysqli_fetch_row,NULL) -	PHP_FALIAS(field_count,mysqli_field_count,NULL)  	PHP_FALIAS(field_seek,mysqli_field_seek,NULL)  	PHP_FALIAS(free_result,mysqli_free_result,NULL)  	{NULL, NULL, NULL} @@ -261,7 +291,7 @@ const zend_function_entry mysqli_result_methods[] = {   * Every user visible function must have an entry in mysqli_stmt_functions[].   */  const zend_function_entry mysqli_stmt_methods[] = { -	PHP_FALIAS(mysqli_stmt, mysqli_stmt_construct, NULL) +	PHP_FALIAS(__construct, mysqli_stmt_construct, NULL)  	PHP_FALIAS(attr_get,mysqli_stmt_attr_get,NULL)  	PHP_FALIAS(attr_set,mysqli_stmt_attr_set,NULL)  	PHP_FALIAS(bind_param,mysqli_stmt_bind_param,second_arg_force_by_ref_rest) @@ -279,6 +309,9 @@ const zend_function_entry mysqli_stmt_methods[] = {  	PHP_FALIAS(reset,mysqli_stmt_reset,NULL)  	PHP_FALIAS(prepare,mysqli_stmt_prepare, NULL)  	PHP_FALIAS(store_result,mysqli_stmt_store_result,NULL) +#if defined(HAVE_MYSQLND) +	PHP_FALIAS(get_result,mysqli_stmt_get_result,NULL) +#endif  	{NULL, NULL, NULL}  };  /* }}} */ diff --git a/ext/mysqli/mysqli_libmysql.h b/ext/mysqli/mysqli_libmysql.h new file mode 100644 index 0000000000..1ea1fc3bf4 --- /dev/null +++ b/ext/mysqli/mysqli_libmysql.h @@ -0,0 +1,36 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2007 The PHP Group                                     | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ + +*/ + +/* These are unused */ +#define MYSQLI_CLOSE_EXPLICIT +#define MYSQLI_CLOSE_IMPLICIT +#define MYSQLI_CLOSE_DISCONNECTED +#define MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE	200 +#define MYSQLND_OPT_INT_AND_YEAR_AS_INT				201 + +#define mysqli_result_is_unbuffered(r)		((r)->handle && (r)->handle->status == MYSQL_STATUS_USE_RESULT) +#define mysqli_server_status(c)				(c)->server_status +#define mysqli_stmt_warning_count(s)		mysql_warning_count((s)->mysql) +#define mysqli_stmt_server_status(s)		(s)->mysql->server_status +#define mysqli_stmt_get_connection(s)		(s)->mysql +#define mysqli_close(c, is_forced)			mysql_close((c)) +#define mysqli_stmt_close(c, implicit)		mysql_stmt_close((c)) +#define mysqli_free_result(r, is_forced)	mysql_free_result((r)) diff --git a/ext/mysqli/mysqli_mysqlnd.h b/ext/mysqli/mysqli_mysqlnd.h new file mode 100644 index 0000000000..27032f15c8 --- /dev/null +++ b/ext/mysqli/mysqli_mysqlnd.h @@ -0,0 +1,41 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 5                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ + +*/ + +#ifndef MYSQLI_MYSQLND_H +#define MYSQLI_MYSQLND_H + +#include "ext/mysqlnd/mysqlnd_libmysql_compat.h" + +/* Here comes non-libmysql API to have less ifdefs in mysqli*/ +#define MYSQLI_CLOSE_EXPLICIT                  MYSQLND_CLOSE_EXPLICIT +#define MYSQLI_CLOSE_IMPLICIT                  MYSQLND_CLOSE_IMPLICIT +#define MYSQLI_CLOSE_DISCONNECTED              MYSQLND_CLOSE_DISCONNECTED + +#define mysqli_result_is_unbuffered(r)	((r)->unbuf) +#define mysqli_server_status(c)			(c)->upsert_status.server_status +#define mysqli_stmt_warning_count(s)	mysqlnd_stmt_warning_count((s)) +#define mysqli_stmt_server_status(s)	(s)->upsert_status.server_status +#define mysqli_stmt_get_connection(s)	(s)->conn +#define mysqli_close(c, how)			mysqlnd_close((c), (how)) +#define mysqli_stmt_close(c, implicit)	mysqlnd_stmt_close((c), (implicit)) +#define mysqli_free_result(r, implicit)	mysqlnd_free_result((r), (implicit)) + +#endif diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 2f9ba5325b..918594ad76 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -12,7 +12,9 @@    | obtain it through the world-wide-web, please send a note to          |    | license@php.net so we can mail you a copy immediately.               |    +----------------------------------------------------------------------+ -  | Author: Georg Richter <georg@php.net>                                | +  | Authors: Georg Richter <georg@php.net>                               | +  |          Andrey Hristov <andrey@php.net>                             | +  |          Ulf Wendel <uw@php.net>                                     |    +----------------------------------------------------------------------+    $Id$  @@ -27,46 +29,54 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h" + +#define SAFE_STR(a) ((a)?a:"") +  /* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]])     Open a connection to a mysql server */   PHP_FUNCTION(mysqli_connect)  { -	MY_MYSQL 			*mysql = NULL; -	MYSQLI_RESOURCE 	*mysqli_resource = NULL; -	zval  				*object = getThis(); -	char 				*hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; -	unsigned int 		hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; -	long				port=0; +	MY_MYSQL			*mysql = NULL; +	MYSQLI_RESOURCE		*mysqli_resource = NULL; +	zval				*object = getThis(); +	char				*hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; +	unsigned int		hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; +	zend_bool			persistent = FALSE; +	long				port = 0; +	uint				hash_len; +	char				*hash_key = NULL; +	zend_bool			new_connection = FALSE; +	zend_rsrc_list_entry	*le; +	mysqli_plist_entry *plist = NULL;  	if (getThis() && !ZEND_NUM_ARGS()) {  		RETURN_NULL();  	} +	hostname = username = dbname = passwd = socket = NULL;  	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssls", &hostname, &hostname_len, &username, &username_len,   		&passwd, &passwd_len, &dbname, &dbname_len, &port, &socket, &socket_len) == FAILURE) {  		return;  	} -	if (!socket_len) { -		socket = NULL; +	if (!socket_len || !socket) { +		socket = MyG(default_socket);  	} -     -	/* TODO: safe mode handling */ -	if (PG(sql_safe_mode)){ -	} else { -		if (!passwd) { -			passwd = MyG(default_pw); -			if (!username){ -				username = MyG(default_user); -				if (!hostname) { -					hostname = MyG(default_host); -				} -			} -		} + +	if (!passwd) { +		passwd = MyG(default_pw); +		passwd_len = strlen(SAFE_STR(passwd)); +	} +	if (!username){ +		username = MyG(default_user); +	} +	if (!hostname || !hostname_len) { +		hostname = MyG(default_host);  	} +  	if (object && instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {  		mysqli_resource = ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr;  		if (mysqli_resource && mysqli_resource->ptr && @@ -75,7 +85,7 @@ PHP_FUNCTION(mysqli_connect)  			mysql = (MY_MYSQL*)mysqli_resource->ptr;  			php_clear_mysql(mysql);  			if (mysql->mysql) { -				mysql_close(mysql->mysql); +				mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT);  				mysql->mysql = NULL;  			}  		} @@ -84,61 +94,194 @@ PHP_FUNCTION(mysqli_connect)  		mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL));  	} +	if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) { +		hostname += 2; +		if (!MyG(allow_persistent)) { +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Persistent connections are disabled. Downgrading to normal");			 +		} else { +			mysql->persistent = persistent = TRUE; + +			if (!strlen(hostname)) { +				hostname = MyG(default_host); +			} + +			hash_len = spprintf(&hash_key, 0, "mysqli_%s%ld%s%s%s", SAFE_STR(hostname),  +								port, SAFE_STR(username), SAFE_STR(dbname),  +								SAFE_STR(passwd)); + +			/* check if we can reuse exisiting connection ... */ +			if (zend_hash_find(&EG(persistent_list), hash_key, hash_len + 1, (void **)&le) == SUCCESS) { +				if (Z_TYPE_P(le) == php_le_pmysqli()) { +					plist = (mysqli_plist_entry *) le->ptr; + +					do { +						if (zend_hash_num_elements(&plist->free_links)) { +							HashPosition pos; +							MYSQL **free_mysql; +							ulong idx; +							dtor_func_t pDestructor = plist->free_links.pDestructor; + +							zend_hash_internal_pointer_reset_ex(&plist->free_links, &pos); +							if (SUCCESS != zend_hash_get_current_data_ex(&plist->free_links, +																		(void **)&free_mysql, &pos)) { +								break; +							} +							if (HASH_KEY_IS_LONG != zend_hash_get_current_key_ex(&plist->free_links, NULL, +																				 NULL, &idx, FALSE, &pos)) { +								break; +							} +							plist->free_links.pDestructor = NULL; /* Don't call pDestructor now */ +							if (SUCCESS != zend_hash_index_del(&plist->free_links, idx)) { +								plist->used_links.pDestructor = pDestructor;  /* Restore the destructor */ +								break; +							} +							plist->free_links.pDestructor = pDestructor;  /* Restore the destructor */ +							mysql->mysql = *free_mysql; + +							MyG(num_inactive_persistent)--; +							MyG(num_active_persistent)++; +							/* reset variables */ +							/* todo: option for ping or change_user */ +#if G0 +							if (!mysql_change_user(mysql->mysql, username, passwd, dbname)) { +#else +							if (!mysql_ping(mysql->mysql)) { +#endif +#ifdef HAVE_MYSQLND +								mysqlnd_restart_psession(mysql->mysql); +#endif +								idx = zend_hash_next_free_element(&plist->used_links); +								if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &free_mysql, +																		   sizeof(MYSQL *), NULL)) { +									php_mysqli_dtor_p_elements(free_mysql); +									MyG(num_links)--; +									break; +								} +								mysql->hash_index = idx; +								mysql->hash_key = hash_key; +								goto end; +							} +						} +					} while (0); +				} +			} else { +				zend_rsrc_list_entry le; +				le.type = php_le_pmysqli(); +				le.ptr = plist = calloc(1, sizeof(mysqli_plist_entry)); + +				zend_hash_init(&plist->free_links, MAX(2, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1); +				zend_hash_init(&plist->used_links, MAX(2, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1); +				zend_hash_update(&EG(persistent_list), hash_key, hash_len + 1, (void *)&le, sizeof(le), NULL); +			} +		} +	} + +	if (MyG(max_links) != -1 && MyG(num_links) >= MyG(max_links)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MyG(num_links)); +		goto err; +	} +	if (persistent && MyG(max_persistent) != -1 && +		(MyG(num_active_persistent) + MyG(num_inactive_persistent))>= MyG(max_persistent)) +	{ +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open persistent links (%ld)", +								MyG(num_active_persistent) + MyG(num_inactive_persistent)); +		goto err; +	} + +#if !defined(HAVE_MYSQLND)  	if (!(mysql->mysql = mysql_init(NULL))) { -		efree(mysql); -		RETURN_FALSE; +#else +	if (!(mysql->mysql = mysqlnd_init(persistent))) { +#endif +		goto err;  	} +	new_connection = TRUE;  #ifdef HAVE_EMBEDDED_MYSQLI -	if (strcmp(hostname, ":embedded")) { +	if (hostname_len) {  		unsigned int external=1;  		mysql_options(mysql->mysql, MYSQL_OPT_USE_REMOTE_CONNECTION, (char *)&external);  	} else { -		hostname[0] = '\0';  		mysql_options(mysql->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, 0);  	}  #endif -	if (!socket) { -		socket = MyG(default_socket); -	} - -	if (mysql_real_connect(mysql->mysql,hostname,username,passwd,dbname,port,socket,CLIENT_MULTI_RESULTS) == NULL) { +#if !defined(HAVE_MYSQLND) +	if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname, port, socket, CLIENT_MULTI_RESULTS) == NULL) +#else +	if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len, +						port, socket, CLIENT_MULTI_RESULTS, MyG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL) +#endif +	{  		/* Save error messages */ - -		php_mysqli_throw_sql_exception( mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC, -										"%s", mysql->mysql->net.last_error); -  		php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC); +		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC, +										"%s", mysql_error(mysql->mysql));  		/* free mysql structure */ -		mysql_close(mysql->mysql); -		efree(mysql); -		RETURN_FALSE; +		mysqli_close(mysql->mysql, MYSQLI_CLOSE_DISCONNECTED); +		goto err;  	}  	/* clear error */  	php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC); +#if !defined(HAVE_MYSQLND)  	mysql->mysql->reconnect = MyG(reconnect);  	/* set our own local_infile handler */  	php_set_local_infile_handler_default(mysql); +#endif + +	mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&MyG(allow_local_infile)); +end:  	if (!mysqli_resource) {  		mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  		mysqli_resource->ptr = (void *)mysql;  	}  	mysqli_resource->status = MYSQLI_STATUS_VALID; +	/* store persistent connection */ +	if (persistent && new_connection) { +		/* save persistent connection */ +		ulong hash_index = zend_hash_next_free_element(&plist->used_links); +		if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &mysql->mysql, +												   sizeof(MYSQL *), NULL)) { +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't store persistent connection"); +		} else { +			mysql->hash_index = hash_index; +		} +		MyG(num_active_persistent)++; +	} + +	mysql->hash_key = hash_key; +	MyG(num_links)++; + +#if !defined(HAVE_MYSQLND) +	mysql->multi_query = 0; +#else +	mysql->multi_query = 1; +#endif + +  	if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {  		MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry);	  	} else {  		((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource;  	} +	return; + +err: +	efree(mysql); +	if (persistent) { +		efree(hash_key); +	} +	RETVAL_FALSE;  }  /* }}} */ +  /* {{{ proto int mysqli_connect_errno(void)     Returns the numerical value of the error message from last connect command */  PHP_FUNCTION(mysqli_connect_errno) @@ -159,11 +302,30 @@ PHP_FUNCTION(mysqli_connect_error)  }  /* }}} */ +  /* {{{ proto mixed mysqli_fetch_array (object result [,int resulttype])     Fetch a result row as an associative array, a numeric array, or both */  PHP_FUNCTION(mysqli_fetch_array)   { +#if !defined(HAVE_MYSQLND)  	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); +#else +	MYSQL_RES		*result; +	zval			*mysql_result; +	long			mode = MYSQLND_FETCH_BOTH; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); + +	if (mode < MYSQLI_ASSOC || mode > MYSQLI_BOTH) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH"); +		RETURN_FALSE; +	} + +	mysqlnd_fetch_into(result, mode, return_value, MYSQLND_MYSQLI); +#endif  }  /* }}} */ @@ -171,20 +333,102 @@ PHP_FUNCTION(mysqli_fetch_array)     Fetch a result row as an associative array */  PHP_FUNCTION(mysqli_fetch_assoc)   { +#if !defined(HAVE_MYSQLND)  	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0); +#else +	MYSQL_RES		*result; +	zval			*mysql_result; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); +	mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQLI); +	 +#endif  }  /* }}} */ + +/* {{{ proto mixed mysqli_fetch_all (object result [,int resulttype])  +   Fetches all result rows as an associative array, a numeric array, or both */ +#if defined(HAVE_MYSQLND) +PHP_FUNCTION(mysqli_fetch_all)  +{ +	MYSQL_RES		*result; +	zval			*mysql_result; +	long			mode = MYSQLND_FETCH_NUM; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); + +	if (!mode || (mode & ~MYSQLND_FETCH_BOTH)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Mode can be only MYSQLI_FETCH_NUM, " +		                 "MYSQLI_FETCH_ASSOC or MYSQLI_FETCH_BOTH"); +		RETURN_FALSE; +	} + +	mysqlnd_fetch_all(result, mode, return_value); +} +/* }}} */ + + +/* {{{ proto array mysqli_cache_stats(void) U +   Returns statistics about the zval cache */ +PHP_FUNCTION(mysqli_get_cache_stats)  +{ +	if (ZEND_NUM_ARGS()) { +		WRONG_PARAM_COUNT; +	} +	mysqlnd_palloc_stats(mysqli_mysqlnd_zval_cache, return_value); +} +/* }}} */ + + +/* {{{ proto array mysqli_get_client_stats(void) +   Returns statistics about the zval cache */ +PHP_FUNCTION(mysqli_get_client_stats)  +{ +	if (ZEND_NUM_ARGS()) { +		WRONG_PARAM_COUNT; +	} +	mysqlnd_get_client_stats(return_value); +} +/* }}} */ + + +/* {{{ proto array mysqli_get_connection_stats(void) +   Returns statistics about the zval cache */ +PHP_FUNCTION(mysqli_get_connection_stats)  +{ +	MY_MYSQL	*mysql; +	zval		*mysql_link; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", +									 &mysql_link, mysqli_link_class_entry) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); + +	mysqlnd_get_connection_stats(mysql->mysql, return_value); +} +#endif +/* }}} */ + +  /* {{{ proto mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]])     Fetch a result row as an object */  PHP_FUNCTION(mysqli_fetch_object)   {  	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1);	 +/* todo: mysqlnd support */  }  /* }}} */  /* {{{ proto bool mysqli_multi_query(object link, string query) -   Binary-safe version of mysql_query() */ +   allows to execute multiple queries  */  PHP_FUNCTION(mysqli_multi_query)  {  	MY_MYSQL		*mysql; @@ -199,25 +443,30 @@ PHP_FUNCTION(mysqli_multi_query)  	MYSQLI_ENABLE_MQ;	  	if (mysql_real_query(mysql->mysql, query, query_len)) { +#ifndef HAVE_MYSQLND  		char s_error[MYSQL_ERRMSG_SIZE], s_sqlstate[SQLSTATE_LENGTH+1];  		unsigned int s_errno; -		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); -  		/* we have to save error information, cause   		MYSQLI_DISABLE_MQ will reset error information */  		strcpy(s_error, mysql_error(mysql->mysql));  		strcpy(s_sqlstate, mysql_sqlstate(mysql->mysql));  		s_errno = mysql_errno(mysql->mysql); - +#else +		mysqlnd_error_info error_info = mysql->mysql->error_info; +#endif +		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);  		MYSQLI_DISABLE_MQ; +#ifndef HAVE_MYSQLND  		/* restore error information */  		strcpy(mysql->mysql->net.last_error, s_error);  		strcpy(mysql->mysql->net.sqlstate, s_sqlstate);  		mysql->mysql->net.last_errno = s_errno;	 - +#else +		mysql->mysql->error_info = error_info; +#endif  		RETURN_FALSE; -	}	 +	}  	RETURN_TRUE;  }  /* }}} */ @@ -258,7 +507,7 @@ PHP_FUNCTION(mysqli_query)  	if (!mysql_field_count(mysql->mysql)) {  		/* no result set - not a SELECT */  		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -			php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC); +			php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);  		}  		RETURN_TRUE;  	} @@ -266,13 +515,13 @@ PHP_FUNCTION(mysqli_query)  	result = (resultmode == MYSQLI_USE_RESULT) ? mysql_use_result(mysql->mysql) : mysql_store_result(mysql->mysql);  	if (!result) { -		php_mysqli_throw_sql_exception(mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC, -										"%s", mysql->mysql->net.last_error);  +		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC, +										"%s", mysql_error(mysql->mysql));   		RETURN_FALSE;  	}  	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) { -		php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC); +		php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); @@ -282,6 +531,36 @@ PHP_FUNCTION(mysqli_query)  }  /* }}} */ + +#if defined(HAVE_MYSQLND) +/* {{{ proto object mysqli_stmt_get_result(object link) U +   Buffer result set on client */ +PHP_FUNCTION(mysqli_stmt_get_result) +{ +	MYSQL_RES 		*result; +	MYSQLI_RESOURCE	*mysqli_resource; +	MY_STMT			*stmt; +	zval 			*mysql_stmt; + +	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { +		return; +	} +	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); + +	if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) { +		MYSQLI_REPORT_STMT_ERROR(stmt->stmt); +		RETURN_FALSE; +	} + +	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); +	mysqli_resource->ptr = (void *)result; +	mysqli_resource->status = MYSQLI_STATUS_VALID; +	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);	 +} +/* }}} */ +#endif + +  /* {{{ proto object mysqli_get_warnings(object link) */  PHP_FUNCTION(mysqli_get_warnings)  { @@ -296,12 +575,13 @@ PHP_FUNCTION(mysqli_get_warnings)  	MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);  	if (mysql_warning_count(mysql->mysql)) { -		w = php_get_warnings(mysql->mysql);  +		w = php_get_warnings(mysql->mysql TSRMLS_CC);   	} else {  		RETURN_FALSE;  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = mysqli_resource->info = (void *)w; +	mysqli_resource->status = MYSQLI_STATUS_VALID;  	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);	  }  /* }}} */ @@ -317,15 +597,16 @@ PHP_FUNCTION(mysqli_stmt_get_warnings)  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) {  		return;  	} -	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", 1); +	MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", MYSQLI_STATUS_VALID); -	if (mysql_warning_count(stmt->stmt->mysql)) { -		w = php_get_warnings(stmt->stmt->mysql);  +	if (mysqli_stmt_warning_count(stmt->stmt)) { +		w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt) TSRMLS_CC);   	} else {  		RETURN_FALSE;  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));  	mysqli_resource->ptr = mysqli_resource->info = (void *)w; +	mysqli_resource->status = MYSQLI_STATUS_VALID;  	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);	  }  /* }}} */ @@ -354,13 +635,19 @@ PHP_FUNCTION(mysqli_set_charset)  #endif  #ifdef HAVE_MYSQLI_GET_CHARSET  -/* {{{ proto object mysqli_get_charset(object link)  +/* {{{ proto object mysqli_get_charset(object link) U     returns a character set object */  PHP_FUNCTION(mysqli_get_charset)  {  	MY_MYSQL				*mysql;  	zval					*mysql_link; +	char 					*name = NULL, *collation = NULL, *dir = NULL; +	uint					minlength, maxlength, number, state; +#if !defined(HAVE_MYSQLND)  	MY_CHARSET_INFO			cs; +#else +	const MYSQLND_CHARSET	*cs; +#endif  	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {  		return; @@ -369,16 +656,32 @@ PHP_FUNCTION(mysqli_get_charset)  	object_init(return_value); +#if !defined(HAVE_MYSQLND)  	mysql_get_character_set_info(mysql->mysql, &cs); +	name = (char *)cs.csname; +	collation = (char *)cs.name; +	dir = (char *)cs.dir; +	minlength = cs.mbminlen; +	maxlength = cs.mbmaxlen; +	number = cs.number; +	state = cs.state; +#else +	cs = mysql->mysql->charset; +	name = cs->name;	 +	collation = cs->collation;	 +	minlength = cs->char_minlen; +	maxlength = cs->char_maxlen; +	number = cs->nr; +	state = 1;	/* all charsets are compiled in */ +#endif -	add_property_string(return_value, "charset", (cs.name) ? (char *)cs.csname : "", 1); -	add_property_string(return_value, "collation",(cs.name) ? (char *)cs.name : "", 1); -	add_property_string(return_value, "comment", (cs.comment) ? (char *)cs.comment : "", 1); -	add_property_string(return_value, "dir", (cs.dir) ? (char *)cs.dir : "", 1); -	add_property_long(return_value, "min_length", cs.mbminlen); -	add_property_long(return_value, "max_length", cs.mbmaxlen); -	add_property_long(return_value, "number", cs.number); -	add_property_long(return_value, "state", cs.state); +	add_property_string(return_value, "charset", (name) ? (char *)name : "", 1); +	add_property_string(return_value, "collation",(collation) ? (char *)collation : "", 1); +	add_property_string(return_value, "dir", (dir) ? (char *)dir : "", 1); +	add_property_long(return_value, "min_length", minlength); +	add_property_long(return_value, "max_length", maxlength); +	add_property_long(return_value, "number", number); +	add_property_long(return_value, "state", state);  }  /* }}} */  #endif diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c index fd0b8a21fa..fd8281b551 100644 --- a/ext/mysqli/mysqli_prop.c +++ b/ext/mysqli/mysqli_prop.c @@ -27,7 +27,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  #define CHECK_STATUS(value) \  	if (((MYSQLI_RESOURCE *)obj->ptr)->status < value ) { \ @@ -221,24 +221,23 @@ static int result_type_read(mysqli_object *obj, zval **retval TSRMLS_DC)  static int result_lengths_read(mysqli_object *obj, zval **retval TSRMLS_DC)  {  	MYSQL_RES *p; +	ulong *ret;  	ALLOC_ZVAL(*retval);  	CHECK_STATUS(MYSQLI_STATUS_VALID);  	p = (MYSQL_RES *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; -	if (!p || !p->field_count) { +	if (!p || !p->field_count || !(ret = mysql_fetch_lengths(p))) +	{  		ZVAL_NULL(*retval);  	} else {  		ulong i; -		zval *l;  		array_init(*retval);  		for (i=0; i < p->field_count; i++) { -			MAKE_STD_ZVAL(l); -			ZVAL_LONG(l, p->lengths[i]); -			add_index_zval(*retval, i, l); -		}	 +			add_index_long(*retval, i, ret[i]); +		}  	}  	return SUCCESS;  } @@ -312,7 +311,7 @@ MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_error_read, mysql_stmt_error, MYSQLI_GET_ST  MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_sqlstate_read, mysql_stmt_sqlstate, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED));  /* }}} */ -mysqli_property_entry mysqli_link_property_entries[] = { +const mysqli_property_entry mysqli_link_property_entries[] = {  	{"affected_rows", link_affected_rows_read, NULL},  	{"client_info", link_client_info_read, NULL},  	{"client_version", link_client_version_read, NULL}, @@ -333,7 +332,7 @@ mysqli_property_entry mysqli_link_property_entries[] = {  	{NULL, NULL, NULL}	  }; -mysqli_property_entry mysqli_result_property_entries[] = { +const mysqli_property_entry mysqli_result_property_entries[] = {  	{"current_field", result_current_field_read, NULL},  	{"field_count", result_field_count_read, NULL},  	{"lengths", result_lengths_read, NULL}, @@ -342,7 +341,7 @@ mysqli_property_entry mysqli_result_property_entries[] = {  	{NULL, NULL, NULL}  }; -mysqli_property_entry mysqli_stmt_property_entries[] = { +const mysqli_property_entry mysqli_stmt_property_entries[] = {  	{"affected_rows", stmt_affected_rows_read, NULL},  	{"insert_id", stmt_insert_id_read, NULL},  	{"num_rows", stmt_num_rows_read, NULL}, diff --git a/ext/mysqli/mysqli_repl.c b/ext/mysqli/mysqli_repl.c index b12706fa6b..8096c0239a 100644 --- a/ext/mysqli/mysqli_repl.c +++ b/ext/mysqli/mysqli_repl.c @@ -27,7 +27,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  /* {{{ proto void mysqli_disable_reads_from_master(object link)  */ diff --git a/ext/mysqli/mysqli_report.c b/ext/mysqli/mysqli_report.c index be6e857226..dae2f9242d 100644 --- a/ext/mysqli/mysqli_report.c +++ b/ext/mysqli/mysqli_report.c @@ -25,7 +25,7 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h"  /* {{{ proto bool mysqli_report(int flags)     sets report level */ @@ -45,13 +45,14 @@ PHP_FUNCTION(mysqli_report)  /* }}} */  /* {{{ void php_mysqli_report_error(char *sqlstate, int errorno, char *error) */  -void php_mysqli_report_error(char *sqlstate, int errorno, char *error TSRMLS_DC) { -	php_mysqli_throw_sql_exception(sqlstate, errorno TSRMLS_CC, "%s", error); +void php_mysqli_report_error(const char *sqlstate, int errorno, const char *error TSRMLS_DC) +{ +	php_mysqli_throw_sql_exception((char *)sqlstate, errorno TSRMLS_CC, "%s", error);  }  /* }}} */  /* {{{ void php_mysqli_report_index() */  -void php_mysqli_report_index(char *query, unsigned int status TSRMLS_DC) { +void php_mysqli_report_index(const char *query, unsigned int status TSRMLS_DC) {  	char index[15];  	if (status & SERVER_QUERY_NO_GOOD_INDEX_USED) { diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c index 92dc8ce327..7b02d9117a 100644 --- a/ext/mysqli/mysqli_warning.c +++ b/ext/mysqli/mysqli_warning.c @@ -25,55 +25,144 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_mysqli.h" +#include "php_mysqli_structs.h" + +/* Define these in the PHP5 tree to make merging easy process */ +#define ZSTR_DUPLICATE (1<<0) +#define ZSTR_AUTOFREE  (1<<1) + +#define ZVAL_UTF8_STRING(z, s, flags)          ZVAL_STRING((z), (char*)(s), ((flags) & ZSTR_DUPLICATE)) +#define ZVAL_UTF8_STRINGL(z, s, l, flags)      ZVAL_STRINGL((z), (char*)(s), (l), ((flags) & ZSTR_DUPLICATE)) +  /* {{{ void php_clear_warnings() */  void php_clear_warnings(MYSQLI_WARNING *w)  { -	MYSQLI_WARNING	*n; +	MYSQLI_WARNING *n;  	while (w) {  		n = w; -		efree(w->reason); +		zval_dtor(&(w->reason)); +		zval_dtor(&(w->sqlstate));  		w = w->next;  		efree(n);  	}   }  /* }}} */ + +#ifndef HAVE_MYSQLND  /* {{{ MYSQLI_WARNING *php_new_warning */ -MYSQLI_WARNING *php_new_warning(char *reason, char *sqlstate, int errorno) +static +MYSQLI_WARNING *php_new_warning(const char *reason, int errorno TSRMLS_DC)  { -	MYSQLI_WARNING	*w; +	MYSQLI_WARNING *w;  	w = (MYSQLI_WARNING *)ecalloc(1, sizeof(MYSQLI_WARNING)); -	w->reason = safe_estrdup(reason); -	if (sqlstate) { -		strcpy(w->sqlstate, sqlstate); -	} else { -		strcpy(w->sqlstate, "00000"); -	} +	ZVAL_UTF8_STRING(&(w->reason), reason, ZSTR_DUPLICATE); +	 +	ZVAL_UTF8_STRINGL(&(w->sqlstate), "HY000", sizeof("HY000") - 1,  ZSTR_DUPLICATE); +  	w->errorno = errorno;  	return w;  }  /* }}} */ -/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql) */ -MYSQLI_WARNING *php_get_warnings(MYSQL *mysql) + +/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC) */ +MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC)  { -	MYSQLI_WARNING	*w, *first = NULL, *prev = NULL; +	MYSQLI_WARNING *w, *first = NULL, *prev = NULL;  	MYSQL_RES		*result;  	MYSQL_ROW 		row; -	if (mysql_query(mysql, "SHOW WARNINGS")) { +	if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) {  		return NULL;  	}  	result = mysql_store_result(mysql); +  	while ((row = mysql_fetch_row(result))) { -		w = php_new_warning(row[2], "HY000", atoi(row[1])); +		w = php_new_warning(row[2], atoi(row[1]) TSRMLS_CC); +		if (!first) { +			first = w; +		} +		if (prev) { +			prev->next = w; +		} +		prev = w; +	} +	mysql_free_result(result); +	return first; +} +/* }}} */ +#else +/* {{{ MYSQLI_WARNING *php_new_warning */ +static +MYSQLI_WARNING *php_new_warning(const zval *reason, int errorno TSRMLS_DC) +{ +	MYSQLI_WARNING *w; + +	w = (MYSQLI_WARNING *)ecalloc(1, sizeof(MYSQLI_WARNING)); + +	w->reason = *reason; +	zval_copy_ctor(&(w->reason)); + +	ZVAL_UTF8_STRINGL(&(w->reason),  Z_STRVAL(w->reason), Z_STRLEN(w->reason),  ZSTR_AUTOFREE); +	 +	ZVAL_UTF8_STRINGL(&(w->sqlstate), "HY000", sizeof("HY000") - 1,  ZSTR_DUPLICATE); + +	w->errorno = errorno; + +	return w; +} +/* }}} */ + + +/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC) */ +MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC) +{ +	MYSQLI_WARNING	*w, *first = NULL, *prev = NULL; +	MYSQL_RES		*result; +	zval			*row; + +	if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) { +		return NULL; +	} + +	result = mysql_use_result(mysql); + +	for (;;) { +		zval **entry; +		int errno; + +		MAKE_STD_ZVAL(row); +		mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, row, MYSQLND_MYSQLI); +		if (Z_TYPE_P(row) != IS_ARRAY) { +			zval_ptr_dtor(&row); +			break; +		} +		zend_hash_internal_pointer_reset(Z_ARRVAL_P(row)); +		/* 0. we don't care about the first */ +		zend_hash_move_forward(Z_ARRVAL_P(row)); + +		/* 1. Here comes the error no */ +		zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry); +		convert_to_long_ex(entry); +		errno = Z_LVAL_PP(entry); +		zend_hash_move_forward(Z_ARRVAL_P(row)); + +		/* 2. Here comes the reason */ +		zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry); + +		w = php_new_warning(*entry, errno TSRMLS_CC); +		/* +		  Don't destroy entry, because the row destroy will decrease +		  the refcounter. Decreased twice then mysqlnd_free_result() +		  will crash, because it will try to access already freed memory. +		*/  		if (!first) {  			first = w;  		} @@ -81,11 +170,16 @@ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql)  			prev->next = (void *)w;  		}  		prev = w; + +		zval_ptr_dtor(&row);  	} +  	mysql_free_result(result);  	return first;  }  /* }}} */ +#endif +  /* {{{ bool mysqli_warning::next() */  PHP_METHOD(mysqli_warning, next)  @@ -112,7 +206,9 @@ PHP_METHOD(mysqli_warning, next)  }  /* }}} */ +  /* {{{ property mysqli_warning_message */ +static  int mysqli_warning_message(mysqli_object *obj, zval **retval TSRMLS_DC)  {  	MYSQLI_WARNING *w; @@ -122,17 +218,16 @@ int mysqli_warning_message(mysqli_object *obj, zval **retval TSRMLS_DC)  	}  	w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; -	ALLOC_ZVAL(*retval); -	if (w->reason) { -		ZVAL_STRING(*retval, w->reason, 1); -	} else { -		ZVAL_NULL(*retval); -	} +	MAKE_STD_ZVAL(*retval); +	**retval = w->reason; +	zval_copy_ctor(*retval);  	return SUCCESS;  }  /* }}} */ +  /* {{{ property mysqli_warning_sqlstate */ +static  int mysqli_warning_sqlstate(mysqli_object *obj, zval **retval TSRMLS_DC)  {  	MYSQLI_WARNING *w; @@ -142,13 +237,16 @@ int mysqli_warning_sqlstate(mysqli_object *obj, zval **retval TSRMLS_DC)  	}  	w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; -	ALLOC_ZVAL(*retval); -	ZVAL_STRING(*retval, w->sqlstate, 1); +	MAKE_STD_ZVAL(*retval); +	**retval = w->sqlstate; +	zval_copy_ctor(*retval);  	return SUCCESS;  }  /* }}} */ +  /* {{{ property mysqli_warning_error */ +static  int mysqli_warning_errno(mysqli_object *obj, zval **retval TSRMLS_DC)  {  	MYSQLI_WARNING *w; @@ -157,7 +255,7 @@ int mysqli_warning_errno(mysqli_object *obj, zval **retval TSRMLS_DC)  		return FAILURE;  	}  	w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; -	ALLOC_ZVAL(*retval); +	MAKE_STD_ZVAL(*retval);  	ZVAL_LONG(*retval, w->errorno);  	return SUCCESS;  } @@ -187,20 +285,22 @@ PHP_METHOD(mysqli_warning, __construct)  	} else if (obj->zo.ce == mysqli_stmt_class_entry) {  		MY_STMT *stmt;  		MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &z, "mysqli_stmt", MYSQLI_STATUS_VALID); -		hdl = stmt->stmt->mysql; +		hdl = mysqli_stmt_get_connection(stmt->stmt);  	} else { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid class argument");  		RETURN_FALSE;  	}  	if (mysql_warning_count(hdl)) { -		w = php_get_warnings(hdl);  +		w = php_get_warnings(hdl TSRMLS_CC);   	} else { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No warnings found");  		RETURN_FALSE;  	}  	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); -	mysqli_resource->status = MYSQLI_STATUS_VALID;  	mysqli_resource->ptr = mysqli_resource->info = (void *)w; +	mysqli_resource->status = MYSQLI_STATUS_VALID;  	if (!getThis() || !instanceof_function(Z_OBJCE_P(getThis()), mysqli_warning_class_entry TSRMLS_CC)) {  		MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);	 @@ -211,18 +311,22 @@ PHP_METHOD(mysqli_warning, __construct)  }  /* }}} */ +/* {{{ mysqli_warning_methods */  const zend_function_entry mysqli_warning_methods[] = {  	PHP_ME(mysqli_warning, __construct,		NULL, ZEND_ACC_PROTECTED)  	PHP_ME(mysqli_warning, next, 			NULL, ZEND_ACC_PUBLIC)  	{NULL, NULL, NULL}  }; +/* }}} */ -mysqli_property_entry mysqli_warning_property_entries[] = { +/* {{{ mysqli_warning_property_entries */ +const mysqli_property_entry mysqli_warning_property_entries[] = {  	{"message", mysqli_warning_message, NULL},  	{"sqlstate", mysqli_warning_sqlstate, NULL},  	{"errno", mysqli_warning_errno, NULL},  	{NULL, NULL, NULL}  }; +/* }}} */  /*   * Local variables: diff --git a/ext/mysqli/php_mysqli.h b/ext/mysqli/php_mysqli.h index 965bec7082..d166d00f52 100644 --- a/ext/mysqli/php_mysqli.h +++ b/ext/mysqli/php_mysqli.h @@ -12,299 +12,17 @@    | obtain it through the world-wide-web, please send a note to          |    | license@php.net so we can mail you a copy immediately.               |    +----------------------------------------------------------------------+ -  | Author: Georg Richter <georg@php.net>                                | +  | Authors: Georg Richter <georg@php.net>                               | +  |          Andrey Hristov <andrey@php.net>                             | +  |          Ulf Wendel <uw@php.net>                                     |    +----------------------------------------------------------------------+    $Id$   */ -/* A little hack to prevent build break, when mysql is used together with - * c-client, which also defines LIST. - */ -#ifdef LIST -#undef LIST -#endif - -#include <mysql.h> - -/* character set support */ -#if MYSQL_VERSION_ID > 50009 -#define HAVE_MYSQLI_GET_CHARSET -#endif - -#if (MYSQL_VERSION_ID > 40112 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID > 50005 -#define HAVE_MYSQLI_SET_CHARSET -#endif - - -#include <errmsg.h> -  #ifndef PHP_MYSQLI_H  #define PHP_MYSQLI_H -#define MYSQLI_VERSION_ID		101009 - -enum mysqli_status { -	MYSQLI_STATUS_UNKNOWN=0, -	MYSQLI_STATUS_CLEARED, -	MYSQLI_STATUS_INITIALIZED, -	MYSQLI_STATUS_VALID -}; - -typedef struct { -	ulong		buflen; -	char		*val; -	ulong		type; -} VAR_BUFFER; - -typedef struct { -	unsigned int	var_cnt; -	VAR_BUFFER		*buf; -	zval			**vars; -	char			*is_null; -} BIND_BUFFER; - -typedef struct { -	MYSQL_STMT			*stmt; -	BIND_BUFFER			param; -	BIND_BUFFER			result; -	char				*query; -} MY_STMT; - -typedef struct { -	MYSQL				*mysql; -	zval				*li_read; -	php_stream			*li_stream; -	unsigned int 		multi_query;	 -} MY_MYSQL; - -typedef struct { -	int			mode; -	int			socket; -	FILE		*fp; -} PROFILER; - -typedef struct { -	void				*ptr;		/* resource: (mysql, result, stmt)   */ -	void				*info;		/* additional buffer				 */ -    enum mysqli_status	status; -} MYSQLI_RESOURCE; - -typedef struct _mysqli_object { -	zend_object 		zo; -	void 				*ptr; -	HashTable 			*prop_handler; -} mysqli_object; /* extends zend_object */ - -typedef struct { -	char			*reason; -	char			sqlstate[6]; -	int				errorno; -   	void			*next; -} MYSQLI_WARNING; - -typedef struct _mysqli_property_entry { -	char *pname; -	int (*r_func)(mysqli_object *obj, zval **retval TSRMLS_DC); -	int (*w_func)(mysqli_object *obj, zval *value TSRMLS_DC); -} mysqli_property_entry; - -typedef struct { -	char	error_msg[LOCAL_INFILE_ERROR_LEN]; -	void	*userdata; -} mysqli_local_infile; - -#define phpext_mysqli_ptr &mysqli_module_entry - -#ifdef PHP_WIN32 -#define PHP_MYSQLI_API __declspec(dllexport) -#define MYSQLI_LLU_SPEC "%I64u" -#define MYSQLI_LL_SPEC "%I64d" -#else -#define PHP_MYSQLI_API -#define MYSQLI_LLU_SPEC "%llu" -#define MYSQLI_LL_SPEC "%lld" -#endif - -#ifdef ZTS -#include "TSRM.h" -#endif - -#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type - -extern zend_module_entry mysqli_module_entry; -extern const zend_function_entry mysqli_functions[]; -extern const zend_function_entry mysqli_link_methods[]; -extern const zend_function_entry mysqli_stmt_methods[]; -extern const zend_function_entry mysqli_result_methods[]; -extern const zend_function_entry mysqli_driver_methods[]; -extern const zend_function_entry mysqli_warning_methods[]; -extern const zend_function_entry mysqli_exception_methods[]; - -extern mysqli_property_entry mysqli_link_property_entries[]; -extern mysqli_property_entry mysqli_result_property_entries[]; -extern mysqli_property_entry mysqli_stmt_property_entries[]; -extern mysqli_property_entry mysqli_driver_property_entries[]; -extern mysqli_property_entry mysqli_warning_property_entries[]; - -extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag, int into_object); -extern void php_clear_stmt_bind(MY_STMT *stmt); -extern void php_clear_mysql(MY_MYSQL *); -extern MYSQLI_WARNING *php_get_warnings(MYSQL *mysql); -extern void php_clear_warnings(MYSQLI_WARNING *w); -extern void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type); -extern void php_mysqli_report_error(char *sqlstate, int errorno, char *error TSRMLS_DC); -extern void php_mysqli_report_index(char *query, unsigned int status TSRMLS_DC); -extern int php_local_infile_init(void **, const char *, void *); -extern int php_local_infile_read(void *, char *, uint); -extern void php_local_infile_end(void *); -extern int php_local_infile_error(void *, char *, uint); -extern void php_set_local_infile_handler_default(MY_MYSQL *); -extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno TSRMLS_DC, char *format, ...); -extern zend_class_entry *mysqli_link_class_entry; -extern zend_class_entry *mysqli_stmt_class_entry; -extern zend_class_entry *mysqli_result_class_entry; -extern zend_class_entry *mysqli_driver_class_entry; -extern zend_class_entry *mysqli_warning_class_entry; -extern zend_class_entry *mysqli_exception_class_entry; - -#ifdef HAVE_SPL -extern PHPAPI zend_class_entry *spl_ce_RuntimeException; -#endif - -PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRMLS_DC); - -#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \ -	mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \ -	mysql->multi_query = 0; \ -}  - -#define MYSQLI_ENABLE_MQ if (!mysql->multi_query) { \ -	mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); \ -	mysql->multi_query = 1; \ -}  - -#define REGISTER_MYSQLI_CLASS_ENTRY(name, mysqli_entry, class_functions) { \ -	zend_class_entry ce; \ -	INIT_CLASS_ENTRY(ce, name,class_functions); \ -	ce.create_object = mysqli_objects_new; \ -	mysqli_entry = zend_register_internal_class(&ce TSRMLS_CC); \ -} \ - -#define MYSQLI_REGISTER_RESOURCE_EX(__ptr, __zval)  \ -	((mysqli_object *) zend_object_store_get_object(__zval TSRMLS_CC))->ptr = __ptr; \ - -#define MYSQLI_RETURN_RESOURCE(__ptr, __ce) \ -	Z_TYPE_P(return_value) = IS_OBJECT; \ -	(return_value)->value.obj = mysqli_objects_new(__ce TSRMLS_CC); \ -	MYSQLI_REGISTER_RESOURCE_EX(__ptr, return_value) - -#define MYSQLI_REGISTER_RESOURCE(__ptr, __ce) \ -{\ -	zval *object = getThis();\ -	if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {\ -		object = return_value;\ -		Z_TYPE_P(object) = IS_OBJECT;\ -		(object)->value.obj = mysqli_objects_new(__ce TSRMLS_CC);\ -	}\ -	MYSQLI_REGISTER_RESOURCE_EX(__ptr, object)\ -} - -#define MYSQLI_FETCH_RESOURCE(__ptr, __type, __id, __name, __check) \ -{ \ -	MYSQLI_RESOURCE *my_res; \ -	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ -	if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {\ -  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", intern->zo.ce->name);\ -  		RETURN_NULL();\ -  	}\ -	__ptr = (__type)my_res->ptr; \ -	if (__check && my_res->status < __check) { \ -		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid object or resource %s\n", intern->zo.ce->name); \ -		RETURN_NULL();\ -	}\ -}  - -#define MYSQLI_SET_STATUS(__id, __value) \ -{ \ -	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ -	((MYSQLI_RESOURCE *)intern->ptr)->status = __value; \ -} \ - -#define MYSQLI_CLEAR_RESOURCE(__id) \ -{ \ -	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ -	efree(intern->ptr); \ -	intern->ptr = NULL; \ -} - -#define MYSQLI_RETURN_LONG_LONG(__val) \ -{ \ -	if ((__val) < LONG_MAX) {		\ -		RETURN_LONG((long) (__val));		\ -	} else {				\ -		char *ret;			\ -		/* always used with my_ulonglong -> %llu */ \ -		int l = spprintf(&ret, 0, "%llu", (__val));	\ -		RETURN_STRINGL(ret, l, 0);		\ -	}					\ -} - -#define MYSQLI_ADD_PROPERTIES(a,b) \ -{ \ -	int i = 0; \ -	while (b[i].pname != NULL) { \ -		mysqli_add_property(a, b[i].pname, (mysqli_read_t)b[i].r_func, (mysqli_write_t)b[i].w_func TSRMLS_CC); \ -		i++; \ -	}\ -} - -#if WIN32|WINNT -#define SCLOSE(a) closesocket(a) -#else -#define SCLOSE(a) close(a) -#endif - -#define MYSQLI_STORE_RESULT 0 -#define MYSQLI_USE_RESULT 	1 - -/* for mysqli_fetch_assoc */ -#define MYSQLI_ASSOC	1 -#define MYSQLI_NUM		2 -#define MYSQLI_BOTH		3 - -/* for mysqli_bind_param */ -#define MYSQLI_BIND_INT		1 -#define MYSQLI_BIND_DOUBLE	2 -#define MYSQLI_BIND_STRING	3 -#define MYSQLI_BIND_SEND_DATA	4 - -/* fetch types */ -#define FETCH_SIMPLE		1 -#define FETCH_RESULT		2 - -/*** REPORT MODES ***/ -#define MYSQLI_REPORT_OFF           0 -#define MYSQLI_REPORT_ERROR			1 -#define MYSQLI_REPORT_STRICT		2 -#define MYSQLI_REPORT_INDEX			4 -#define MYSQLI_REPORT_CLOSE			8	 -#define MYSQLI_REPORT_ALL		  255 - -#define MYSQLI_REPORT_MYSQL_ERROR(mysql) \ -if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql->net.last_errno) { \ -	php_mysqli_report_error(mysql->net.sqlstate, mysql->net.last_errno, mysql->net.last_error TSRMLS_CC); \ -} - -#define MYSQLI_REPORT_STMT_ERROR(stmt) \ -if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && stmt->last_errno) { \ -	php_mysqli_report_error(stmt->sqlstate, stmt->last_errno, stmt->last_error TSRMLS_CC); \ -} - -PHP_MYSQLI_API void mysqli_register_link(zval *return_value, void *link TSRMLS_DC); -PHP_MYSQLI_API void mysqli_register_stmt(zval *return_value, void *stmt TSRMLS_DC); -PHP_MYSQLI_API void mysqli_register_result(zval *return_value, void *result TSRMLS_DC); -PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRMLS_DC);  PHP_MINIT_FUNCTION(mysqli);  PHP_MSHUTDOWN_FUNCTION(mysqli); @@ -317,9 +35,7 @@ PHP_FUNCTION(mysqli_affected_rows);  PHP_FUNCTION(mysqli_autocommit);  PHP_FUNCTION(mysqli_change_user);  PHP_FUNCTION(mysqli_character_set_name); -#ifdef HAVE_MYSQLI_SET_CHARSET  PHP_FUNCTION(mysqli_set_charset); -#endif  PHP_FUNCTION(mysqli_close);  PHP_FUNCTION(mysqli_commit);  PHP_FUNCTION(mysqli_connect); @@ -334,6 +50,7 @@ PHP_FUNCTION(mysqli_enable_reads_from_master);  PHP_FUNCTION(mysqli_enable_rpl_parse);  PHP_FUNCTION(mysqli_errno);  PHP_FUNCTION(mysqli_error); +PHP_FUNCTION(mysqli_fetch_all);  PHP_FUNCTION(mysqli_fetch_array);  PHP_FUNCTION(mysqli_fetch_assoc);  PHP_FUNCTION(mysqli_fetch_object); @@ -346,9 +63,10 @@ PHP_FUNCTION(mysqli_field_count);  PHP_FUNCTION(mysqli_field_seek);  PHP_FUNCTION(mysqli_field_tell);  PHP_FUNCTION(mysqli_free_result); -#ifdef HAVE_MYSQLI_GET_CHARSET  +PHP_FUNCTION(mysqli_get_cache_stats); +PHP_FUNCTION(mysqli_get_client_stats); +PHP_FUNCTION(mysqli_get_connection_stats);  PHP_FUNCTION(mysqli_get_charset); -#endif  PHP_FUNCTION(mysqli_get_client_info);  PHP_FUNCTION(mysqli_get_client_version);  PHP_FUNCTION(mysqli_get_host_info); @@ -408,6 +126,7 @@ PHP_FUNCTION(mysqli_stmt_data_seek);  PHP_FUNCTION(mysqli_stmt_errno);  PHP_FUNCTION(mysqli_stmt_error);  PHP_FUNCTION(mysqli_stmt_free_result); +PHP_FUNCTION(mysqli_stmt_get_result);  PHP_FUNCTION(mysqli_stmt_get_warnings);  PHP_FUNCTION(mysqli_stmt_reset);  PHP_FUNCTION(mysqli_stmt_insert_id); @@ -425,53 +144,11 @@ ZEND_FUNCTION(mysqli_result_construct);  ZEND_FUNCTION(mysqli_driver_construct);  ZEND_METHOD(mysqli_warning,__construct); -ZEND_BEGIN_MODULE_GLOBALS(mysqli) -	long			default_link; -	long			num_links; -	long			max_links; -	unsigned int	default_port; -	char			*default_host; -	char			*default_user; -	char			*default_socket; -	char            *default_pw; -	int				reconnect; -	int				strict; -	long			error_no; -	char			*error_msg; -	int				report_mode; -	HashTable		*report_ht; -	unsigned int	multi_query; -	unsigned int	embedded; -ZEND_END_MODULE_GLOBALS(mysqli) - - -#define MYSQLI_PROPERTY(a) extern int a(mysqli_object *obj, zval **retval TSRMLS_DC) - -MYSQLI_PROPERTY(my_prop_link_host); - -#ifdef ZTS -#define MyG(v) TSRMG(mysqli_globals_id, zend_mysqli_globals *, v) -#else -#define MyG(v) (mysqli_globals.v) -#endif - -#define my_estrdup(x) (x) ? estrdup(x) : NULL -#define my_efree(x) if (x) efree(x) - -#ifdef PHP_WIN32 -#define L64(x) x##i64 -typedef __int64 my_longlong; -#else -#define L64(x) x##LL -typedef long long my_longlong; -#endif - - -ZEND_EXTERN_MODULE_GLOBALS(mysqli) +#define phpext_mysqli_ptr &mysqli_module_entry +extern zend_module_entry mysqli_module_entry;  #endif	/* PHP_MYSQLI.H */ -  /*   * Local variables:   * tab-width: 4 diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h new file mode 100644 index 0000000000..5c6e23f4f3 --- /dev/null +++ b/ext/mysqli/php_mysqli_structs.h @@ -0,0 +1,396 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 5                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 1997-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Author: Georg Richter <georg@php.net>                                | +  +----------------------------------------------------------------------+ + +  $Id$  +*/ + +#ifndef PHP_MYSQLI_STRUCTS_H +#define PHP_MYSQLI_STRUCTS_H + +/* A little hack to prevent build break, when mysql is used together with + * c-client, which also defines LIST. + */ +#ifdef LIST +#undef LIST +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef HAVE_MYSQLND +#include "ext/mysqlnd/mysqlnd.h" +#include "ext/mysqli/mysqli_mysqlnd.h" +#else +#include <mysql.h> +#include <errmsg.h> +#include "ext/mysqli/mysqli_libmysql.h" +#endif + +#include "php_mysqli.h" + +/* character set support */ +#if defined(MYSQLND_VERSION_ID) || MYSQL_VERSION_ID > 50009 +#define HAVE_MYSQLI_GET_CHARSET +#endif + +#if defined(MYSQLND_VERSION_ID) || (MYSQL_VERSION_ID > 40112 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID > 50005 +#define HAVE_MYSQLI_SET_CHARSET +#endif + +#define MYSQLI_VERSION_ID		101009 + +enum mysqli_status { +	MYSQLI_STATUS_UNKNOWN=0, +	MYSQLI_STATUS_CLEARED, +	MYSQLI_STATUS_INITIALIZED, +	MYSQLI_STATUS_VALID +}; + +typedef struct { +	ulong		buflen; +	char		*val; +	ulong		type; +} VAR_BUFFER; + +typedef struct { +	unsigned int	var_cnt; +	VAR_BUFFER		*buf; +	zval			**vars; +	char			*is_null; +} BIND_BUFFER; + +typedef struct { +	MYSQL_STMT	*stmt; +	BIND_BUFFER	param; +	BIND_BUFFER	result; +	char		*query; +} MY_STMT; + +typedef struct { +	MYSQL			*mysql; +	char			*hash_key; +	zval			*li_read; +	php_stream		*li_stream; +	zend_bool		persistent; +	unsigned long	hash_index; /* Used when persistent, hold the index in plist->used_links */ +	unsigned int 	multi_query; +} MY_MYSQL; + +typedef struct { +	int			mode; +	int			socket; +	FILE		*fp; +} PROFILER; + +typedef struct { +	void				*ptr;		/* resource: (mysql, result, stmt)   */ +	void				*info;		/* additional buffer				 */ +	enum mysqli_status	status;		/* object status */ +} MYSQLI_RESOURCE; + +typedef struct _mysqli_object { +	zend_object 		zo; +	void 				*ptr; +	HashTable 			*prop_handler; +} mysqli_object; /* extends zend_object */ + +typedef struct st_mysqli_warning MYSQLI_WARNING; + +struct st_mysqli_warning { +	zval	reason; +	zval	sqlstate; +	int		errorno; +   	MYSQLI_WARNING	*next; +}; + +typedef struct _mysqli_property_entry { +	char *pname; +	int (*r_func)(mysqli_object *obj, zval **retval TSRMLS_DC); +	int (*w_func)(mysqli_object *obj, zval *value TSRMLS_DC); +} mysqli_property_entry; + +#if !defined(HAVE_MYSQLND) +typedef struct { +	char	error_msg[LOCAL_INFILE_ERROR_LEN]; +	void	*userdata; +} mysqli_local_infile; +#endif + +typedef struct { +	HashTable free_links; +	HashTable used_links; +} mysqli_plist_entry; + +#ifdef PHP_WIN32 +#define PHP_MYSQLI_API __declspec(dllexport) +#define MYSQLI_LLU_SPEC "%I64u" +#define MYSQLI_LL_SPEC "%I64d" +#define L64(x) x##i64 +typedef __int64 my_longlong; +#else +#define PHP_MYSQLI_API +#define MYSQLI_LLU_SPEC "%llu" +#define MYSQLI_LL_SPEC "%lld" +#define L64(x) x##LL +typedef long long my_longlong; +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type + +extern const zend_function_entry mysqli_functions[]; +extern const zend_function_entry mysqli_link_methods[]; +extern const zend_function_entry mysqli_stmt_methods[]; +extern const zend_function_entry mysqli_result_methods[]; +extern const zend_function_entry mysqli_driver_methods[]; +extern const zend_function_entry mysqli_warning_methods[]; +extern const zend_function_entry mysqli_exception_methods[]; + +extern const mysqli_property_entry mysqli_link_property_entries[]; +extern const mysqli_property_entry mysqli_result_property_entries[]; +extern const mysqli_property_entry mysqli_stmt_property_entries[]; +extern const mysqli_property_entry mysqli_driver_property_entries[]; +extern const mysqli_property_entry mysqli_warning_property_entries[]; + +#ifdef HAVE_MYSQLND +extern MYSQLND_ZVAL_PCACHE	*mysqli_mysqlnd_zval_cache; +extern MYSQLND_QCACHE		*mysqli_mysqlnd_qcache; +#endif + +extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag, int into_object); +extern void php_clear_stmt_bind(MY_STMT *stmt TSRMLS_DC); +extern void php_clear_mysql(MY_MYSQL *); +extern MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC); +extern void php_clear_warnings(MYSQLI_WARNING *w); +extern void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type); +extern void php_mysqli_report_error(const char *sqlstate, int errorno, const char *error TSRMLS_DC); +extern void php_mysqli_report_index(const char *query, unsigned int status TSRMLS_DC); +extern int php_local_infile_init(void **, const char *, void *); +extern int php_local_infile_read(void *, char *, uint); +extern void php_local_infile_end(void *); +extern int php_local_infile_error(void *, char *, uint); +extern void php_set_local_infile_handler_default(MY_MYSQL *); +extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno TSRMLS_DC, char *format, ...); +extern zend_class_entry *mysqli_link_class_entry; +extern zend_class_entry *mysqli_stmt_class_entry; +extern zend_class_entry *mysqli_result_class_entry; +extern zend_class_entry *mysqli_driver_class_entry; +extern zend_class_entry *mysqli_warning_class_entry; +extern zend_class_entry *mysqli_exception_class_entry; +extern int php_le_pmysqli(void); +extern void php_mysqli_dtor_p_elements(void *data); + +#ifdef HAVE_SPL +extern PHPAPI zend_class_entry *spl_ce_RuntimeException; +#endif + +PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRMLS_DC); + +#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \ +	mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \ +	mysql->multi_query = 0; \ +}  + +#define MYSQLI_ENABLE_MQ if (!mysql->multi_query) { \ +	mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); \ +	mysql->multi_query = 1; \ +}  + +#define REGISTER_MYSQLI_CLASS_ENTRY(name, mysqli_entry, class_functions) { \ +	zend_class_entry ce; \ +	INIT_CLASS_ENTRY(ce, name,class_functions); \ +	ce.create_object = mysqli_objects_new; \ +	mysqli_entry = zend_register_internal_class(&ce TSRMLS_CC); \ +} \ + +#define MYSQLI_REGISTER_RESOURCE_EX(__ptr, __zval)  \ +	((mysqli_object *) zend_object_store_get_object(__zval TSRMLS_CC))->ptr = __ptr; + +#define MYSQLI_RETURN_RESOURCE(__ptr, __ce) \ +	Z_TYPE_P(return_value) = IS_OBJECT; \ +	(return_value)->value.obj = mysqli_objects_new(__ce TSRMLS_CC); \ +	MYSQLI_REGISTER_RESOURCE_EX(__ptr, return_value) + +#define MYSQLI_REGISTER_RESOURCE(__ptr, __ce) \ +{\ +	zval *object = getThis();\ +	if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {\ +		object = return_value;\ +		Z_TYPE_P(object) = IS_OBJECT;\ +		(object)->value.obj = mysqli_objects_new(__ce TSRMLS_CC);\ +	}\ +	MYSQLI_REGISTER_RESOURCE_EX(__ptr, object)\ +} + +#define MYSQLI_FETCH_RESOURCE(__ptr, __type, __id, __name, __check) \ +{ \ +	MYSQLI_RESOURCE *my_res; \ +	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ +	if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {\ +  		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", intern->zo.ce->name);\ +  		RETURN_NULL();\ +  	}\ +	__ptr = (__type)my_res->ptr; \ +	if (__check && my_res->status < __check) { \ +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid object or resource %s\n", intern->zo.ce->name); \ +		RETURN_NULL();\ +	}\ +} + +#define MYSQLI_SET_STATUS(__id, __value) \ +{ \ +	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ +	((MYSQLI_RESOURCE *)intern->ptr)->status = __value; \ +} \ + +#define MYSQLI_CLEAR_RESOURCE(__id) \ +{ \ +	mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\ +	efree(intern->ptr); \ +	intern->ptr = NULL; \ +} + +#define MYSQLI_RETURN_LONG_LONG(__val) \ +{ \ +	if ((__val) < LONG_MAX) {		\ +		RETURN_LONG((__val));		\ +	} else {				\ +		char *ret;			\ +		int l = spprintf(&ret, 0, "%llu", (__val));	\ +		RETURN_STRINGL(ret, l, 0);		\ +	}					\ +} + +#define MYSQLI_ADD_PROPERTIES(a,b) \ +{ \ +	int i = 0; \ +	while (b[i].pname != NULL) { \ +		mysqli_add_property(a, b[i].pname, (mysqli_read_t)b[i].r_func, (mysqli_write_t)b[i].w_func TSRMLS_CC); \ +		i++; \ +	}\ +} + +#if WIN32|WINNT +#define SCLOSE(a) closesocket(a) +#else +#define SCLOSE(a) close(a) +#endif + +#define MYSQLI_STORE_RESULT 0 +#define MYSQLI_USE_RESULT 	1 + +/* for mysqli_fetch_assoc */ +#define MYSQLI_ASSOC	1 +#define MYSQLI_NUM		2 +#define MYSQLI_BOTH		3 + +/* for mysqli_bind_param */ +#define MYSQLI_BIND_INT		1 +#define MYSQLI_BIND_DOUBLE	2 +#define MYSQLI_BIND_STRING	3 +#define MYSQLI_BIND_SEND_DATA	4 + +/* fetch types */ +#define FETCH_SIMPLE		1 +#define FETCH_RESULT		2 + +/*** REPORT MODES ***/ +#define MYSQLI_REPORT_OFF           0 +#define MYSQLI_REPORT_ERROR			1 +#define MYSQLI_REPORT_STRICT		2 +#define MYSQLI_REPORT_INDEX			4 +#define MYSQLI_REPORT_CLOSE			8	 +#define MYSQLI_REPORT_ALL		  255 + +#define MYSQLI_REPORT_MYSQL_ERROR(mysql) \ +if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql_errno(mysql)) { \ +	php_mysqli_report_error(mysql_sqlstate(mysql), mysql_errno(mysql), mysql_error(mysql) TSRMLS_CC); \ +} + +#define MYSQLI_REPORT_STMT_ERROR(stmt) \ +if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql_stmt_errno(stmt)) { \ +	php_mysqli_report_error(mysql_stmt_sqlstate(stmt), mysql_stmt_errno(stmt), mysql_stmt_error(stmt) TSRMLS_CC); \ +} + +PHP_MYSQLI_API void mysqli_register_link(zval *return_value, void *link TSRMLS_DC); +PHP_MYSQLI_API void mysqli_register_stmt(zval *return_value, void *stmt TSRMLS_DC); +PHP_MYSQLI_API void mysqli_register_result(zval *return_value, void *result TSRMLS_DC); +PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRMLS_DC); + +ZEND_BEGIN_MODULE_GLOBALS(mysqli) +	long			default_link; +	long			num_links; +	long			max_links; +	long 			num_active_persistent; +	long 			num_inactive_persistent; +	long			max_persistent; +	long			allow_persistent; +	long			cache_size; +	unsigned long	default_port; +	char			*default_host; +	char			*default_user; +	char			*default_socket; +	char			*default_pw; +	long			reconnect; +	long			allow_local_infile; +	long			strict; +	long			error_no; +	char			*error_msg; +	long			report_mode; +	HashTable		*report_ht; +	unsigned long	multi_query; +	unsigned long	embedded; +#ifdef HAVE_MYSQLND +	MYSQLND_THD_ZVAL_PCACHE	*mysqlnd_thd_zval_cache; +#endif +ZEND_END_MODULE_GLOBALS(mysqli) + +#define MYSQLI_PROPERTY(a) extern int a(mysqli_object *obj, zval **retval TSRMLS_DC) + +MYSQLI_PROPERTY(my_prop_link_host); + +#ifdef ZTS +#define MyG(v) TSRMG(mysqli_globals_id, zend_mysqli_globals *, v) +#else +#define MyG(v) (mysqli_globals.v) +#endif + +#define my_estrdup(x) (x) ? estrdup(x) : NULL +#define my_efree(x) if (x) efree(x) + +ZEND_EXTERN_MODULE_GLOBALS(mysqli) + +#endif	/* PHP_MYSQLI_STRUCTS.H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/CREDITS b/ext/mysqlnd/CREDITS new file mode 100644 index 0000000000..bac8d1a4a2 --- /dev/null +++ b/ext/mysqlnd/CREDITS @@ -0,0 +1,2 @@ +MySQLnd +Georg Richter, Andrey Hristov, Ulf Wendel diff --git a/ext/mysqlnd/config-win.h b/ext/mysqlnd/config-win.h new file mode 100644 index 0000000000..86018221a6 --- /dev/null +++ b/ext/mysqlnd/config-win.h @@ -0,0 +1,98 @@ +/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB  +This file is public domain and comes with NO WARRANTY of any kind */ + +/* Defines for Win32 to make it compatible for MySQL */ + +#include <sys/locking.h> +#include <windows.h> +#include <math.h>			/* Because of rint() */ +#include <fcntl.h> +#include <io.h> +#include <malloc.h> + +#if defined(__NT__) +#define	SYSTEM_TYPE	"NT" +#elif defined(__WIN2000__) +#define	SYSTEM_TYPE	"WIN2000" +#else +#define	SYSTEM_TYPE	"Win95/Win98" +#endif + +#ifdef _WIN64 +#define MACHINE_TYPE	"ia64"		/* Define to machine type name */ +#else +#define MACHINE_TYPE	"i32"		/* Define to machine type name */ +#ifndef _WIN32 +#define _WIN32				/* Compatible with old source */ +#endif +#ifndef __WIN32__ +#define __WIN32__ +#endif +#endif /* _WIN64 */ +#ifndef __WIN__ +#define __WIN__                       /* To make it easier in VC++ */ +#endif + +#define LONGLONG_MIN	((__int64) 0x8000000000000000) +#define LONGLONG_MAX	((__int64) 0x7FFFFFFFFFFFFFFF) +#define LL(A)		((__int64) A) + +/* Type information */ + +typedef unsigned short  ushort; +typedef unsigned int    uint; +typedef unsigned __int64 ulonglong;	/* Microsofts 64 bit types */ +typedef __int64	longlong; +typedef int sigset_t; +#define longlong_defined + +#define SIZEOF_CHAR		1 +#define SIZEOF_LONG		4 +#define SIZEOF_LONG_LONG	8 + + +#ifndef _WIN64 +/* Optimized store functions for Intel x86 */ + +#define sint2korr(A)	(*((int16 *) (A))) +#define sint3korr(A)	((int32) ((((uchar) (A)[2]) & 128) ? \ +				  (((uint32) 255L << 24) | \ +				   (((uint32) (uchar) (A)[2]) << 16) |\ +				   (((uint32) (uchar) (A)[1]) << 8) | \ +				   ((uint32) (uchar) (A)[0])) : \ +				  (((uint32) (uchar) (A)[2]) << 16) |\ +				  (((uint32) (uchar) (A)[1]) << 8) | \ +				  ((uint32) (uchar) (A)[0]))) +#define sint4korr(A)	(*((long *) (A))) +#define uint2korr(A)	(*((uint16 *) (A))) +#define uint3korr(A)	(long) (*((unsigned long *) (A)) & 0xFFFFFF) +#define uint4korr(A)	(*((unsigned long *) (A))) +#define uint5korr(A)	((ulonglong)(((uint32) ((uchar) (A)[0])) +\ +				    (((uint32) ((uchar) (A)[1])) << 8) +\ +				    (((uint32) ((uchar) (A)[2])) << 16) +\ +				    (((uint32) ((uchar) (A)[3])) << 24)) +\ +			 	    (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint8korr(A)	(*((ulonglong *) (A))) +#define sint8korr(A)	(*((longlong *) (A))) +#define int2store(T,A)	*((uint16*) (T))= (uint16) (A) +#define int3store(T,A)		{ *(T)=  (uchar) ((A));\ +				  *(T+1)=(uchar) (((uint) (A) >> 8));\ +				  *(T+2)=(uchar) (((A) >> 16)); } +#define int4store(T,A)	*((long *) (T))= (long) (A) +#define int5store(T,A)	{ *(T)= (uchar)((A));\ +			  *((T)+1)=(uchar) (((A) >> 8));\ +			  *((T)+2)=(uchar) (((A) >> 16));\ +			  *((T)+3)=(uchar) (((A) >> 24)); \ +			  *((T)+4)=(uchar) (((A) >> 32)); } +#define int8store(T,A)	*((ulonglong *) (T))= (ulonglong) (A) + +#define doubleget(V,M)	{ *((long *) &V) = *((long*) M); \ +			  *(((long *) &V)+1) = *(((long*) M)+1); } +#define doublestore(T,V) { *((long *) T) = *((long*) &V); \ +			   *(((long *) T)+1) = *(((long*) &V)+1); } +#define float4get(V,M) { *((long *) &(V)) = *((long*) (M)); } +#define float8get(V,M) doubleget((V),(M)) +#define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float)) +#define float8store(V,M) doublestore((V),(M)) + +#endif /* _WIN64 */ diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32 new file mode 100644 index 0000000000..99a702f1f0 --- /dev/null +++ b/ext/mysqlnd/config.w32 @@ -0,0 +1,20 @@ +// $Id$ +// vim:ft=javascript + +mysqld_source = ""; +if (CHECK_LIB("ws2_32.lib", "mysqlnd")) { +	mysqlnd_source =  +		"mysqlnd.c " + +		"mysqlnd_debug.c " + +		"mysqlnd_charset.c " + +		"mysqlnd_loaddata.c " + +		"mysqlnd_palloc.c " + +		"mysqlnd_ps.c " + +		"mysqlnd_ps_codec.c " + +		"mysqlnd_qcache.c " + +		"mysqlnd_result.c " + +		"mysqlnd_result_meta.c " + +		"mysqlnd_statistics.c " + +		"mysqlnd_wireprotocol.c"; +	EXTENSION("mysqlnd", mysqlnd_source, false); +} diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 new file mode 100644 index 0000000000..9d09ba71da --- /dev/null +++ b/ext/mysqlnd/config9.m4 @@ -0,0 +1,29 @@ +dnl +dnl $Id$ +dnl config.m4 for mysqlnd driver + +dnl If some extension uses mysqlnd it will get compiled in PHP core +if test "$PHP_MYSQLND_ENABLED" = "yes"; then +  mysqlnd_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \ +                   mysqlnd_ps.c mysqlnd_loaddata.c mysqlnd_palloc.c \ +                   mysqlnd_ps_codec.c mysqlnd_statistics.c mysqlnd_qcache.c\ +				   mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c" + +  PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, no) +  PHP_ADD_BUILD_DIR([ext/mysqlnd], 1) +  PHP_INSTALL_HEADERS([ext/mysqlnd]) +  PHP_INSTALL_HEADERS([$ext_builddir/php_mysqlnd_config.h]) +  AC_DEFINE([HAVE_MYSQLND], 1, [Whether mysqlnd is enabled]) + +  dnl This creates a file so it has to be after above macros +  PHP_CHECK_TYPES([int8 uint8 int16 uint16 int32 uint32 uchar ulong int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t], [ +    $ext_builddir/php_mysqlnd_config.h +  ],[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +  ]) +fi diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c new file mode 100644 index 0000000000..98b3ab7dc9 --- /dev/null +++ b/ext/mysqlnd/mysqlnd.c @@ -0,0 +1,2078 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_result.h" +#include "mysqlnd_statistics.h" +#include "mysqlnd_charset.h" +#include "mysqlnd_debug.h" +#include "php_ini.h" +#include "ext/standard/basic_functions.h" +#include "ext/standard/php_lcg.h" +#include "ext/standard/info.h" + +/* the server doesn't support 4byte utf8, but let's make it forward compatible */ +#define MYSQLND_MAX_ALLOWED_USER_LEN	256  /* 64 char * 4byte */ +#define MYSQLND_MAX_ALLOWED_DB_LEN		256  /* 64 char * 4byte */ +/* +  TODO : +  - Don't bind so tightly the metadata with the result set. This means +	that the metadata reading should not expect a MYSQLND_RES pointer, it +	does not need it, but return a pointer to the metadata (MYSQLND_FIELD *). +	For normal statements we will then just assign it to a member of +	MYSQLND_RES. For PS statements, it will stay as part of the statement +	(MYSQLND_STMT) between prepare and execute. At execute the new metadata +	will be sent by the server, so we will discard the old one and then +	finally attach it to the result set. This will make the code more clean, +	as a prepared statement won't have anymore stmt->result != NULL, as it +	is now, just to have where to store the metadata. + +  - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING +	terminated by a string with ptr being NULL. Thus, multi-part messages can be +	sent to the network like writev() and this can save at least for +	mysqlnd_stmt_send_long_data() new malloc. This change will probably make the +	code in few other places cleaner. +*/ + +extern MYSQLND_CHARSET *mysqlnd_charsets; + + + + +const char * mysqlnd_server_gone = "MySQL server has gone away"; +const char * mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now"; + +MYSQLND_STATS *mysqlnd_global_stats = NULL; +static zend_bool mysqlnd_library_initted = FALSE; + + +enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC); + +/* {{{ mysqlnd_library_init */ +static +void mysqlnd_library_init() +{ +	if (mysqlnd_library_initted == FALSE) { +		mysqlnd_library_initted = TRUE; +		_mysqlnd_init_ps_subsystem(); +		/* Should be calloc, as mnd_calloc will reference LOCK_access*/ +		mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS)); +#ifdef ZTS +		mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc(); +#endif +	} +} +/* }}} */ + + +/* {{{ mysqlnd_library_end */ +static +void mysqlnd_library_end() +{ +	if (mysqlnd_library_initted == TRUE) { +#ifdef ZTS +		tsrm_mutex_free(mysqlnd_global_stats->LOCK_access); +#endif +		/* mnd_free will reference LOCK_access and crash...*/ +		free(mysqlnd_global_stats); +		mysqlnd_global_stats = NULL; +		mysqlnd_library_initted = FALSE; +	} +} +/* }}} */ + + +/* {{{ mysqlnd_conn::free_contents */ +static void +MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) +{ +	zend_bool pers = conn->persistent; + +	DBG_ENTER("mysqlnd_conn::free_contents"); + +	mysqlnd_local_infile_default(conn); +	if (conn->current_result) { +		conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC); +		mnd_efree(conn->current_result); +		conn->current_result = NULL; +	} + +	if (conn->net.stream) { +		DBG_INF_FMT("Freeing stream. abstract=%p", conn->net.stream->abstract); +		if (pers) { +			php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); +		} else { +			php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE); +		 +		} +		conn->net.stream = NULL; +	} + +	DBG_INF("Freeing memory of members"); +	if (conn->host) { +		DBG_INF("Freeing host"); +		mnd_pefree(conn->host, pers); +		conn->host = NULL; +	} +	if (conn->user) { +		DBG_INF("Freeing user"); +		mnd_pefree(conn->user, pers); +		conn->user = NULL; +	} +	if (conn->passwd) { +		DBG_INF("Freeing passwd"); +		mnd_pefree(conn->passwd, pers); +		conn->passwd = NULL; +	} +	if (conn->unix_socket) { +		DBG_INF("Freeing unix_socket"); +		mnd_pefree(conn->unix_socket, pers); +		conn->unix_socket = NULL; +	} +	if (conn->scheme) { +		DBG_INF("Freeing scheme"); +		mnd_pefree(conn->scheme, pers); +		conn->scheme = NULL; +	} +	if (conn->server_version) { +		DBG_INF("Freeing server_version"); +		mnd_pefree(conn->server_version, pers); +		conn->server_version = NULL; +	} +	if (conn->host_info) { +		DBG_INF("Freeing host_info"); +		mnd_pefree(conn->host_info, pers); +		conn->host_info = NULL; +	} +	if (conn->scramble) { +		DBG_INF("Freeing scramble"); +		mnd_pefree(conn->scramble, pers); +		conn->scramble = NULL; +	} +	if (conn->last_message) { +		mnd_pefree(conn->last_message, pers); +		conn->last_message = NULL; +	} +	if (conn->options.charset_name) { +		mnd_pefree(conn->options.charset_name, pers); +		conn->options.charset_name = NULL; +	} +	if (conn->options.num_commands) { +		unsigned int i; +		for (i=0; i < conn->options.num_commands; i++) { +			mnd_pefree(conn->options.init_commands[i], pers); +		} +		mnd_pefree(conn->options.init_commands, pers); +		conn->options.init_commands = NULL; +	} +	if (conn->options.cfg_file) { +		mnd_pefree(conn->options.cfg_file, pers); +		conn->options.cfg_file = NULL; +	} +	if (conn->options.cfg_section) { +		mnd_pefree(conn->options.cfg_section, pers); +		conn->options.cfg_section = NULL; +	} +	if (conn->options.ssl_key) { +		mnd_pefree(conn->options.ssl_key, pers); +		conn->options.ssl_key = NULL; +	} +	if (conn->options.ssl_cert) { +		mnd_pefree(conn->options.ssl_cert, pers); +		conn->options.ssl_cert = NULL; +	} +	if (conn->options.ssl_ca) { +		mnd_pefree(conn->options.ssl_ca, pers); +		conn->options.ssl_ca = NULL; +	} +	if (conn->options.ssl_capath) { +		mnd_pefree(conn->options.ssl_capath, pers); +		conn->options.ssl_capath = NULL; +	} +	if (conn->options.ssl_cipher) { +		mnd_pefree(conn->options.ssl_cipher, pers); +		conn->options.ssl_cipher = NULL; +	} +	if (conn->zval_cache) { +		DBG_INF("Freeing zval cache reference"); +		mysqlnd_palloc_free_thd_cache_reference(&conn->zval_cache); +		conn->zval_cache = NULL; +	} +	if (conn->qcache) { +		DBG_INF("Freeing qcache reference"); +		mysqlnd_qcache_free_cache_reference(&conn->qcache); +		conn->qcache = NULL; +	} +	if (conn->net.cmd_buffer.buffer) { +		DBG_INF("Freeing cmd buffer"); +		mnd_pefree(conn->net.cmd_buffer.buffer, pers); +		conn->net.cmd_buffer.buffer = NULL; +	} +	conn->charset = NULL; +	conn->greet_charset = NULL; + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::dtor */ +static void +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_conn::dtor"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	conn->m->free_contents(conn TSRMLS_CC); + +	mnd_pefree(conn, conn->persistent); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_simple_command_handle_response */ +enum_func_status +mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet, +									   zend_bool silent, enum php_mysqlnd_server_command command +									   TSRMLS_DC) +{ +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_simple_command_handle_response"); +	DBG_INF_FMT("silent=%d packet=%d command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); + +	switch (ok_packet) { +		case PROT_OK_PACKET:{ +			php_mysql_packet_ok ok_response; +			PACKET_INIT_ALLOCA(ok_response, PROT_OK_PACKET); +			if (FAIL == (ret = PACKET_READ_ALLOCA(ok_response, conn))) { +				if (!silent) { +					DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]); +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%d", +									 mysqlnd_command_to_text[command], getpid()); +				} +			} else { +				DBG_INF_FMT("OK from server"); +				if (0xFF == ok_response.field_count) { +					/* The server signalled error. Set the error */ +					SET_CLIENT_ERROR(conn->error_info, ok_response.error_no, +									 ok_response.sqlstate, ok_response.error);  +					ret = FAIL; +					/* +					  Cover a protocol design error: error packet does not +					  contain the server status. Therefore, the client has no way +					  to find out whether there are more result sets of +					  a multiple-result-set statement pending. Luckily, in 5.0 an +					  error always aborts execution of a statement, wherever it is +					  a multi-statement or a stored procedure, so it should be +					  safe to unconditionally turn off the flag here. +					*/ +					conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS; +					SET_ERROR_AFF_ROWS(conn); +				} else { +					SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, +									ok_response.message, ok_response.message_len, +									conn->persistent); + +					conn->upsert_status.warning_count = ok_response.warning_count; +					conn->upsert_status.server_status = ok_response.server_status; +					conn->upsert_status.affected_rows = ok_response.affected_rows; +					conn->upsert_status.last_insert_id = ok_response.last_insert_id; +				} +			} +			PACKET_FREE_ALLOCA(ok_response); +			break; +		} +		case PROT_EOF_PACKET:{ +			php_mysql_packet_eof ok_response; +			PACKET_INIT_ALLOCA(ok_response, PROT_EOF_PACKET); +			if (FAIL == (ret = PACKET_READ_ALLOCA(ok_response, conn))) { +				SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, +								 "Malformed packet"); +				if (!silent) { +					DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]); +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d", +								 	mysqlnd_command_to_text[command], getpid()); +				} +			} else if (0xFF == ok_response.field_count) { +				/* The server signalled error. Set the error */ +				SET_CLIENT_ERROR(conn->error_info, ok_response.error_no, +								 ok_response.sqlstate, ok_response.error); +				SET_ERROR_AFF_ROWS(conn); +			} else if (0xFE != ok_response.field_count) { +				SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, +								 "Malformed packet"); +				if (!silent) { +					DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response.field_count); +					php_error_docref(NULL TSRMLS_CC, E_WARNING, +									"EOF packet expected, field count wasn't 0xFE but 0x%2X", +									ok_response.field_count); +				} +			} else { +				DBG_INF_FMT("OK from server"); +			} +			PACKET_FREE_ALLOCA(ok_response); +			break; +		} +		default: +			ret = FAIL; +			SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, +							 "Malformed packet"); +			php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %d passed to the function", +							 ok_packet); +			break; +	} +	DBG_INF(ret == PASS ? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_simple_command */ +enum_func_status +mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, +					   const char * const arg, size_t arg_len, +					   enum php_mysql_packet_type ok_packet, zend_bool silent TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	php_mysql_packet_command cmd_packet; + +	DBG_ENTER("mysqlnd_simple_command"); +	DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent); + +	switch (conn->state) { +		case CONN_READY: +			break; +		case CONN_QUIT_SENT: +			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); +			DBG_ERR("Server is gone"); +			DBG_RETURN(FAIL); +		default: +			SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +							 mysqlnd_out_of_sync); +			DBG_ERR("Command out of sync"); +			DBG_RETURN(FAIL); +	} + +	/* clean UPSERT info */ +	memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); +	SET_ERROR_AFF_ROWS(conn); +	SET_EMPTY_ERROR(conn->error_info); + +	PACKET_INIT_ALLOCA(cmd_packet, PROT_CMD_PACKET); +	cmd_packet.command = command; +	if (arg && arg_len) { +		cmd_packet.argument = arg; +		cmd_packet.arg_len  = arg_len; +	} + +	if (! PACKET_WRITE_ALLOCA(cmd_packet, conn)) { +		if (!silent) { +			DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]); +			php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid()); +		} +		DBG_ERR("Server is gone"); +		ret = FAIL; +	} else if (ok_packet != PROT_LAST) { +		ret = mysqlnd_simple_command_handle_response(conn, ok_packet, silent, command TSRMLS_CC); +	} + +	/* +	  There is no need to call FREE_ALLOCA on cmd_packet as the +	  only allocated string is cmd_packet.argument and it was passed +	  to us. We should not free it. +	*/ + +	DBG_INF(ret == PASS ? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::set_server_option */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, +												enum_mysqlnd_server_option option TSRMLS_DC) +{ +	enum_func_status ret; +	char buffer[2]; +	DBG_ENTER("mysqlnd_conn::set_server_option"); + +	int2store(buffer, (uint) option); +	ret = mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), +								 PROT_EOF_PACKET, FALSE TSRMLS_CC); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_restart_psession */ +PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC)  +{ +	DBG_ENTER("_mysqlnd_restart_psession"); +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_REUSED); +	/* Free here what should not be seen by the next script */ +	if (conn->last_message) { +		mnd_pefree(conn->last_message, conn->persistent); +		conn->last_message = NULL; +	} +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_end_psession */ +PHPAPI void mysqlnd_end_psession(MYSQLND *conn) +{ + +} +/* }}} */ + + +/* {{{ mysqlnd_connect */ +PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, +						 char *host, char *user, +						 char *passwd, unsigned int passwd_len, +						 char *db, unsigned int db_len, +						 unsigned int port, +						 char *socket, +						 unsigned int mysql_flags, +						 MYSQLND_THD_ZVAL_PCACHE *zval_cache +						 TSRMLS_DC) +{ +	char *transport = NULL, *errstr = NULL; +	char *hashed_details = NULL; +	int transport_len, hashed_details_len, errcode = 0; +	unsigned int streams_options = ENFORCE_SAFE_MODE; +	unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; +	zend_bool self_alloced = FALSE; +	struct timeval tv; +	zend_bool unix_socket = FALSE; +	const MYSQLND_CHARSET * charset; +	zend_bool reconnect = FALSE; + +	php_mysql_packet_greet greet_packet; +	php_mysql_packet_auth *auth_packet; +	php_mysql_packet_ok ok_packet; + +	DBG_ENTER("mysqlnd_connect"); +	DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d", +				host?host:"", user?user:"", db?db:"", port, mysql_flags, +				conn? conn->persistent:0, conn? conn->state:-1); + +	DBG_INF_FMT("state=%d", conn->state); +	if (conn && conn->state > CONN_ALLOCED && conn->state ) { +		DBG_INF("Connecting on a connected handle."); + +		if (conn->state < CONN_QUIT_SENT) { +			MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CLOSE_IMPLICIT); +			reconnect = TRUE; +			mysqlnd_send_close(conn TSRMLS_CC); +		} + +		conn->m->free_contents(conn TSRMLS_CC); +		MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); +		if (conn->persistent) { +			MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); +		} +		/* Now reconnect using the same handle */ +	} + +	if (!host || !host[0]) { +		host = "localhost"; +	} +	if (!user || !user[0]) { +		user = php_get_current_user(); +	} +	if (!passwd) { +		passwd = ""; +		passwd_len = 0; +	} +	if (!db) { +		db = ""; +		db_len = 0; +	} +	if (!port && !socket) { +		port = 3306; +	} +#ifndef PHP_WIN32 +	if (!strncasecmp(host, "localhost", sizeof("localhost") - 1)) { +		if (!socket) { +			socket = "/tmp/mysql.sock"; +		} +		transport_len = spprintf(&transport, 0, "unix://%s", socket); +		unix_socket = TRUE; +	} else  +#endif +	{ +		transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port); +	} +	DBG_INF_FMT("transport=%p", transport); + +	if (conn->persistent) { +		struct timeval tv; +		gettimeofday(&tv, NULL); +		/* We should generate something unique */ +		hashed_details_len = spprintf(&hashed_details, 0, "%s@%s@%s@%ld@%ld@%0.8F", +									  transport, user, db, tv.tv_sec, (long int)tv.tv_usec, +									  php_combined_lcg(TSRMLS_C) * 10); +		DBG_INF_FMT("hashed_details=%s", hashed_details); +	}  + +	PACKET_INIT_ALLOCA(greet_packet, PROT_GREET_PACKET); +	PACKET_INIT(auth_packet, PROT_AUTH_PACKET, php_mysql_packet_auth *); +	PACKET_INIT_ALLOCA(ok_packet, PROT_OK_PACKET); + +	if (!conn) { +		conn = mysqlnd_init(FALSE); +		self_alloced = TRUE; +	} + +	conn->state	= CONN_ALLOCED; +	conn->net.packet_no = 0; + +	if (conn->options.timeout_connect) { +		tv.tv_sec = conn->options.timeout_connect; +		tv.tv_usec = 0; +	} +	if (conn->persistent) { +		conn->scheme = pestrndup(transport, transport_len, 1); +		mnd_efree(transport); +	} else { +		conn->scheme = transport; +	} +	DBG_INF(conn->scheme); +	conn->net.stream = php_stream_xport_create(conn->scheme, transport_len, streams_options, streams_flags, +											   hashed_details,  +											   (conn->options.timeout_connect) ? &tv : NULL, +											    NULL /*ctx*/, &errstr, &errcode); +	DBG_INF_FMT("stream=%p", conn->net.stream); + +	if (hashed_details) { +		/* +		  If persistent, the streams register it in EG(persistent_list). +		  This is unwanted. ext/mysql or ext/mysqli are responsible to clean, +		  whatever they have to. +		*/ +		zend_rsrc_list_entry *le; + +		if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, +						   (void*) &le) == SUCCESS) { +			/* +			  in_free will let streams code skip destructing - big HACK, +			  but STREAMS suck big time regarding persistent streams. +			  Just not compatible for extensions that need persistency. +			*/ +			conn->net.stream->in_free = 1; +			zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1); +			conn->net.stream->in_free = 0; +		} +#if ZEND_DEBUG +		/* Shut-up the streams, they don't know what they are doing */ +		conn->net.stream->__exposed = 1; +#endif +		mnd_efree(hashed_details); +	} + +	if (errstr || !conn->net.stream) { +		goto err; +	} + +	if (conn->options.timeout_read) +	{ +		tv.tv_sec = conn->options.timeout_read; +		tv.tv_usec = 0; +		php_stream_set_option(conn->net.stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); +	} + +	if (!unix_socket) { +		/* Set TCP_NODELAY */ +		mysqlnd_set_sock_no_delay(conn->net.stream); +	} + +	if (FAIL == PACKET_READ_ALLOCA(greet_packet, conn)) { +		DBG_ERR("Error while reading greeting packet"); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid()); +		goto err; +	} else if (greet_packet.error_no) { +		DBG_ERR_FMT("errorno=%d error=%s", greet_packet.error_no, greet_packet.error); +		SET_CLIENT_ERROR(conn->error_info, greet_packet.error_no, +						 greet_packet.sqlstate, greet_packet.error); +		goto err;	 +	} else if (greet_packet.pre41) { +		DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", +					greet_packet.server_version); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 " +						" is not supported. Server is %-.32s", greet_packet.server_version); +		SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, +						 "Connecting to 3.22, 3.23 & 4.0 servers is not supported"); +		goto err; +	} + +	conn->thread_id			= greet_packet.thread_id; +	conn->protocol_version	= greet_packet.protocol_version; +	conn->server_version	= greet_packet.server_version; +	greet_packet.server_version = NULL; /* The string will be freed otherwise */ + +	conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); +	/* we allow load data local infile by default */ +	mysql_flags  |= CLIENT_LOCAL_FILES; + +	auth_packet->user		= user; +	auth_packet->password	= passwd; + +	if (conn->options.charset_name && +		(charset = mysqlnd_find_charset_name(conn->options.charset_name))) +	{ +		auth_packet->charset_no	= charset->nr; +#if PHP_MAJOR_VERSION >= 6 +	} else if (UG(unicode)) { +		auth_packet->charset_no	= 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ +#endif +	} else { +		auth_packet->charset_no	= greet_packet.charset_no; +	} +	auth_packet->db			= db; +	auth_packet->db_len		= db_len; +	auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL; +	auth_packet->client_flags= mysql_flags; + +	conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); +	memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH); +	if (!PACKET_WRITE(auth_packet, conn)) { +		conn->state = CONN_QUIT_SENT; +		SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); +		goto err;	 +	} + +	if (FAIL == PACKET_READ_ALLOCA(ok_packet, conn) || ok_packet.field_count >= 0xFE) { +		if (ok_packet.field_count == 0xFE) { +			/* old authentication with new server  !*/ +			DBG_ERR("mysqlnd cannot connect to MySQL 4.1+ using old authentication"); +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd cannot connect to MySQL 4.1+ using old authentication"); +		} else if (ok_packet.field_count == 0xFF) { +			if (ok_packet.sqlstate[0]) { +				if (!self_alloced) { +					strncpy(conn->error_info.sqlstate, ok_packet.sqlstate, sizeof(conn->error_info.sqlstate)); +				} +				DBG_ERR_FMT("ERROR:%d [SQLSTATE:%s] %s", +							ok_packet.error_no, ok_packet.sqlstate, ok_packet.error); +			} +			if (!self_alloced) { +				conn->error_info.error_no = ok_packet.error_no; +				strncpy(conn->error_info.error, ok_packet.error, sizeof(conn->error_info.error)); +			} +		} +	} else { +		conn->state				= CONN_READY; + +		conn->user				= pestrdup(user, conn->persistent); +		conn->passwd			= pestrndup(passwd, passwd_len, conn->persistent); +		conn->port				= port; +		if (host && !socket) { +			char *p; + +			conn->host = pestrdup(host, conn->persistent); +			spprintf(&p, 0, "MySQL host info: %s via TCP/IP", conn->host); +			if (conn->persistent) { +				conn->host_info = pestrdup(p, 1); +				mnd_efree(p); +			} else { +				conn->host_info = p; +			} +		} else { +			conn->unix_socket	= pestrdup(socket, conn->persistent); +			conn->host_info		= pestrdup("MySQL host info: Localhost via UNIX socket", conn->persistent); +		} +		conn->client_flag		= auth_packet->client_flags; +		conn->max_packet_size	= auth_packet->max_packet_size; +		/* todo: check if charset is available */ +		conn->charset			= mysqlnd_find_charset_nr(auth_packet->charset_no); +		conn->server_capabilities = greet_packet.server_capabilities; +		conn->upsert_status.warning_count = 0; +		conn->upsert_status.server_status = greet_packet.server_status; +		conn->upsert_status.affected_rows = 0; +		SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, +						ok_packet.message, ok_packet.message_len, +						conn->persistent); + +		SET_EMPTY_ERROR(conn->error_info); + +		PACKET_FREE_ALLOCA(greet_packet); +		PACKET_FREE(auth_packet); +		PACKET_FREE_ALLOCA(ok_packet); + +		conn->zval_cache = mysqlnd_palloc_get_thd_cache_reference(zval_cache); +		conn->net.cmd_buffer.length = 128L*1024L; +		conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent); + +        mysqlnd_local_infile_default(conn); +		{ +			uint buf_size; +			buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to uint*/ +			conn->m->set_client_option(conn, MYSQLND_OPT_NET_READ_BUFFER_SIZE, +								   		(char *)&buf_size TSRMLS_CC); + +			buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to uint*/ +			conn->m->set_client_option(conn, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, +								   		(char *)&buf_size TSRMLS_CC);			 +		} + +		MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_SUCCESS); +		if (reconnect) { +			MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);	 +		} +		MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); +		if (conn->persistent) { +			MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); +		} + +		DBG_INF_FMT("connection_id=%llu", conn->thread_id); +#if PHP_MAJOR_VERSION >= 6 +		{ +			uint as_unicode = 1; +			conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, +									   (char *)&as_unicode TSRMLS_CC); +			DBG_INF("unicode set"); +		} +#endif +		DBG_RETURN(conn); +	} +err: +	PACKET_FREE_ALLOCA(greet_packet); +	PACKET_FREE(auth_packet); +	PACKET_FREE_ALLOCA(ok_packet); + +	if (errstr) { +		DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); +		SET_CLIENT_ERROR(conn->error_info, errcode, UNKNOWN_SQLSTATE, errstr); + +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + +		mnd_efree(errstr); +	} +	if (conn->scheme) { +		mnd_pefree(conn->scheme, conn->persistent); +		conn->scheme = NULL; +	} + + +	/* This will also close conn->net.stream if it has been opened */ +	conn->m->free_contents(conn TSRMLS_CC); + +	if (self_alloced) { +		/* +		  We have alloced, thus there are no references to this +		  object - we are free to kill it! +		*/ +		conn->m->dtor(conn TSRMLS_CC); +	} else { +		MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_FAILURE); +	} +	DBG_RETURN(NULL); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::query */ +/*  +  If conn->error_info.error_no is not zero, then we had an error. +  Still the result from the query is PASS +*/ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) +{ +	enum_func_status ret; +	DBG_ENTER("mysqlnd_conn::query"); +	DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query); + +	if (PASS != mysqlnd_simple_command(conn, COM_QUERY, query, query_len, +									   PROT_LAST /* we will handle the OK packet*/, +									   FALSE TSRMLS_CC)) { +		DBG_RETURN(FAIL); +	} + +	/* +	  Here read the result set. We don't do it in simple_command because it need +	  information from the ok packet. We will fetch it ourselves. +	*/ +	ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* +  COM_FIELD_LIST is special, different from a SHOW FIELDS FROM : +  - There is no result set header - status from the command, which +    impacts us to allocate big chunk of memory for reading the metadata. +  - The EOF packet is consumed by the metadata packet reader. +*/ + +/* {{{ mysqlnd_conn::list_fields */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC) +{ +	/* db + \0 + wild + \0 (for wild) */ +	char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 4 * 2 + 1 + 1], *p; +	size_t table_len, wild_len; +	MYSQLND_RES *result = NULL; +	DBG_ENTER("mysqlnd_conn::list_fields"); +	DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:""); + +	p = buff; +	if (table && (table_len = strlen(table))) { +		memcpy(p, table, MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); +		p += table_len; +		*p++ = '\0'; +	} + +	if (achtung_wild && (wild_len = strlen(achtung_wild))) { +		memcpy(p, achtung_wild, MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); +		p += wild_len;	 +		*p++ = '\0'; +	} + +	if (PASS != mysqlnd_simple_command(conn, COM_FIELD_LIST, buff, p - buff, +									   PROT_LAST /* we will handle the OK packet*/, +									   FALSE TSRMLS_CC)) { +		DBG_RETURN(NULL); +	} +	/* +	   Prepare for the worst case. +	   MyISAM goes to 2500 BIT columns, double it for safety. +	 */ +	result = mysqlnd_result_init(5000, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC); + + +	if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) { +		DBG_ERR("Error ocurred while reading metadata"); +		result->m.free_result(result, TRUE TSRMLS_CC); +		DBG_RETURN(NULL); +	} + +	result->type = MYSQLND_RES_NORMAL; +	result->m.fetch_row = result->m.fetch_row_normal_unbuffered; +	result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); +	result->unbuf->eof_reached = TRUE; + +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::list_method */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, +										  const char *achtung_wild, char *par1 TSRMLS_DC) +{ +	char *show_query = NULL; +	size_t show_query_len; +	MYSQLND_RES *result = NULL; + +	DBG_ENTER("mysqlnd_conn::list_method"); +	DBG_INF_FMT("conn=%llu query=%s wild=%d", conn->thread_id, query, achtung_wild); + +	if (par1) { +		if (achtung_wild) { +			show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild); +		} else { +			show_query_len = spprintf(&show_query, 0, query, par1);	 +		} +	} else { +		if (achtung_wild) { +			show_query_len = spprintf(&show_query, 0, query, achtung_wild); +		} else { +			show_query_len = strlen(show_query = (char *)query);	 +		}	 +	} + +	if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) { +		result = conn->m->store_result(conn TSRMLS_CC); +	} +	if (show_query != query) { +		mnd_efree(show_query); +	} +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::errno */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn) +{ +	return conn->error_info.error_no; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::error */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn) +{ +	return conn->error_info.error; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::sqlstate */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn) +{ +	return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_old_escape_string */ +PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_old_escape_string"); +	DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), +										   newstr, escapestr, escapestr_len TSRMLS_CC)); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::escape_string */ +static ulong +MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, +											const char *escapestr, int escapestr_len TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_conn::escape_string"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); +	if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) { +		DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC)); +	} +	DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC)); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::dump_debug_info */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_conn::dump_debug_info"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); +	DBG_RETURN(mysqlnd_simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE TSRMLS_CC)); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::select_db */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, +										const char * const db, +										unsigned int db_len TSRMLS_DC) +{ +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_conn::select_db"); +	DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db); + +	ret = mysqlnd_simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE TSRMLS_CC); +	/* +	  The server sends 0 but libmysql doesn't read it and has established +	  a protocol of giving back -1. Thus we have to follow it :( +	*/ +	SET_ERROR_AFF_ROWS(conn); + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::ping */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) +{ +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_conn::ping"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE TSRMLS_CC); +	/* +	  The server sends 0 but libmysql doesn't read it and has established +	  a protocol of giving back -1. Thus we have to follow it :( +	*/ +	SET_ERROR_AFF_ROWS(conn); + +	DBG_INF_FMT("ret=%d", ret); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::stat */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC) +{ +	enum_func_status ret; +	php_mysql_packet_stats stats_header; + +	DBG_ENTER("mysqlnd_conn::stat"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	ret = mysqlnd_simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE TSRMLS_CC); +	if (FAIL == ret) { +		DBG_RETURN(FAIL); +	} +	PACKET_INIT_ALLOCA(stats_header, PROT_STATS_PACKET); +	if (FAIL == (ret = PACKET_READ_ALLOCA(stats_header, conn))) { +		DBG_RETURN(FAIL); +	} +	*message = stats_header.message; +	*message_len = stats_header.message_len; +	/* Ownership transfer */ +	stats_header.message = NULL; +	PACKET_FREE_ALLOCA(stats_header); + +	DBG_INF(*message); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::kill */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC) +{ +	enum_func_status ret; +	char buff[4]; + +	DBG_ENTER("mysqlnd_conn::kill"); +	DBG_INF_FMT("conn=%llu pid=%lu", conn->thread_id, pid); + +	int4store(buff, pid); + +	/* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */ +	if (pid != conn->thread_id) { +		ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE TSRMLS_CC); +		/* +		  The server sends 0 but libmysql doesn't read it and has established +		  a protocol of giving back -1. Thus we have to follow it :( +		*/ +		SET_ERROR_AFF_ROWS(conn); +	} else if (PASS == (ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff, +													 4, PROT_LAST, FALSE TSRMLS_CC))) { +		conn->state = CONN_QUIT_SENT; +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_conn::set_charset */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	char *query; +	size_t query_len; +	const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname); + +	DBG_ENTER("mysqlnd_conn::set_charset"); +	DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname); + +	if (!charset) { +		SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, +						 "Invalid characterset or character set not supported"); +		DBG_RETURN(FAIL); +	} + +	query_len = spprintf(&query, 0, "SET NAMES %s", csname); + +	if (FAIL == conn->m->query(conn, query, query_len TSRMLS_CC)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query"); +	} else if (conn->error_info.error_no) { +		ret = FAIL; +	} else { +		conn->charset = charset; +	} +	mnd_efree(query); + +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::refresh */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, unsigned long options TSRMLS_DC) +{ +	zend_uchar bits[1]; +	DBG_ENTER("mysqlnd_conn::refresh"); +	DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options); + +	int1store(bits, options); + +	DBG_RETURN(mysqlnd_simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC)); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::shutdown */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, unsigned long level TSRMLS_DC) +{ +	zend_uchar bits[1]; +	DBG_ENTER("mysqlnd_conn::shutdown"); +	DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level); + +	int1store(bits, level); + +	DBG_RETURN(mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC)); +} +/* }}} */ + + +/* {{{ mysqlnd_send_close */ +enum_func_status +mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) +{ +	enum_func_status ret = PASS; + +	DBG_ENTER("mysqlnd_send_close"); +	DBG_INF_FMT("conn=%llu conn->net.stream->abstract=%p", +				conn->thread_id, conn->net.stream? conn->net.stream->abstract:NULL); + +	switch (conn->state) { +		case CONN_READY: +			DBG_INF("Connection clean, sending COM_QUIT"); +			ret =  mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, +										  TRUE TSRMLS_CC); +			/* Do nothing */ +			break; +		case CONN_SENDING_LOAD_DATA: +			/* +			  Don't send COM_QUIT if we are in a middle of a LOAD DATA or we +			  will crash (assert) a debug server. +			*/ +		case CONN_NEXT_RESULT_PENDING: +		case CONN_QUERY_SENT: +		case CONN_FETCHING_DATA: +			MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE); +			DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme); +			/* +			  Do nothing, the connection will be brutally closed +			  and the server will catch it and free close from its side. +			*/ +		case CONN_ALLOCED: +			/* +			  Allocated but not connected or there was failure when trying +			  to connect with pre-allocated connect. + +			  Fall-through +			*/ +		case CONN_QUIT_SENT: +			/* The user has killed its own connection */ +			break; +	} +	/* +	  We hold one reference, and every other object which needs the +	  connection does increase it by 1. +	*/ +	conn->state = CONN_QUIT_SENT; + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::close */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	static enum_mysqlnd_collected_stats +	close_type_to_stat_map[MYSQLND_CLOSE_LAST] = { +		STAT_CLOSE_EXPLICIT, +		STAT_CLOSE_IMPLICIT, +		STAT_CLOSE_DISCONNECT +	}; +	enum_mysqlnd_collected_stats stat = close_type_to_stat_map[close_type]; + +	DBG_ENTER("mysqlnd_conn::close"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); +	MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); +	if (conn->persistent) { +		MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); +	} + +	/* +	  Close now, free_reference will try, +	  if we are last, but that's not a problem. +	*/ +	ret = mysqlnd_send_close(conn TSRMLS_CC); + +	ret = conn->m->free_reference(conn TSRMLS_CC); + + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_reference */ +static MYSQLND * +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn) +{ +	++conn->refcount; +	return conn; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::free_reference */ +static enum_func_status +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	DBG_ENTER("mysqlnd_conn::free_reference"); +	DBG_INF_FMT("conn=%llu conn->refcount=%u", conn->thread_id, conn->refcount); +	if (!(--conn->refcount)) { +		/* +		  No multithreading issues as we don't share the connection :) +		  This will free the object too, of course because references has +		  reached zero. +		*/ +		ret = mysqlnd_send_close(conn TSRMLS_CC); +		conn->m->dtor(conn TSRMLS_CC); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::field_count */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn) +{ +	return conn->field_count; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::insert_id */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn) +{ +	return conn->upsert_status.last_insert_id; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::affected_rows */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn) +{ +	return conn->upsert_status.affected_rows; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::warning_count */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn) +{ +	return conn->upsert_status.warning_count; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::info */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn) +{ +	return conn->last_message; +} +/* }}} */ + + +/* {{{ mysqlnd_get_client_info */ +PHPAPI const char * mysqlnd_get_client_info() +{ +	return MYSQLND_VERSION; +} +/* }}} */ + + +/* {{{ mysqlnd_get_client_version */ +PHPAPI unsigned int mysqlnd_get_client_version() +{ +	return MYSQLND_VERSION_ID; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_server_info */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn) +{ +	return conn->server_version; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_host_info */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn) +{ +	return conn->host_info; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_proto_info */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn) +{ +	return conn->protocol_version; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::charset_name */ +static const char * +MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn) +{ +	return conn->charset->name; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::thread_id */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn) +{ +	return conn->thread_id; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_server_version */ +static unsigned long +MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn) +{ +	long major, minor, patch; +	char *p; + +	if (!(p = conn->server_version)) { +		return 0; +	} + +	major = strtol(p, &p, 10); +	p += 1; /* consume the dot */ +	minor = strtol(p, &p, 10); +	p += 1; /* consume the dot */ +	patch = strtol(p, &p, 10); +	 +	return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch)); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::more_results */ +static zend_bool +MYSQLND_METHOD(mysqlnd_conn,more_results)(const MYSQLND * const conn) +{ +	/* (conn->state == CONN_NEXT_RESULT_PENDING) too */ +	return conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE; +} +/* }}} */ + + +/* {{{ mysqlnd_conn::next_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) +{ +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_conn::next_result"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	if (conn->state != CONN_NEXT_RESULT_PENDING) { +		DBG_RETURN(FAIL); +	} + +	SET_EMPTY_ERROR(conn->error_info); +	conn->upsert_status.affected_rows= ~(mynd_ulonglong) 0; +	/* +	  We are sure that there is a result set, since conn->state is set accordingly +	  in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered() +	*/ +	if (FAIL == (ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC))) { +		DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid()); +		conn->state = CONN_QUIT_SENT; +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_field_type_name */ +PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type) +{ +	switch(field_type) { +		case FIELD_TYPE_STRING: +		case FIELD_TYPE_VAR_STRING: +			return "string"; +		case FIELD_TYPE_TINY: +		case FIELD_TYPE_SHORT: +		case FIELD_TYPE_LONG: +		case FIELD_TYPE_LONGLONG: +		case FIELD_TYPE_INT24: +			return "int"; +		case FIELD_TYPE_FLOAT: +		case FIELD_TYPE_DOUBLE: +		case FIELD_TYPE_DECIMAL: +		case FIELD_TYPE_NEWDECIMAL: +			return "real"; +		case FIELD_TYPE_TIMESTAMP: +			return "timestamp"; +		case FIELD_TYPE_YEAR: +			return "year"; +		case FIELD_TYPE_DATE: +		case FIELD_TYPE_NEWDATE: +			return "date"; +		case FIELD_TYPE_TIME: +			return "time"; +		case FIELD_TYPE_SET: +			return "set"; +		case FIELD_TYPE_ENUM: +			return "enum"; +		case FIELD_TYPE_GEOMETRY: +			return "geometry"; +		case FIELD_TYPE_DATETIME: +			return "datetime"; +		case FIELD_TYPE_TINY_BLOB: +		case FIELD_TYPE_MEDIUM_BLOB: +		case FIELD_TYPE_LONG_BLOB: +		case FIELD_TYPE_BLOB: +			return "blob"; +		case FIELD_TYPE_NULL: +			return "null"; +		case FIELD_TYPE_BIT: +			return "bit"; +		default: +			return "unknown"; +	} +} +/* }}} */ + + +/* {{{ mysqlnd_conn::change_user */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, +										  const char *user, +										  const char *passwd, +										  const char *db TSRMLS_DC) +{ +	/* +	  User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3 +	  Stack space is not that expensive, so use a bit more to be protected against +	  stack overrungs. +	*/ +	size_t user_len; +	enum_func_status ret; +	php_mysql_packet_chg_user_resp chg_user_resp; +	char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1]; +	char *p = buffer; + +	DBG_ENTER("mysqlnd_conn::change_user"); +	DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s", +				conn->thread_id, user?user:"", passwd?"***":"null", db?db:""); + +	if (!user) { +		user = ""; +	} +	if (!passwd) { +		passwd = ""; +	} +	if (!db) { +		db = ""; +	} + +	/* 1. user ASCIIZ */ +	user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_DB_LEN); +	memcpy(p, user, user_len); +	p += user_len; +	*p++ = '\0'; + +	/* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */ +	if (passwd[0]) { +		*p++ = SCRAMBLE_LENGTH; +		php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd); +		p += SCRAMBLE_LENGTH; +	} else { +		*p++ = '\0'; +	} + +	/* 3. db ASCIIZ */ +	if (db[0]) { +		size_t db_len = strlen(db); +		memcpy(p, db, MIN(db_len, MYSQLND_MAX_ALLOWED_DB_LEN)); +		p += db_len; +	} +	*p++ = '\0'; + +	if (PASS != mysqlnd_simple_command(conn, COM_CHANGE_USER, buffer, p - buffer, +									   PROT_LAST /* we will handle the OK packet*/, +									   FALSE TSRMLS_CC)) { +		DBG_RETURN(FAIL); +	} + +	PACKET_INIT_ALLOCA(chg_user_resp, PROT_CHG_USER_PACKET); +	ret = PACKET_READ_ALLOCA(chg_user_resp, conn); +	conn->error_info = chg_user_resp.error_info; +	PACKET_FREE_ALLOCA(chg_user_resp); + +	if (conn->error_info.error_no) { +		ret = FAIL; +		/* +		  COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune. +		  bug#25371 mysql_change_user() triggers "packets out of sync" +		  When it gets fixed, there should be one more check here +		*/ +		if (mysqlnd_get_server_version(conn) > 50113L && +			mysqlnd_get_server_version(conn) < 50118L) +		{ +			php_mysql_packet_ok redundant_error_packet; +			PACKET_INIT_ALLOCA(redundant_error_packet, PROT_OK_PACKET); +			PACKET_READ_ALLOCA(redundant_error_packet, conn); +			PACKET_FREE_ALLOCA(redundant_error_packet); +			DBG_INF_FMT("Server is %d, buggy, sends two ERR messages", mysqlnd_get_server_version(conn)); +		} +	} +	if (ret == PASS) { +		mnd_pefree(conn->user, conn->persistent); +		conn->user = pestrndup(user, user_len, conn->persistent); +		mnd_pefree(conn->passwd, conn->persistent); +		conn->passwd = pestrdup(passwd, conn->persistent); +		if (conn->last_message) { +			mnd_pefree(conn->last_message, conn->persistent); +			conn->last_message = NULL; +		} +		conn->charset = conn->greet_charset; +		memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); +	} + +	SET_ERROR_AFF_ROWS(conn); + +	/* +	  Here we should close all statements. Unbuffered queries should not be a +	  problem as we won't allow sending COM_CHANGE_USER. +	*/ +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::set_client_option */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, +												enum mysqlnd_option option, +												const char * const value +												TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_conn::set_client_option"); +	DBG_INF_FMT("conn=%llu option=%d", conn->thread_id, option); +	switch (option) { +#if PHP_MAJOR_VERSION >= 6 +		case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE: +			conn->options.numeric_and_datetime_as_unicode = *(uint*) value; +			break; +#endif +		case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: +			conn->net.cmd_buffer.length = *(uint*) value; +			if (!conn->net.cmd_buffer.buffer) { +				conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent); +			} else { +				conn->net.cmd_buffer.buffer = mnd_perealloc(conn->net.cmd_buffer.buffer, +															conn->net.cmd_buffer.length, +															conn->persistent); +			} +			break; +		case MYSQLND_OPT_NET_READ_BUFFER_SIZE: +			conn->options.net_read_buffer_size = *(uint*) value; +			break; +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +		case MYSQLND_OPT_INT_AND_YEAR_AS_INT: +			conn->options.int_and_year_as_int = *(uint*) value; +			break; +#endif +		case MYSQL_OPT_CONNECT_TIMEOUT: +			conn->options.timeout_connect = *(uint*) value; +			break; +#ifdef WHEN_SUPPORTED_BY_MYSQLI +		case MYSQL_OPT_READ_TIMEOUT: +			conn->options.timeout_read = *(uint*) value; +			break; +		case MYSQL_OPT_WRITE_TIMEOUT: +			conn->options.timeout_write = *(uint*) value; +			break; +#endif +		case MYSQL_OPT_LOCAL_INFILE: +			if (!value || (*(uint*) value) ? 1 : 0) { +				conn->options.flags |= CLIENT_LOCAL_FILES; +			} else { +				conn->options.flags &= ~CLIENT_LOCAL_FILES; +			} +			break; +#ifdef WHEN_SUPPORTED_BY_MYSQLI +		case MYSQL_OPT_COMPRESS: +#endif +		case MYSQL_INIT_COMMAND: +		case MYSQL_READ_DEFAULT_FILE: +		case MYSQL_READ_DEFAULT_GROUP: +#ifdef WHEN_SUPPORTED_BY_MYSQLI +		case MYSQL_SET_CLIENT_IP: +		case MYSQL_REPORT_DATA_TRUNCATION: +		case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: +#endif +			/* currently not supported. Todo!! */ +			break; +		case MYSQL_SET_CHARSET_NAME: +			conn->options.charset_name = pestrdup(value, conn->persistent); +			break; +#ifdef WHEN_SUPPORTED_BY_MYSQLI +		case MYSQL_SET_CHARSET_DIR: +		case MYSQL_OPT_RECONNECT: +		case MYSQL_OPT_PROTOCOL: +			/* we don't need external character sets, all character sets are +			   compiled in. For compatibility we just ignore this setting. +			   Same for protocol, we don't support old protocol */ +		case MYSQL_OPT_USE_REMOTE_CONNECTION: +		case MYSQL_OPT_USE_EMBEDDED_CONNECTION: +		case MYSQL_OPT_GUESS_CONNECTION: +			/* todo: throw an error, we don't support embedded */ +			break; +#endif + +#ifdef WHEN_SUPPORTED_BY_MYSQLI +		case MYSQL_OPT_NAMED_PIPE: +		case MYSQL_SHARED_MEMORY_BASE_NAME: +		case MYSQL_OPT_USE_RESULT: +		case MYSQL_SECURE_AUTH: +			/* not sure, todo ? */ +#endif +		default: +			DBG_RETURN(FAIL); +	} +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ _mysqlnd_conn::use_result */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) +{ +	MYSQLND_RES *result; + +	DBG_ENTER("mysqlnd_conn::use_result"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	if (!conn->current_result) { +		DBG_RETURN(NULL); +	} + +	/* Nothing to store for UPSERT/LOAD DATA */ +	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +						 mysqlnd_out_of_sync); +		DBG_ERR("Command out of sync"); +		DBG_RETURN(NULL); +	} + +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS); + +	result = conn->current_result; +	conn->current_result = NULL; +	result->conn = conn->m->get_reference(conn); + +	result = result->m.use_result(result, FALSE TSRMLS_CC); +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ _mysqlnd_conn::store_result */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) +{ +	MYSQLND_RES *result; + +	DBG_ENTER("mysqlnd_conn::store_result"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); + +	if (!conn->current_result) { +		DBG_RETURN(NULL); +	} + +	/* Nothing to store for UPSERT/LOAD DATA*/ +	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +						 mysqlnd_out_of_sync); +		DBG_ERR("Command out of sync"); +		DBG_RETURN(NULL); +	} + +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); + +	result = conn->current_result; +	conn->current_result = NULL; + +	result = result->m.store_result(result, conn, FALSE TSRMLS_CC); +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_conn::get_connection_stats */ +void +MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn, +												   zval *return_value +												   TSRMLS_DC ZEND_FILE_LINE_DC) +{ +	DBG_ENTER("mysqlnd_conn::get_connection_stats"); +	DBG_INF_FMT("conn=%llu", conn->thread_id); +	mysqlnd_fill_stats_hash(&(conn->stats), return_value TSRMLS_CC ZEND_FILE_LINE_CC); +	DBG_VOID_RETURN; +} +/* }}} */ + + +MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC); + + +MYSQLND_CLASS_METHODS_START(mysqlnd_conn) +	MYSQLND_METHOD(mysqlnd_conn, escape_string), +	MYSQLND_METHOD(mysqlnd_conn, set_charset), +	MYSQLND_METHOD(mysqlnd_conn, query), +	MYSQLND_METHOD(mysqlnd_conn, use_result), +	MYSQLND_METHOD(mysqlnd_conn, store_result), +	MYSQLND_METHOD(mysqlnd_conn, next_result), +	MYSQLND_METHOD(mysqlnd_conn, more_results), + +	_mysqlnd_stmt_init, + +	MYSQLND_METHOD(mysqlnd_conn, shutdown), +	MYSQLND_METHOD(mysqlnd_conn, refresh), + +	MYSQLND_METHOD(mysqlnd_conn, ping), +	MYSQLND_METHOD(mysqlnd_conn, kill), +	MYSQLND_METHOD(mysqlnd_conn, select_db), +	MYSQLND_METHOD(mysqlnd_conn, dump_debug_info), +	MYSQLND_METHOD(mysqlnd_conn, change_user), + +	MYSQLND_METHOD(mysqlnd_conn, errno), +	MYSQLND_METHOD(mysqlnd_conn, error), +	MYSQLND_METHOD(mysqlnd_conn, sqlstate), +	MYSQLND_METHOD(mysqlnd_conn, thread_id), + +	MYSQLND_METHOD(mysqlnd_conn, get_connection_stats), + +	MYSQLND_METHOD(mysqlnd_conn, get_server_version), +	MYSQLND_METHOD(mysqlnd_conn, get_server_info), +	MYSQLND_METHOD(mysqlnd_conn, stat), +	MYSQLND_METHOD(mysqlnd_conn, get_host_info), +	MYSQLND_METHOD(mysqlnd_conn, get_proto_info), +	MYSQLND_METHOD(mysqlnd_conn, info), +	MYSQLND_METHOD(mysqlnd_conn, charset_name), +	MYSQLND_METHOD(mysqlnd_conn, list_fields), +	MYSQLND_METHOD(mysqlnd_conn, list_method), + +	MYSQLND_METHOD(mysqlnd_conn, insert_id), +	MYSQLND_METHOD(mysqlnd_conn, affected_rows), +	MYSQLND_METHOD(mysqlnd_conn, warning_count), +	MYSQLND_METHOD(mysqlnd_conn, field_count), + +	MYSQLND_METHOD(mysqlnd_conn, set_server_option), +	MYSQLND_METHOD(mysqlnd_conn, set_client_option), +	MYSQLND_METHOD(mysqlnd_conn, free_contents), +	MYSQLND_METHOD(mysqlnd_conn, close), + +	MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor), + +	MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference), +	MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference), +MYSQLND_CLASS_METHODS_END; + + +/* {{{ mysqlnd_init */ +PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC) +{ +	MYSQLND *ret = mnd_pecalloc(1, sizeof(MYSQLND), persistent); + +	DBG_ENTER("mysqlnd_init"); +	DBG_INF_FMT("persistent=%d", persistent); + +	SET_ERROR_AFF_ROWS(ret); +	ret->persistent = persistent; + +	ret->m = & mysqlnd_mysqlnd_conn_methods; +	ret->m->get_reference(ret); + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_functions[] + * + * Every user visible function must have an entry in mysqlnd_functions[]. + */ +static zend_function_entry mysqlnd_functions[] = { +	{NULL, NULL, NULL}	/* Must be the last line in mysqlnd_functions[] */ +}; +/* }}} */ + + +/* {{{ mysqlnd_minfo_print_hash */ +#if PHP_MAJOR_VERSION >= 6 +PHPAPI void mysqlnd_minfo_print_hash(zval *values) +{ +	zval **values_entry; +	HashPosition pos_values; + +	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values); +	while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values), +										(void **)&values_entry, &pos_values) == SUCCESS) { +		TSRMLS_FETCH(); +		zstr	string_key; +		uint	string_key_len; +		ulong	num_key; +		char 	*s = NULL; + +		zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values); + +		convert_to_string(*values_entry); + +		if (UG(unicode)) { +			int s_len; +			if (zend_unicode_to_string(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), +									   &s, &s_len, string_key.u, string_key_len TSRMLS_CC) == SUCCESS) { +				php_info_print_table_row(2, s, Z_STRVAL_PP(values_entry)); +			} +			if (s) { +				mnd_efree(s); +			} +		} else { +			php_info_print_table_row(2, string_key.s, Z_STRVAL_PP(values_entry)); +		} + +		zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values); +	} +} +#else +void mysqlnd_minfo_print_hash(zval *values) +{ +	zval **values_entry; +	HashPosition pos_values; + +	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values); +	while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&values_entry, &pos_values) == SUCCESS) { +		char	*string_key; +		uint	string_key_len; +		ulong	num_key; + +		zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values); + +		convert_to_string(*values_entry); +		php_info_print_table_row(2, string_key, Z_STRVAL_PP(values_entry)); + +		zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values); +	} +} +#endif +/* }}} */ + + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(mysqlnd) +{ +	char buf[32]; +	zval values; + +	php_info_print_table_start(); +	php_info_print_table_header(2, "mysqlnd", "enabled"); +	php_info_print_table_row(2, "Version", mysqlnd_get_client_info()); + +	/* Print client stats */ +	php_info_print_table_header(2, "Client statistics", ""); +	mysqlnd_get_client_stats(&values); +	mysqlnd_minfo_print_hash(&values); +	php_info_print_table_row(2, "Collecting statistics", MYSQLND_G(collect_statistics)? "Yes":"No"); +	php_info_print_table_row(2, "Collecting memory statistics", MYSQLND_G(collect_memory_statistics)? "Yes":"No"); + +	snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_cmd_buffer_size)); +	php_info_print_table_row(2, "Command buffer size", buf); +	snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size)); +	php_info_print_table_row(2, "Read buffer size", buf); + +	zval_dtor(&values); +	php_info_print_table_end(); +} +/* }}} */ + + +ZEND_DECLARE_MODULE_GLOBALS(mysqlnd); + + +/* {{{ PHP_GINIT_FUNCTION + */ +static PHP_GINIT_FUNCTION(mysqlnd) +{ +	mysqlnd_globals->collect_statistics = TRUE; +	mysqlnd_globals->collect_memory_statistics = FALSE; +	mysqlnd_globals->debug = NULL;	/* The actual string */ +	mysqlnd_globals->dbg = NULL;	/* The DBG object*/ +	mysqlnd_globals->net_cmd_buffer_size = 2048; +	mysqlnd_globals->net_read_buffer_size = 32768; +} +/* }}} */ + + +/* {{{ PHP_INI_BEGIN +*/ +PHP_INI_BEGIN() +	STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics",	"1", 	PHP_INI_ALL, OnUpdateBool,	collect_statistics, zend_mysqlnd_globals, mysqlnd_globals) +	STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics",	"0", 	PHP_INI_SYSTEM, OnUpdateBool,	collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals) +	STD_PHP_INI_ENTRY("mysqlnd.debug",					NULL, 	PHP_INI_SYSTEM, OnUpdateString,	debug, zend_mysqlnd_globals, mysqlnd_globals) +	STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size",	"2048",	PHP_INI_ALL,	OnUpdateLong,	net_cmd_buffer_size,	zend_mysqlnd_globals,		mysqlnd_globals) +	STD_PHP_INI_ENTRY("mysqlnd.net_read_buffer_size",	"32768",PHP_INI_ALL,	OnUpdateLong,	net_read_buffer_size,	zend_mysqlnd_globals,		mysqlnd_globals) +PHP_INI_END() +/* }}} */ + + +/* {{{ PHP_MINIT_FUNCTION + */ +static PHP_MINIT_FUNCTION(mysqlnd) +{ +	REGISTER_INI_ENTRIES(); + +	mysqlnd_library_init(); +	return SUCCESS; +} +/* }}} */ + + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +static PHP_MSHUTDOWN_FUNCTION(mysqlnd) +{ +	mysqlnd_library_end(); + +	UNREGISTER_INI_ENTRIES(); +	return SUCCESS; +} +/* }}} */ + + +#if PHP_DEBUG +/* {{{ PHP_RINIT_FUNCTION + */ +static PHP_RINIT_FUNCTION(mysqlnd) +{ +#ifdef PHP_DEBUG +	if (MYSQLND_G(debug)) { +		MYSQLND_DEBUG *dbg = mysqlnd_debug_init(TSRMLS_C); +		if (!dbg) { +			return FAILURE; +		} +		dbg->m->set_mode(dbg, MYSQLND_G(debug)); +		MYSQLND_G(dbg) = dbg;	 +	} +#endif +	return SUCCESS; +} +/* }}} */ + + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +static PHP_RSHUTDOWN_FUNCTION(mysqlnd) +{ +#ifdef PHP_DEBUG +	MYSQLND_DEBUG *dbg = MYSQLND_G(dbg); +	DBG_ENTER("RSHUTDOWN"); +	if (dbg) { +		dbg->m->close(dbg); +		dbg->m->free(dbg); +		MYSQLND_G(dbg) = NULL; +	} +#endif +	return SUCCESS; +} +/* }}} */ +#endif + + +/* {{{ mysqlnd_module_entry + */ +zend_module_entry mysqlnd_module_entry = { +	STANDARD_MODULE_HEADER, +	"mysqlnd", +	mysqlnd_functions, +	PHP_MINIT(mysqlnd), +	PHP_MSHUTDOWN(mysqlnd), +#if PHP_DEBUG +	PHP_RINIT(mysqlnd), +	PHP_RSHUTDOWN(mysqlnd), +#else +	NULL, +	NULL, +#endif +	PHP_MINFO(mysqlnd), +	MYSQLND_VERSION, +	PHP_MODULE_GLOBALS(mysqlnd), +	PHP_GINIT(mysqlnd), +	NULL, +	NULL, +	STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h new file mode 100644 index 0000000000..f648207fdc --- /dev/null +++ b/ext/mysqlnd/mysqlnd.h @@ -0,0 +1,354 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_H +#define MYSQLND_H + +#define MYSQLND_VERSION "mysqlnd 5.0.2-dev - 070928 - $Revision$" +#define MYSQLND_VERSION_ID 50002 + +/* This forces inlining of some accessor functions */ +#define MYSQLND_USE_OPTIMISATIONS 0 + +/* #define MYSQLND_STRING_TO_INT_CONVERSION */ +/* +  This force mysqlnd to do a single (or more depending on ammount of data) +  non-blocking read() calls before sending a command to the server. Useful +  for debugging, if previous function hasn't consumed all the output sent +  to it - like stmt_send_long_data() error because the data was larger that +  max_allowed_packet_size, and COM_STMT_SEND_LONG_DATA by protocol doesn't +  use response packets, thus letting the next command to fail miserably, if +  the connector implementor is not aware of this deficiency. Should be off +  on production systems, if of course measured performance degradation is not +  minimal. +*/ +#if PHP_DEBUG +#define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1 +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#include "mysqlnd_portability.h" +#include "mysqlnd_enum_n_def.h" +#include "mysqlnd_structs.h" + + +/* Library related */ +#define mysqlnd_restart_psession(conn) _mysqlnd_restart_psession((conn) TSRMLS_CC) +PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC); +PHPAPI void mysqlnd_end_psession(MYSQLND *conn); +PHPAPI void mysqlnd_minfo_print_hash(zval *values); +#define mysqlnd_thread_safe()	TRUE + +PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint charsetno); +PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const charsetname); + + +/* Connect */ +#define mysqlnd_init(persistent) _mysqlnd_init((persistent) TSRMLS_CC) +PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, +						  char *host, char *user, +						  char *passwd, unsigned int passwd_len, +						  char *db, unsigned int db_len, +						  unsigned int port, +						  char *socket, +						  unsigned int mysql_flags, +						  MYSQLND_THD_ZVAL_PCACHE *zval_cache +						  TSRMLS_DC); + +#define mysqlnd_change_user(conn, user, passwd, db)		(conn)->m->change_user((conn), (user), (passwd), (db) TSRMLS_CC) + +#define mysqlnd_debug(x)								_mysqlnd_debug((x) TSRMLS_CC) +void _mysqlnd_debug(const char *mode TSRMLS_DC); + +/* Query */ +#define mysqlnd_fetch_into(result, flags, ret_val, ext)	(result)->m.fetch_into((result), (flags), (ret_val), (ext) TSRMLS_CC ZEND_FILE_LINE_CC) +#define mysqlnd_fetch_all(result, flags, return_value)	(result)->m.fetch_all((result), (flags), (return_value) TSRMLS_CC ZEND_FILE_LINE_CC) +#define mysqlnd_result_fetch_field_data(res,offset,ret)	(res)->m.fetch_field_data((res), (offset), (ret) TSRMLS_CC) +#define mysqlnd_get_connection_stats(conn, values)		(conn)->m->get_statistics((conn),  (values) TSRMLS_CC ZEND_FILE_LINE_CC) +#define mysqlnd_get_client_stats(values)				_mysqlnd_get_client_stats((values) TSRMLS_CC ZEND_FILE_LINE_CC) + +#define mysqlnd_close(conn,is_forced)					(conn)->m->close((conn), (is_forced) TSRMLS_CC) +#define mysqlnd_query(conn, query_str, query_len)		(conn)->m->query((conn), (query_str), (query_len) TSRMLS_CC) +#define mysqlnd_unbuffered_skip_result(result)			(result)->m.skip_result((result) TSRMLS_CC) + + +#define mysqlnd_use_result(conn)		(conn)->m->use_result((conn) TSRMLS_CC) +#define mysqlnd_store_result(conn)		(conn)->m->store_result((conn) TSRMLS_CC) +#define mysqlnd_next_result(conn)		(conn)->m->next_result((conn) TSRMLS_CC) +#define mysqlnd_more_results(conn)		(conn)->m->more_results((conn)) +#define mysqlnd_free_result(r,e_or_i)	((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC) +#define mysqlnd_data_seek(result, row)	(result)->m.seek_data((result), (row) TSRMLS_CC) + +/*****************************************************************************************************/ +#if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1 + +/* Errors */ +#define mysqlnd_errno(conn)				(conn)->error_info.error_no +#define mysqlnd_error(conn)				(conn)->error_info.error +#define mysqlnd_sqlstate(conn)  		((conn)->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) + +/* Charset */ +#define mysqlnd_character_set_name(conn) (conn)->charset->name + +/* Simple metadata */ +#define mysqlnd_field_count(conn)		(conn)->field_count +#define mysqlnd_insert_id(conn)			(conn)->upsert_status.last_insert_id +#define mysqlnd_affected_rows(conn)		(conn)->upsert_status.affected_rows +#define mysqlnd_warning_count(conn)		(conn)->upsert_status.warning_count +#define mysqlnd_info(conn)  			(conn)->last_message +#define mysqlnd_get_server_info(conn)	(conn)->server_version +#define mysqlnd_get_host_info(conn)		(conn)->host_info +#define mysqlnd_get_proto_info(conn)	(conn)->protocol_version +#define mysqlnd_thread_id(conn)			(conn)->thread_id + +#define mysqlnd_num_rows(result)		((result)->data? (result)->data->row_count:0) +#define mysqlnd_num_fields(result)		(result)->field_count + +#define mysqlnd_fetch_lengths(result)	((result)->m.fetch_lengths? (result)->m.fetch_lengths((result)):NULL) + +#define mysqlnd_field_seek(result, ofs)			(result)->m.seek_field((result), (ofs)) +#define mysqlnd_field_tell(result)				(result)->meta? (result)->meta->current_field:0) +#define mysqlnd_fetch_field(result)				(result)->m.fetch_field((result) TSRMLS_CC) +#define mysqlnd_fetch_field_direct(result,fnr)	((result)->meta? &((result)->meta->fields[(fnr)]):NULL) + +/* mysqlnd metadata */ +#define mysqlnd_get_client_info()		MYSQLND_VERSION +#define mysqlnd_get_client_version()	MYSQLND_VERSION_ID + +/* PS */ +#define mysqlnd_stmt_insert_id(stmt)		(stmt)->upsert_status.last_insert_id +#define mysqlnd_stmt_affected_rows(stmt)	(stmt)->upsert_status.affected_rows +#define mysqlnd_stmt_num_rows(stmt)			(stmt)->result? mysqlnd_num_rows((stmt)->result):0 +#define mysqlnd_stmt_param_count(stmt)		(stmt)->param_count +#define mysqlnd_stmt_field_count(stmt)		(stmt)->field_count +#define mysqlnd_stmt_warning_count(stmt)	(stmt)->upsert_status.warning_count +#define mysqlnd_stmt_errno(stmt)			(stmt)->error_info.error_no +#define mysqlnd_stmt_error(stmt)			(stmt)->error_info.error +#define mysqlnd_stmt_sqlstate(stmt)			((stmt)->error_info.sqlstate[0] ? (stmt)->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) + + + +/*****************************************************************************************************/ +#else /* Using plain functions */ +/*****************************************************************************************************/ + +/* Errors */ +#define mysqlnd_errno(conn)				(conn)->m->get_error_no((conn)) +#define mysqlnd_error(conn)				(conn)->m->get_error_str((conn)) +#define mysqlnd_sqlstate(conn)  		(conn)->m->get_sqlstate((conn)) + +/* Charset */ +#define mysqlnd_character_set_name(conn) (conn)->m->charset_name((conn)) + +/* Simple metadata */ +#define mysqlnd_field_count(conn)		(conn)->m->get_field_count((conn)) +#define mysqlnd_insert_id(conn)			(conn)->m->get_last_insert_id((conn)) +#define mysqlnd_affected_rows(conn)		(conn)->m->get_affected_rows((conn)) +#define mysqlnd_warning_count(conn)		(conn)->m->get_warning_count((conn)) +#define mysqlnd_info(conn)				(conn)->m->get_last_message((conn)) +#define mysqlnd_get_server_info(conn)	(conn)->m->get_server_information((conn)) +#define mysqlnd_get_host_info(conn)		(conn)->m->get_host_information((conn)) +#define mysqlnd_get_proto_info(conn)	(conn)->m->get_protocol_information((conn)) +#define mysqlnd_thread_id(conn)			(conn)->m->get_thread_id((conn)) + +#define mysqlnd_num_rows(result)		(result)->m.num_rows((result)) +#define mysqlnd_num_fields(result)		(result)->m.num_fields((result)) + +PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result); + +#define mysqlnd_field_seek(result, ofs)			(result)->m.seek_field((result), (ofs)) +#define mysqlnd_field_tell(result)				(result)->m.field_tell((result)) +#define mysqlnd_fetch_field(result)				(result)->m.fetch_field((result) TSRMLS_CC) +#define mysqlnd_fetch_field_direct(result,fnr)	(result)->m.fetch_field_direct((result), (fnr) TSRMLS_CC) + +/* mysqlnd metadata */ +PHPAPI const char *	mysqlnd_get_client_info(); +PHPAPI unsigned int	mysqlnd_get_client_version(); + +/* PS */ +#define mysqlnd_stmt_insert_id(stmt)		(stmt)->m->get_last_insert_id((stmt)) +#define mysqlnd_stmt_affected_rows(stmt)	(stmt)->m->get_affected_rows((stmt)) +#define mysqlnd_stmt_num_rows(stmt)			(stmt)->m->get_num_rows((stmt)) +#define mysqlnd_stmt_param_count(stmt)		(stmt)->m->get_param_count((stmt)) +#define mysqlnd_stmt_field_count(stmt)		(stmt)->m->get_field_count((stmt)) +#define mysqlnd_stmt_warning_count(stmt)	(stmt)->m->get_warning_count((stmt)) +#define mysqlnd_stmt_errno(stmt)			(stmt)->m->get_error_no((stmt)) +#define mysqlnd_stmt_error(stmt)			(stmt)->m->get_error_str((stmt)) +#define mysqlnd_stmt_sqlstate(stmt)			(stmt)->m->get_sqlstate((stmt)) +#endif /* MYSQLND_USE_OPTIMISATIONS */ +/*****************************************************************************************************/ + + + +PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type); + +/* LOAD DATA LOCAL */ +PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn); +PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname); + +/* Simple commands */ +#define mysqlnd_autocommit(conn, mode)		(conn)->m->query((conn),(mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", 16 TSRMLS_CC) +#define mysqlnd_commit(conn)				(conn)->m->query((conn), "COMMIT", sizeof("COMMIT")-1 TSRMLS_CC) +#define mysqlnd_rollback(conn)				(conn)->m->query((conn), "ROLLBACK", sizeof("ROLLBACK")-1 TSRMLS_CC) +#define mysqlnd_list_dbs(conn, wild)		(conn)->m->list_method((conn), wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL TSRMLS_CC) +#define mysqlnd_list_fields(conn, tab,wild)	(conn)->m->list_fields((conn), (tab), (wild) TSRMLS_CC) +#define mysqlnd_list_processes(conn)		(conn)->m->list_method((conn), "SHOW PROCESSLIST", NULL, NULL TSRMLS_CC) +#define mysqlnd_list_tables(conn, wild)		(conn)->m->list_method((conn), wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL TSRMLS_CC) +#define mysqlnd_dump_debug_info(conn)		(conn)->m->server_dump_debug_information((conn) TSRMLS_CC) +#define mysqlnd_select_db(conn, db, db_len)	(conn)->m->select_db((conn), (db), (db_len) TSRMLS_CC) +#define mysqlnd_ping(conn)					(conn)->m->ping((conn) TSRMLS_CC) +#define mysqlnd_kill(conn, pid)				(conn)->m->kill_connection((conn), (pid) TSRMLS_CC) +#define mysqlnd_refresh(conn, options)		(conn)->m->refresh_server((conn), (options) TSRMLS_CC)  +#define mysqlnd_shutdown(conn, level)		(conn)->m->shutdown_server((conn), (level) TSRMLS_CC) +#define mysqlnd_get_server_version(conn)	(conn)->m->get_server_version((conn)) +#define mysqlnd_set_character_set(conn, cs)	(conn)->m->set_charset((conn), (cs) TSRMLS_CC) +#define mysqlnd_stat(conn, msg, msg_len)	(conn)->m->get_server_statistics((conn), (msg), (msg_len) TSRMLS_CC) +#define mysqlnd_options(conn, opt, value)	(conn)->m->set_client_option((conn), (opt), (value) TSRMLS_CC) +#define mysqlnd_set_server_option(conn, op)	(conn)->m->set_server_option((conn), (op) TSRMLS_CC) + +/* Escaping */ +#define mysqlnd_real_escape_string(conn, newstr, escapestr, escapestr_len) \ +		(conn)->m->escape_string((conn), (newstr), (escapestr), (escapestr_len) TSRMLS_CC) +#define mysqlnd_escape_string(newstr, escapestr, escapestr_len) \ +		mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len) TSRMLS_CC) + +PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC); + + +/* PS */ +#define mysqlnd_stmt_init(conn)				(conn)->m->stmt_init((conn) TSRMLS_CC) +#define mysqlnd_stmt_store_result(stmt)		(!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL)) +#define mysqlnd_stmt_get_result(stmt)		(stmt)->m->get_result((stmt) TSRMLS_CC) +#define mysqlnd_stmt_data_seek(stmt, row)	(stmt)->m->seek_data((stmt), (row) TSRMLS_CC) +#define mysqlnd_stmt_prepare(stmt, q, qlen)	(stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC) +#define mysqlnd_stmt_execute(stmt) 			(stmt)->m->execute((stmt) TSRMLS_CC) +#define mysqlnd_stmt_send_long_data(s,p,d,l) (s)->m->send_long_data((s), (p), (d), (l) TSRMLS_CC) +#define mysqlnd_stmt_bind_param(stmt,bind)	(stmt)->m->bind_param((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_result(stmt,bind)	(stmt)->m->bind_result((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_param_metadata(stmt)	(stmt)->m->get_parameter_metadata((stmt)) +#define mysqlnd_stmt_result_metadata(stmt)	(stmt)->m->get_result_metadata((stmt) TSRMLS_CC) + +#define	mysqlnd_stmt_free_result(stmt)		(stmt)->m->free_result((stmt) TSRMLS_CC) +#define	mysqlnd_stmt_close(stmt, implicit)	(stmt)->m->dtor((stmt), (implicit) TSRMLS_CC) +#define	mysqlnd_stmt_reset(stmt)			(stmt)->m->reset((stmt) TSRMLS_CC) + + +#define mysqlnd_stmt_attr_get(stmt, attr, value)	(stmt)->m->get_attribute((stmt), (attr), (value) TSRMLS_CC) +#define mysqlnd_stmt_attr_set(stmt, attr, value)	(stmt)->m->set_attribute((stmt), (attr), (value) TSRMLS_CC) + +#define mysqlnd_stmt_fetch(stmt, fetched)	(stmt)->m->fetch((stmt), (fetched) TSRMLS_CC) + + +/* Performance statistics */ +PHPAPI void			_mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); + +/* Persistent caching zval allocator */ +#define mysqlnd_palloc_init_cache(size)		_mysqlnd_palloc_init_cache((size) TSRMLS_CC) +#define mysqlnd_palloc_free_cache(cache)	_mysqlnd_palloc_free_cache((cache) TSRMLS_CC) +PHPAPI MYSQLND_ZVAL_PCACHE*		_mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC); +PHPAPI void 					_mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC); +PHPAPI void						mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, +													 zval *return_value); + +#define mysqlnd_palloc_rinit(cache)		_mysqlnd_palloc_rinit((cache) TSRMLS_CC) +#define mysqlnd_palloc_rshutdown(cache)	_mysqlnd_palloc_rshutdown((cache) TSRMLS_CC) +PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC); +PHPAPI void						 _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache TSRMLS_DC); + + +#define mysqlnd_palloc_init_thd_cache(cache) 			_mysqlnd_palloc_init_thd_cache((cache) TSRMLS_CC) +#define mysqlnd_palloc_free_thd_cache_reference(cache)	_mysqlnd_palloc_free_thd_cache_reference((cache) TSRMLS_CC) + +PHPAPI MYSQLND_THD_ZVAL_PCACHE*	_mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC); +MYSQLND_THD_ZVAL_PCACHE*		mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache); +PHPAPI void						_mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC); + + +/* There two should not be used from outside */ +void *	mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated TSRMLS_DC); +void	mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const cache, +									 enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC); + + + +/* ---------------------- QUERY CACHE ---------------*/ +struct st_mysqlnd_qcache { +	HashTable		*ht; +	unsigned int	references; +#ifdef ZTS +	MUTEX_T		LOCK_access; +#endif +}; + + +typedef struct st_mysqlnd_qcache_element { +	MYSQLND_RES_BUFFERED *data; +	MYSQLND_RES_METADATA *meta; +	const char * query; +	size_t	query_len; +} MYSQLND_QCACHE_ELEMENT; + + +PHPAPI MYSQLND_QCACHE *	mysqlnd_qcache_init_cache(); +PHPAPI MYSQLND_QCACHE *	mysqlnd_qcache_get_cache_reference(MYSQLND_QCACHE * const cache); +PHPAPI void				mysqlnd_qcache_free_cache_reference(MYSQLND_QCACHE **cache); +PHPAPI void				mysqlnd_qcache_stats(const MYSQLND_QCACHE * const cache, zval *return_value); +MYSQLND_RES * 		mysqlnd_qcache_get(MYSQLND_QCACHE * const cache, const char * query, +									   size_t query_len); +void				mysqlnd_qcache_put(MYSQLND_QCACHE * const cache, char * query, size_t query_len, +									   MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta); + + + +ZEND_BEGIN_MODULE_GLOBALS(mysqlnd) +	zend_bool		collect_statistics; +	zend_bool		collect_memory_statistics; +	char*			debug;	/* The actual string */ +	MYSQLND_DEBUG	*dbg;	/* The DBG object */ +	long			net_cmd_buffer_size; +	long			net_read_buffer_size; +ZEND_END_MODULE_GLOBALS(mysqlnd) + +ZEND_EXTERN_MODULE_GLOBALS(mysqlnd); + +#ifdef ZTS +#define MYSQLND_G(v) TSRMG(mysqlnd_globals_id, zend_mysqlnd_globals *, v) +#else +#define MYSQLND_G(v) (mysqlnd_globals.v) +#endif + + +#endif	/* MYSQLND_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c new file mode 100644 index 0000000000..9b3b819fcd --- /dev/null +++ b/ext/mysqlnd/mysqlnd_alloc.c @@ -0,0 +1,285 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ + +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_palloc.h" + + +#define MYSQLND_SILENT +#define MYSQLND_DONT_DUMP_STATS + +#define MYSQLND_ZVALS_MAX_CACHE 5000 + + +#if A0 +/* Caching zval allocator */ +zval * 				mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache); +void 				mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache); +MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(); +MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache); +void				mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache); +#endif + + +/* + The cache line is a big contiguous array of zval pointers. + Because the CPU cache will cache starting from an address, and not + before it, then we have to organize our structure according to this. + Thus, if 'last_added' is valid pointer (not NULL) then last_added is + increased. When zval is cached, if there is room, last_added is decreased + and then the zval pointer will be assigned to it. This means that some + positions may become hot points and stay in the cache. + Imagine we have 5 pointers in a line + 1. last_added = list_item->ptr_line + cache->max_items; + 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL + 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL + 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL +    0x0 +	0x0 +	0x0 +	0x0  +	0x0 +	--- +    empty_position, always 0x0 <-- last_added + + 5. free_zval -> if (free_items++ != max_items) {// we can add more +                   *(--last_added) = zval_ptr; +				} +	(memory addresses increase downwards) +    0x0 +	0x0 +	0x0 +	0x0  +	0xA <-- last_added +	--- +	0x0 + + 6. free_zval -> if (free_items++ != max_items) {// we can add more +                   *(--last_added) = zval_ptr; +				} +    0x0 +	0x0 +	0x0 +	0xB <-- last_added +	0xA  +	--- +	0x0 + + 7. free_zval -> if (free_items++ != max_items) {// we can add more +                   *(--last_added) = zval_ptr; +				} +    0x0 +	0x0 +	0xC <-- last_added +	0xB  +	0xA  +	--- +	0x0 + + 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +    0x0 +	0x0 +	0x0  +	0xB <-- last_added +	0xA  +	--- +	0x0 + + 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +    0x0 +	0x0 +	0x0  +	0x0  +	0xA <-- last_added +	--- +	0x0 + +10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +    0x0 +	0x0 +	0x0  +	0x0  +	0x0  +	--- +	0x0 <-- last_added + +*/ + + +zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache) +{ +	zval *ret = NULL; + +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_alloc_get_zval %p] *last_added=%p free_items=%d  ", cache, cache? cache->free_list->last_added:NULL, cache->free_items); +#endif + +	if (cache) { +		if ((ret = *cache->free_list->last_added)) { +			*cache->free_list->last_added++ = NULL; + +			--cache->free_items; +			++cache->get_hits; +		} else { +			++cache->get_misses; +		} +	} +	if (!ret) { +		ALLOC_ZVAL(ret); +	} +	INIT_PZVAL(ret); + +#ifndef MYSQLND_SILENT +	php_printf("ret=%p\n", ret); +#endif +	return ret; +} + +static +void mysqlnd_alloc_cache_prealloc(MYSQLND_ZVAL_CACHE * const cache, unsigned int count) +{ +	zval *data; +	cache->free_items = count; +	while (count--) { +		MAKE_STD_ZVAL(data); +		ZVAL_NULL(data); +#ifndef MYSQLND_SILENT +		php_printf("[mysqlnd_alloc_prealloc %p] items=%d data=%p\n", cache, cache->free_items, data); +#endif +		 +		*(--cache->free_list->last_added) = data; +	} +} + +void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache) +{ +	if (!cache || ZVAL_REFCOUNT(*zv) > 1 || cache->max_items == cache->free_items) { +#ifndef MYSQLND_SILENT +		php_printf("[mysqlnd_alloc_zval_ptr_dtor %p]1 last_added-1=%p *zv=%p\n", cache->free_list->last_added, *zv); +#endif +		/* We can't cache zval's with refcount > 1 */ +		zval_ptr_dtor(zv); +		if (cache) { +			if (cache->max_items == cache->free_items) { +				++cache->put_full_misses; +			} else { +				++cache->put_refcount_misses; +			} +		} +	} else { +		/* refcount is 1 and there is place. Go, cache it! */ +		++cache->free_items; +		zval_dtor(*zv); +		ZVAL_NULL(*zv); +		*(--cache->free_list->last_added) = *zv; +		++cache->put_hits; +	} +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_alloc_zval_ptr_dtor %p] free_items=%d\n", cache, cache->free_items); +#endif +} + + +MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(void) +{ +	MYSQLND_ZVAL_CACHE *ret = ecalloc(1, sizeof(MYSQLND_ZVAL_CACHE)); + +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_alloc_init_cache %p]\n", ret); +#endif + +	ret->max_items = MYSQLND_ZVALS_MAX_CACHE; +	ret->free_items = 0; +	ret->references = 1; + +	/* Let's have always one, so we don't need to do a check in get_zval */ +	ret->free_list		= ecalloc(1, sizeof(struct st_mysqlnd_zval_list)); + +	/* One more for empty position of last_added */ +	ret->free_list->ptr_line = ecalloc(ret->max_items + 1, sizeof(zval *)); +	ret->free_list->last_added = ret->free_list->ptr_line + ret->max_items; + +	mysqlnd_alloc_cache_prealloc(ret, (ret->max_items / 100) * 100); + +	return ret; +} + + +MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache) +{ +	if (cache) { +		cache->references++; +	} +	return cache; +} + + +static +void mysqlnd_alloc_free_cache(MYSQLND_ZVAL_CACHE *cache) +{ +#ifndef MYSQLND_SILENT +	uint i = 0; +	php_printf("[mysqlnd_alloc_free_cache %p]\n", cache); +#endif + +	while (*cache->free_list->last_added) { +#ifndef MYSQLND_SILENT +		php_printf("\t[free_item %d  %p]\n", i++, *cache->free_list->last_added); +#endif +		zval_ptr_dtor(cache->free_list->last_added); +		cache->free_list->last_added++; +	} +#ifndef MYSQLND_DONT_DUMP_STATS +	php_printf("CACHE STATS:\n\tGET\n\t\tHITS:%lu\n\t\tMISSES=%lu\n\t\tHIT RATIO=%1.3f\n\t" +				"PUT\n\t\tHITS:%lu\n\t\tFULL_MISS=%lu\n\t\tREFC_MISS=%lu\n\t\tHIT RATIO=%1.3f\n\n", +		cache->get_hits, cache->get_misses, (1.0*cache->get_hits/(cache->get_hits + cache->get_misses)), +		cache->put_hits, cache->put_full_misses, cache->put_refcount_misses, +		(1.0 * cache->put_hits / (cache->put_hits + cache->put_full_misses + cache->put_refcount_misses))); +#endif +	efree(cache->free_list->ptr_line); +	efree(cache->free_list); +	efree(cache); +} + + + +void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache) +{ +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_alloc_free_cache_reference %p] refs=%d\n", *cache, (*cache)->references); +#endif +	if (*cache && --(*cache)->references == 0) { +		mysqlnd_alloc_free_cache(*cache); +	} +	*cache = NULL; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c new file mode 100644 index 0000000000..cd6112af9f --- /dev/null +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -0,0 +1,603 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ +#include "php.h" +#include "php_globals.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" + +/* {{{ utf8 functions */ + +static uint check_mb_utf8_sequence(const char *start, const char *end) +{ +	zend_uchar	c; + +	if (start >= end) { +		return 0; +	} + +	c = (zend_uchar) start[0]; + +	if (c < 0x80) { +		return 1;		/* single byte character */ +	} +	if (c < 0xC2) { +		return 0;		/* invalid mb character */ +	} +	if (c < 0xE0) { +		if (start + 2 > end) { +			return 0;	/* too small */ +		} +		if (!(((zend_uchar)start[1] ^ 0x80) < 0x40)) { +			return 0; +		} +		return 2; +	} +	if (c < 0xF0) { +		if (start + 3 > end) { +			return 0;	/* too small */ +		} +		if (!(((zend_uchar)start[1] ^ 0x80) < 0x40 && ((zend_uchar)start[2] ^ 0x80) < 0x40 && +			(c >= 0xE1 || (zend_uchar)start[1] >= 0xA0))) { +			return 0;	/* invalid utf8 character */ +		} +		return 3; +	} +	return 0; +} + +static uint check_mb_utf8_valid(const char *start, const char *end) +{ +	uint len = check_mb_utf8_sequence(start, end); +	return (len > 1)? len:0; +} + +static uint mysqlnd_mbcharlen_utf8(uint utf8) +{ +	if (utf8 < 0x80) { +		return 1;		/* single byte character */ +	} +	if (utf8 < 0xC2) { +		return 0;		/* invalid multibyte header */ +	} +	if (utf8 < 0xE0) { +		return 2;		/* double byte character */ +	} +	if (utf8 < 0xF0) { +		return 3;		/* triple byte character */ +	} +	/* We still don't support characters out of the BMP */ + +	return 0; +} +/* }}} */ + + +/* {{{ big5 functions */ +#define valid_big5head(c)	(0xA1 <= (uint)(c) && (uint)(c) <= 0xF9) +#define valid_big5tail(c)	((0x40 <= (uint)(c) && (uint)(c) <= 0x7E) || \ +							(0xA1 <= (uint)(c) && (uint)(c) <= 0xFE)) + +#define isbig5code(c,d) (isbig5head(c) && isbig5tail(d)) + +static uint check_mb_big5(const char *start, const char *end) +{ +	return (valid_big5head(*(start)) && (end - start) > 1 && valid_big5tail(*(start + 1)) ? 2 : 0); +} + + +static uint mysqlnd_mbcharlen_big5(uint big5) +{ +	return (valid_big5head(big5)) ? 2 : 1; +} +/* }}} */ + + +/* {{{ cp932 functions */ +#define valid_cp932head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && c <= 0xFC)) +#define valid_cp932tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && c <= 0xFC)) + + +static uint check_mb_cp932(const char *start, const char *end) +{ +	return (valid_cp932head((zend_uchar)start[0]) && (end - start >  1) && +			valid_cp932tail((zend_uchar)start[1])) ? 2 : 0; +} + + +static uint mysqlnd_mbcharlen_cp932(uint cp932) +{ +	return (valid_cp932head((zend_uchar)cp932)) ? 2 : 1; +} +/* }}} */ + + +/* {{{ euckr functions */ +#define valid_euckr(c)	((0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE)) + +static uint check_mb_euckr(const char *start, const char *end) +{ +	if (end - start <= 1) { +		return 0;	/* invalid length */ +	} +	if (*(zend_uchar *)start < 0x80) { +		return 0;	/* invalid euckr character */ +	} +	if (valid_euckr(start[1])) { +		return 2; +	} +	return 0; +} + + +static uint mysqlnd_mbcharlen_euckr(uint kr) +{ +	return (valid_euckr(kr)) ? 2 : 1; +} +/* }}} */ + + +/* {{{ eucjpms functions */ +#define valid_eucjpms(c) 		(((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xFE) +#define valid_eucjpms_kata(c)	(((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xDF) +#define valid_eucjpms_ss2(c)	(((c) & 0xFF) == 0x8E) +#define valid_eucjpms_ss3(c)	(((c) & 0xFF) == 0x8F) + +static uint check_mb_eucjpms(const char *start, const char *end) +{ +	if (*((zend_uchar *)start) < 0x80) { +		return 0;	/* invalid eucjpms character */ +	} +	if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) { +		return 2; +	} +	if (valid_eucjpms_ss2(start[0]) && (end - start) > 1 && valid_eucjpms_kata(start[1])) { +		return 2; +	} +	if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) &&  +		valid_eucjpms(start[2])) { +		return 2; +	} +	return 0; +} + + +static uint mysqlnd_mbcharlen_eucjpms(uint jpms) +{ +	if (valid_eucjpms(jpms) || valid_eucjpms_ss2(jpms)) { +		return 2; +	} +	if (valid_eucjpms_ss3(jpms)) { +		return 3; +	} +	return 1; +} +/* }}} */ + + +/* {{{ gb2312 functions */ +#define valid_gb2312_head(c)	(0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xF7) +#define valid_gb2312_tail(c)	(0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE) + + +static uint check_mb_gb2312(const char *start, const char *end) +{ +	return (valid_gb2312_head((uint)start[0]) && end - start > 1 &&  +			valid_gb2312_tail((uint)start[1])) ? 2 : 0; +} + + +static uint mysqlnd_mbcharlen_gb2312(uint gb) +{ +	return (valid_gb2312_head(gb)) ? 2 : 1; +} +/* }}} */ + + +/* {{{ gbk functions */ +#define valid_gbk_head(c)	(0x81<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE) +#define valid_gbk_tail(c)	((0x40<=(zend_uchar)(c) && (zend_uchar)(c)<=0x7E) || (0x80<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE)) + +static uint check_mb_gbk(const char *start, const char *end) +{ +	return (valid_gbk_head(start[0]) && (end) - (start) > 1 && valid_gbk_tail(start[1])) ? 2 : 0; +} + +static uint mysqlnd_mbcharlen_gbk(uint gbk) +{ +	return (valid_gbk_head(gbk) ? 2 : 1); +} +/* }}} */ + + +/* {{{ sjis functions */ +#define valid_sjis_head(c)	((0x81 <= (c) && (c) <= 0x9F) && \ +							 (0xE0 <= (c) && (c) <= 0xFC)) +#define valid_sjis_tail(c)	((0x40 <= (c) && (c) <= 0x7E) && \ +							 (0x80 <= (c) && (c) <= 0x7C)) + + +static uint check_mb_sjis(const char *start, const char *end) +{ +	return (valid_sjis_head((zend_uchar)start[0]) && (end - start) > 1 && valid_sjis_tail((zend_uchar)start[1])) ? 2 : 0; +} + + +static uint mysqlnd_mbcharlen_sjis(uint sjis) +{ +	return (valid_sjis_head((zend_uchar)sjis)) ? 2 : 1; +} +/* }}} */ + + +/* {{{ ucs2 functions */ +static uint check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused))) +{ +	return 2; /* always 2 */ +} + +static uint mysqlnd_mbcharlen_ucs2(uint ucs2 __attribute((unused))) +{ +	return 2; /* always 2 */ +} +/* }}} */ + + +/* {{{ ujis functions */ +#define valid_ujis(c)     	((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xFE)) +#define valid_ujis_kata(c)  ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xDF)) +#define valid_ujis_ss2(c) 	(((c)&0xFF) == 0x8E) +#define valid_ujis_ss3(c) 	(((c)&0xFF) == 0x8F) + +static uint check_mb_ujis(const char *start, const char *end) +{ +	if (*(uchar*)start < 0x80) { +		return 0;	/* invalid ujis character */ +	} +	if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) { +		return 2; +	} +	if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) { +		return 2; +	} +	if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) { +		return 3; +	} +	return 0; +} + + +static uint mysqlnd_mbcharlen_ujis(uint ujis) +{ +	return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1); +} +/* }}} */ + + + +/* {{{ mysqlnd_charsets */ +const MYSQLND_CHARSET mysqlnd_charsets[] = +{ +	{   1, "big5","big5_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5}, +	{   3, "dec8", "dec8_swedisch_ci", 1, 1, 0, NULL, NULL}, +	{   4, "cp850", "cp850_general_ci", 1, 1, 0, NULL, NULL}, +	{   6, "hp8", "hp8_english_ci", 1, 1, 0, NULL, NULL}, +	{   7, "koi8r", "koi8r_general_ci", 1, 1, 0, NULL, NULL}, +	{   8, "latin1", "latin1_swedish_ci", 1, 1, 0, NULL, NULL}, +	{   9, "latin2", "latin2_general_ci", 1, 1, 0, NULL, NULL}, +	{  10, "swe7", "swe7_swedish_ci", 1, 1, 0, NULL, NULL}, +	{  11, "ascii", "ascii_general_ci", 1, 1, 0, NULL, NULL}, +	{  12, "ujis", "ujis_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis}, +	{  13, "sjis", "sjis_japanese_ci", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis}, +	{  16, "hebrew", "hebrew_general_ci", 1, 1, 0, NULL, NULL}, +	{  18, "tis620", "tis620_thai_ci", 1, 1, 0, NULL, NULL}, +	{  19, "euckr", "euckr_korean_ci", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr}, +	{  22, "koi8u", "koi8u_general_ci", 1, 1, 0, NULL, NULL}, +	{  24, "gb2312", "gb2312_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, +	{  25, "greek", "greek_general_ci", 1, 1, 0, NULL, NULL},	 +	{  26, "cp1250", "cp1250_general_ci", 1, 1, 0, NULL, NULL}, +	{  28, "gbk", "gbk_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk}, +	{  30, "latin5", "latin5_turkish_ci", 1, 1, 0, NULL, NULL}, +	{  32, "armscii8", "armscii8_general_ci", 1, 1, 0, NULL, NULL}, +	{  33, "utf8", "utf8_general_ci", 1, 2, 0, mysqlnd_mbcharlen_utf8,  check_mb_utf8_valid}, +	{  35, "ucs2", "ucs2_general_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{  36, "cp866", "cp866_general_ci", 1, 1, 0, NULL, NULL}, +	{  37, "keybcs2", "keybcs2_general_ci", 1, 1, 0, NULL, NULL}, +	{  38, "macce", "macce_general_ci", 1, 1, 0, NULL, NULL}, +	{  39, "macroman", "macroman_general_ci", 1, 1, 0, NULL, NULL}, +	{  40, "cp852", "cp852_general_ci", 1, 1, 0, NULL, NULL}, +	{  41, "latin7", "latin7_general_ci", 1, 1, 0, NULL, NULL}, +	{  51, "cp1251", "cp1251_general_ci", 1, 1, 0, NULL, NULL}, +	{  57, "cp1256", "cp1256_general_ci", 1, 1, 0, NULL, NULL}, +	{  59, "cp1257", "cp1257_general_ci", 1, 1, 0, NULL, NULL}, +	{  63, "binary", "binary", 1, 1, 0, NULL, NULL}, +	{  92, "geostd8", "geostd8_general_ci", 1, 1, 0, NULL, NULL}, +	{  95, "cp932", "cp932_japanese_ci", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932}, +	{  97, "eucjpms", "eucjpms_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms}, +	{   2, "latin2", "latin2_czech_cs", 1, 1, 0, NULL, NULL}, +	{   5, "latin1", "latin1_german_ci", 1, 1, 0, NULL, NULL}, +	{  14, "cp1251", "cp1251_bulgarian_ci", 1, 1, 0, NULL, NULL}, +	{  15, "latin1", "latin1_danish_ci", 1, 1, 0, NULL, NULL}, +	{  17, "filename", "filename", 1, 5, 1, NULL, NULL}, +	{  20, "latin7", "latin7_estonian_cs", 1, 1, 0, NULL, NULL}, +	{  21, "latin2", "latin2_hungarian_ci", 1, 1, 0, NULL, NULL}, +	{  23, "cp1251", "cp1251_ukrainian_ci", 1, 1, 0, NULL, NULL}, +	{  27, "latin2", "latin2_croatian_ci", 1, 1, 0, NULL, NULL}, +	{  29, "cp1257", "cp1257_lithunian_ci", 1, 1, 0, NULL, NULL}, +	{  31, "latin1", "latin1_german2_ci", 1, 1, 0, NULL, NULL}, +	{  34, "cp1250", "cp1250_czech_cs", 1, 1, 0, NULL, NULL}, +	{  42, "latin7", "latin7_general_cs", 1, 1, 0, NULL, NULL}, +	{  43, "macce", "macce_bin", 1, 1, 0, NULL, NULL}, +	{  44, "cp1250", "cp1250_croatian_ci", 1, 1, 0, NULL, NULL}, +	{  47, "latin1", "latin1_bin", 1, 1, 0, NULL, NULL}, +	{  48, "latin1", "latin1_general_ci", 1, 1, 0, NULL, NULL}, +	{  49, "latin1", "latin1_general_cs", 1, 1, 0, NULL, NULL}, +	{  50, "cp1251", "cp1251_bin", 1, 1, 0, NULL, NULL}, +	{  52, "cp1251", "cp1251_general_cs", 1, 1, 0, NULL, NULL}, +	{  53, "macroman", "macroman_bin", 1, 1, 0, NULL, NULL}, +	{  58, "cp1257", "cp1257_bin", 1, 1, 0, NULL, NULL}, +	{  60, "armascii8", "armascii8_bin", 1, 1, 0, NULL, NULL}, +	{  65, "ascii", "ascii_bin", 1, 1, 0, NULL, NULL}, +	{  66, "cp1250", "cp1250_bin", 1, 1, 0, NULL, NULL}, +	{  67, "cp1256", "cp1256_bin", 1, 1, 0, NULL, NULL}, +	{  68, "cp866", "cp866_bin", 1, 1, 0, NULL, NULL}, +	{  69, "dec8", "dec8_bin", 1, 1, 0, NULL, NULL}, +	{  70, "greek", "greek_bin", 1, 1, 0, NULL, NULL}, +	{  71, "hebew", "hebrew_bin", 1, 1, 0, NULL, NULL}, +	{  72, "hp8", "hp8_bin", 1, 1, 0, NULL, NULL}, +	{  73, "keybcs2", "keybcs2_bin", 1, 1, 0, NULL, NULL}, +	{  74, "koi8r", "koi8r_bin", 1, 1, 0, NULL, NULL}, +	{  75, "koi8u", "koi8u_bin", 1, 1, 0, NULL, NULL}, +	{  77, "latin2", "latin2_bin", 1, 1, 0, NULL, NULL}, +	{  78, "latin5", "latin5_bin", 1, 1, 0, NULL, NULL}, +	{  79, "latin7", "latin7_bin", 1, 1, 0, NULL, NULL}, +	{  80, "cp850", "cp850_bin", 1, 1, 0, NULL, NULL}, +	{  81, "cp852", "cp852_bin", 1, 1, 0, NULL, NULL}, +	{  82, "swe7", "swe7_bin", 1, 1, 0, NULL, NULL}, +	{  93, "geostd8", "geostd8_bin", 1, 1, 0, NULL, NULL}, +	{  83, "utf8", "utf8_bin", 1, 2, 0, mysqlnd_mbcharlen_utf8,  check_mb_utf8_valid}, +	{  84, "big5", "big5_bin", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5}, +	{  85, "euckr", "euckr_bin", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr}, +	{  86, "gb2312", "gb2312_bin", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, +	{  87, "gbk", "gbk_bin", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk}, +	{  88, "sjis", "sjis_bin", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis}, +	{  89, "tis620", "tis620_bin", 1, 1, 0, NULL, NULL}, +	{  90, "ucs2", "ucs2_bin", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{  91, "ujis", "ujis_bin", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis}, +	{  94, "latin1", "latin1_spanish_ci", 1, 1, 0, NULL, NULL}, +	{  96, "cp932", "cp932_bin", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932}, +	{  99, "cp1250", "cp1250_polish_ci", 1, 1, 0, NULL, NULL}, +	{  98, "eucjpms", "eucjpms_bin", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms}, +	{ 128, "ucs2", "ucs2_unicode_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 129, "ucs2", "ucs2_icelandic_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 130, "ucs2", "ucs2_latvian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 131, "ucs2", "ucs2_romanian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 132, "ucs2", "ucs2_slovenian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 133, "ucs2", "ucs2_polish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 134, "ucs2", "ucs2_estonian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 135, "ucs2", "ucs2_spanish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 136, "ucs2", "ucs2_swedish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 137, "ucs2", "ucs2_turkish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 138, "ucs2", "ucs2_czech_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 139, "ucs2", "ucs2_danish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 140, "ucs2", "ucs2_lithunian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 141, "ucs2", "ucs2_slovak_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 142, "ucs2", "ucs2_spanish2_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 143, "ucs2", "ucs2_roman_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 144, "ucs2", "ucs2_persian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 145, "ucs2", "ucs2_esperanto_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 146, "ucs2", "ucs2_hungarian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, +	{ 192, "utf8", "utf8_general_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8,  check_mb_utf8_valid}, +	{ 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, +	{ 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, +	{ 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, +	{ 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, +	{   0, NULL, NULL, 0, 0, 0, NULL, NULL} +}; +/* }}} */ + + +/* {{{ mysqlnd_find_charset_nr */ +PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint charsetnr) +{ +	const MYSQLND_CHARSET * c = mysqlnd_charsets; + +	do { +		if (c->nr == charsetnr) { +			return c; +		} +		++c; +	} while (c[0].nr != 0); +	return NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_find_charset_name */ +PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const name) +{ +	const MYSQLND_CHARSET *c = mysqlnd_charsets; + +	do { +		if (!strcasecmp(c->name, name)) { +			return c; +		} +		++c; +	} while (c[0].nr != 0); +	return NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_cset_escape_quotes */ +PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr, +										const char *escapestr, int escapestr_len TSRMLS_DC) +{ +	const char 	*newstr_s = newstr; +	const char 	*newstr_e = newstr + 2 * escapestr_len; +	const char 	*end = escapestr + escapestr_len; +	zend_bool	escape_overflow = FALSE; + +	DBG_ENTER("mysqlnd_cset_escape_quotes"); + +	for (;escapestr < end; escapestr++) { +		uint len = 0; +		/* check unicode characters */ +		 +		if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) { + +			/* check possible overflow */ +			if ((newstr + len) > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			/* copy mb char without escaping it */ +			while (len--) { +				*newstr++ = *escapestr++; +			} +			escapestr--; +			continue; +		} +		if (*escapestr == '\'') { +			if (newstr + 2 > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			*newstr++ = '\''; +			*newstr++ = '\''; +		} else { +			if (newstr + 1 > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			*newstr++ = *escapestr; +		} +	} +	*newstr = '\0'; + +	if (escape_overflow) { +		DBG_RETURN((ulong)~0); +	} +	DBG_RETURN((ulong)(newstr - newstr_s)); +} +/* }}} */ + + +/* {{{ mysqlnd_cset_escape_slashes */ +PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, +										 const char *escapestr, int escapestr_len TSRMLS_DC) +{ +	const char 	*newstr_s = newstr; +	const char 	*newstr_e = newstr + 2 * escapestr_len; +	const char 	*end = escapestr + escapestr_len; +	zend_bool	escape_overflow = FALSE; + +	DBG_ENTER("mysqlnd_cset_escape_slashes"); +	 +	for (;escapestr < end; escapestr++) { +		char esc = '\0'; +		uint len = 0; + +		/* check unicode characters */ +		if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) { +			/* check possible overflow */ +			if ((newstr + len) > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			/* copy mb char without escaping it */ +			while (len--) { +				*newstr++ = *escapestr++; +			} +			escapestr--; +			continue; +		} +		if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1) { +			esc = *escapestr; +		} else { +			switch (*escapestr) { +				case 0: +					esc = '0'; +					break; +				case '\n': +					esc = 'n'; +					break; +				case '\r': +					esc = 'r'; +					break; +				case '\\': +				case '\'': +				case '"': +					esc = *escapestr; +					break; +				case '\032': +					esc = 'Z'; +					break; +			} +		} +		if (esc) { +			if (newstr + 2 > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			/* copy escaped character */ +			*newstr++ = '\\'; +			*newstr++ = esc; +		} else { +			if (newstr + 1 > newstr_e) { +				escape_overflow = TRUE; +				break; +			} +			/* copy non escaped character */ +			*newstr++ = *escapestr; +		} +	} +	*newstr = '\0'; + +	if (escape_overflow) { +		DBG_RETURN((ulong)~0); +	} +	DBG_RETURN((ulong)(newstr - newstr_s)); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_charset.h b/ext/mysqlnd/mysqlnd_charset.h new file mode 100644 index 0000000000..d324d1ff0b --- /dev/null +++ b/ext/mysqlnd/mysqlnd_charset.h @@ -0,0 +1,35 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 1997-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char *newstr, +										const char *escapestr, int escapestr_len TSRMLS_DC); + +PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, +										 const char *escapestr, int escapestr_len TSRMLS_DC); + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c new file mode 100644 index 0000000000..3dc3f592a6 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_debug.c @@ -0,0 +1,1345 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_palloc.h" +#include "mysqlnd_statistics.h" +#include "zend_builtin_functions.h" + + +static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace"; + +#ifdef ZTS  +#define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C +#else +#define MYSQLND_ZTS(self) +#endif + +#define MYSQLND_DEBUG_DUMP_TIME				1 +#define MYSQLND_DEBUG_DUMP_TRACE			2 +#define MYSQLND_DEBUG_DUMP_PID				4 +#define MYSQLND_DEBUG_DUMP_LINE				8 +#define MYSQLND_DEBUG_DUMP_FILE				16 +#define MYSQLND_DEBUG_DUMP_LEVEL			32 +#define MYSQLND_DEBUG_APPEND				64 +#define MYSQLND_DEBUG_FLUSH					128 +#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS	256 + +static char * mysqlnd_emalloc_name	= "_mysqlnd_emalloc"; +static char * mysqlnd_pemalloc_name	= "_mysqlnd_pemalloc"; +static char * mysqlnd_ecalloc_name	= "_mysqlnd_ecalloc"; +static char * mysqlnd_pecalloc_name	= "_mysqlnd_pecalloc"; +static char * mysqlnd_erealloc_name	= "_mysqlnd_erealloc"; +static char * mysqlnd_perealloc_name= "_mysqlnd_perealloc"; +static char * mysqlnd_efree_name	= "_mysqlnd_efree"; +static char * mysqlnd_pefree_name	= "_mysqlnd_pefree"; +static char * mysqlnd_malloc_name	= "_mysqlnd_malloc"; +static char * mysqlnd_calloc_name	= "_mysqlnd_calloc"; +static char * mysqlnd_realloc_name	= "_mysqlnd_realloc"; +static char * mysqlnd_free_name		= "_mysqlnd_free"; + +/* {{{ mysqlnd_debug::open */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen) +{ +	MYSQLND_ZTS(self); + +	if (!self->file_name) { +		return FAIL; +	} + +	self->stream = php_stream_open_wrapper(self->file_name, +										   reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb", +										   REPORT_ERRORS, NULL); +	return self->stream? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self, +								   unsigned int line, const char * const file, +								   unsigned int level, const char * type, const char * message) +{ +	char pipe_buffer[512]; +	enum_func_status ret; +	int i; +	char * message_line; +	uint message_line_len; +	unsigned int flags = self->flags; +	char pid_buffer[10], time_buffer[30], file_buffer[200], +		 line_buffer[6], level_buffer[7]; +	MYSQLND_ZTS(self); + +	if (!self->stream) { +		if (FAIL == self->m->open(self, FALSE)) { +			return FAIL; +		} +	} + +	if (level == -1) { +		level = zend_stack_count(&self->call_stack); +	} +	i = MIN(level, sizeof(pipe_buffer) / 2  - 1); +	pipe_buffer[i*2] = '\0'; +	for (;i > 0;i--) { +		pipe_buffer[i*2 - 1] = ' '; +		pipe_buffer[i*2 - 2] = '|'; +	} + + +	if (flags & MYSQLND_DEBUG_DUMP_PID) { +		snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); +		pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_TIME) { +		/* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) +		/* FIXME This doesn't give microseconds as in Unix case, and the resolution is +		in system ticks, 10 ms intervals. See my_getsystime.c for high res */ +		SYSTEMTIME loc_t; +		GetLocalTime(&loc_t); +		snprintf(time_buffer, sizeof(time_buffer) - 1, +				 /* "%04d-%02d-%02d " */ +				 "%02d:%02d:%02d.%06d ", +				 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +				 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +		time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else +		struct timeval tv; +		struct tm *tm_p; +		if (gettimeofday(&tv, NULL) != -1) { +			if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { +				snprintf(time_buffer, sizeof(time_buffer) - 1, +						 /* "%04d-%02d-%02d " */ +						 "%02d:%02d:%02d.%06d ", +						 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +						 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, +						 (int) (tv.tv_usec)); +				time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +			} +		} +#endif +	} +	if (flags & MYSQLND_DEBUG_DUMP_FILE) { +		snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); +		file_buffer[sizeof(file_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LINE) { +		snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); +		line_buffer[sizeof(line_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { +		snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); +		level_buffer[sizeof(level_buffer) - 1 ] = '\0'; +	} + +	message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", +								flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", +								pipe_buffer, type? type:"", message); + +	ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; +	efree(message_line); +	if (flags & MYSQLND_DEBUG_FLUSH) { +		self->m->close(self); +		self->m->open(self, TRUE);	 +	} +	return ret; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log_va */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self, +									  unsigned int line, const char * const file, +									  unsigned int level, const char * type, +									  const char *format, ...) +{ +	char pipe_buffer[512]; +	int i; +	enum_func_status ret; +	char * message_line, *buffer; +	uint message_line_len; +	va_list args; +	unsigned int flags = self->flags; +	char pid_buffer[10], time_buffer[30], file_buffer[200], +		 line_buffer[6], level_buffer[7]; +	MYSQLND_ZTS(self); + +	if (!self->stream) { +		if (FAIL == self->m->open(self, FALSE)) { +			return FAIL; +		} +	} + +	if (level == -1) { +		level = zend_stack_count(&self->call_stack); +	} +	i = MIN(level, sizeof(pipe_buffer) / 2  - 1); +	pipe_buffer[i*2] = '\0'; +	for (;i > 0;i--) { +		pipe_buffer[i*2 - 1] = ' '; +		pipe_buffer[i*2 - 2] = '|'; +	} + + +	if (flags & MYSQLND_DEBUG_DUMP_PID) { +		snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); +		pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_TIME) { +		/* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) +		/* FIXME This doesn't give microseconds as in Unix case, and the resolution is +		in system ticks, 10 ms intervals. See my_getsystime.c for high res */ +		SYSTEMTIME loc_t; +		GetLocalTime(&loc_t); +		snprintf(time_buffer, sizeof(time_buffer) - 1, +				 /* "%04d-%02d-%02d " */ +				 "%02d:%02d:%02d.%06d ", +				 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +				 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +		time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else +		struct timeval tv; +		struct tm *tm_p; +		if (gettimeofday(&tv, NULL) != -1) { +			if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { +				snprintf(time_buffer, sizeof(time_buffer) - 1, +						 /* "%04d-%02d-%02d " */ +						 "%02d:%02d:%02d.%06d ", +						 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +						 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, +						 (int) (tv.tv_usec)); +				time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +			} +		} +#endif +	} +	if (flags & MYSQLND_DEBUG_DUMP_FILE) { +		snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); +		file_buffer[sizeof(file_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LINE) { +		snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); +		line_buffer[sizeof(line_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { +		snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); +		level_buffer[sizeof(level_buffer) - 1 ] = '\0'; +	} + + +	va_start(args, format); +	vspprintf(&buffer, 0, format, args); +	va_end(args); + +	message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", +								flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", +								pipe_buffer, type? type:"", buffer); +	efree(buffer); + +	ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; +	efree(message_line); + +	if (flags & MYSQLND_DEBUG_FLUSH) { +		self->m->close(self); +		self->m->open(self, TRUE);	 +	} +	return ret; +} +/* }}} */ + + +/* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */ +/* {{{ mysqlnd_res_meta::func_enter */ +static zend_bool +MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, +										  unsigned int line, const char * const file, +										  char * func_name, uint func_name_len) +{ +	if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { +		return FALSE; +	} +	if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { +		return FALSE; +	} + +	if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 &&  +		(func_name == mysqlnd_emalloc_name	|| func_name == mysqlnd_pemalloc_name	|| +		 func_name == mysqlnd_ecalloc_name	|| func_name == mysqlnd_pecalloc_name	|| +		 func_name == mysqlnd_erealloc_name || func_name == mysqlnd_perealloc_name	|| +		 func_name == mysqlnd_efree_name	|| func_name == mysqlnd_pefree_name		||  +		 func_name == mysqlnd_efree_name	|| func_name == mysqlnd_pefree_name		||  +		 func_name == mysqlnd_malloc_name	|| func_name == mysqlnd_calloc_name		||  +		 func_name == mysqlnd_realloc_name	|| func_name == mysqlnd_free_name		|| +		 func_name == mysqlnd_palloc_zval_ptr_dtor_name	|| func_name == mysqlnd_palloc_get_zval_name || +		 func_name == mysqlnd_read_header_name || func_name == mysqlnd_read_body_name)) { +		zend_stack_push(&self->call_stack, "", sizeof("")); +	   	return FALSE; +	} + +	zend_stack_push(&self->call_stack, func_name, func_name_len + 1); + +	if (zend_hash_num_elements(&self->not_filtered_functions) && +		0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) +	{ +		return FALSE; +	} + +	self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name); +	return TRUE; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::func_leave */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, +										  const char * const file) +{ +	char *func_name; + +	if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { +		return PASS; +	} +	if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { +		return PASS; +	} + +	zend_stack_top(&self->call_stack, (void **)&func_name); + +	if (func_name[0] == '\0') { +		; /* don't log that function */ +	} else if (!zend_hash_num_elements(&self->not_filtered_functions) || +		1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) +	{ +		self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name); +	} + +	return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::close */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self) +{ +	MYSQLND_ZTS(self); +	if (self->stream) { +		php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE); +		self->stream = NULL; +	} +	return PASS; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::free */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self) +{ +	if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { +		efree(self->file_name); +		self->file_name = NULL; +	} +	zend_stack_destroy(&self->call_stack); +	zend_hash_destroy(&self->not_filtered_functions); +	efree(self); +	return PASS; +} +/* }}} */ + +enum mysqlnd_debug_parser_state +{ +	PARSER_WAIT_MODIFIER, +	PARSER_WAIT_COLON, +	PARSER_WAIT_VALUE +}; + + +/* {{{ mysqlnd_res_meta::set_mode */ +static void +MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode) +{ +	uint mode_len = strlen(mode), i; +	enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER; + +	self->flags = 0; +	self->nest_level_limit = 0; +	if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { +		efree(self->file_name); +		self->file_name = NULL; +	} +	if (zend_hash_num_elements(&self->not_filtered_functions)) { +		zend_hash_destroy(&self->not_filtered_functions); +		zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0); +	} + +	for (i = 0; i < mode_len; i++) { +		switch (mode[i]) { +			case 'O': +			case 'A': +				self->flags |= MYSQLND_DEBUG_FLUSH; +			case 'a': +			case 'o': +				if (mode[i] == 'a' || mode[i] == 'A') { +					self->flags |= MYSQLND_DEBUG_APPEND; +				} +				if (i + 1 < mode_len && mode[i+1] == ',') { +					unsigned int j = i + 2; +					while (j < mode_len) { +						if (mode[j] == ':') { +							break; +						} +						j++; +					} +					if (j > i + 2) { +						self->file_name = estrndup(mode + i + 2, j - i - 2); +					} +					i = j; +				} else { +					self->file_name = (char *) mysqlnd_debug_default_trace_file; +				} +				state = PARSER_WAIT_COLON; +				break; +			case ':': +#if 0 +				if (state != PARSER_WAIT_COLON) { +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i); +				} +#endif +				state = PARSER_WAIT_MODIFIER; +				break; +			case 'f': /* limit output to these functions */ +				if (i + 1 < mode_len && mode[i+1] == ',') { +					unsigned int j = i + 2; +					i++; +					while (j < mode_len) { +						if (mode[j] == ':') { +							/* function names with :: */ +							if ((j + 1 < mode_len) && mode[j+1] == ':') { +								j += 2; +								continue; +							} +						} +						if (mode[j] == ',' || mode[j] == ':') { +							if (j > i + 2) { +								char func_name[1024]; +								uint func_name_len = MIN(sizeof(func_name) - 1, j - i - 1); +								memcpy(func_name, mode + i + 1, func_name_len); +								func_name[func_name_len] = '\0';  + +								zend_hash_add_empty_element(&self->not_filtered_functions, +															func_name, func_name_len + 1); +								i = j; +							} +							if (mode[j] == ':') { +								break; +							} +						} +						j++; +					} +					i = j; +				} else { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, +									 "Expected list of functions for '%c' found none", mode[i]);				 +#endif +				} +				state = PARSER_WAIT_COLON; +				break; +			case 'D': +			case 'd': +			case 'g': +			case 'p': +				/* unsupported */ +				if ((i + 1) < mode_len && mode[i+1] == ',') { +					i+= 2; +					while (i < mode_len) { +						if (mode[i++] == ':') { +							break; +						} +					} +				} +				state = PARSER_WAIT_COLON; +				break; +			case 'F': +				self->flags |= MYSQLND_DEBUG_DUMP_FILE; +				state = PARSER_WAIT_COLON; +				break; +			case 'i': +				self->flags |= MYSQLND_DEBUG_DUMP_PID; +				state = PARSER_WAIT_COLON; +				break; +			case 'L': +				self->flags |= MYSQLND_DEBUG_DUMP_LINE; +				state = PARSER_WAIT_COLON; +				break; +			case 'n': +				self->flags |= MYSQLND_DEBUG_DUMP_LEVEL; +				state = PARSER_WAIT_COLON; +				break; +			case 't': +				if (mode[i+1] == ',') { +					unsigned int j = i + 2; +					while (j < mode_len) { +						if (mode[j] == ':') { +							break; +						} +						j++; +					} +					if (j > i + 2) { +						char *value_str = estrndup(mode + i + 2, j - i - 2); +						self->nest_level_limit = atoi(value_str); +						efree(value_str); +					} +					i = j; +				} else { +					self->nest_level_limit = 200; /* default value for FF DBUG */ +				} +				self->flags |= MYSQLND_DEBUG_DUMP_TRACE; +				state = PARSER_WAIT_COLON; +				break; +			case 'T': +				self->flags |= MYSQLND_DEBUG_DUMP_TIME; +				state = PARSER_WAIT_COLON; +				break; +			case 'N': +			case 'P': +			case 'r': +			case 'S': +				state = PARSER_WAIT_COLON; +				break; +			case 'm': /* mysqlnd extension - trace memory functions */ +				self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS; +				state = PARSER_WAIT_COLON; +				break; +			default: +				if (state == PARSER_WAIT_MODIFIER) { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]); +#endif +					if (i+1 < mode_len && mode[i+1] == ',') { +						i+= 2; +						while (i < mode_len) { +							if (mode[i] == ':') { +								break; +							} +							i++; +						} +					} +					state = PARSER_WAIT_COLON; +				} else if (state == PARSER_WAIT_COLON) { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);				 +#endif +				} +				break; +		} +	} +} +/* }}} */ + + +MYSQLND_CLASS_METHODS_START(mysqlnd_debug) +	MYSQLND_METHOD(mysqlnd_debug, open), +	MYSQLND_METHOD(mysqlnd_debug, set_mode), +	MYSQLND_METHOD(mysqlnd_debug, log), +	MYSQLND_METHOD(mysqlnd_debug, log_va), +	MYSQLND_METHOD(mysqlnd_debug, func_enter), +	MYSQLND_METHOD(mysqlnd_debug, func_leave), +	MYSQLND_METHOD(mysqlnd_debug, close), +	MYSQLND_METHOD(mysqlnd_debug, free), +MYSQLND_CLASS_METHODS_END; + + + +/* {{{ mysqlnd_debug_init */ +MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D) +{ +	MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG)); +#ifdef ZTS +	ret->TSRMLS_C = TSRMLS_C; +#endif +	ret->nest_level_limit = 0; +	ret->pid = getpid(); +	zend_stack_init(&ret->call_stack); +	zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0); + +	ret->m = & mysqlnd_mysqlnd_debug_methods; +	 +	return ret; +} +/* }}} */ + + +/* {{{ _mysqlnd_debug */ +void _mysqlnd_debug(const char *mode TSRMLS_DC) +{ +#ifdef PHP_DEBUG +	MYSQLND_DEBUG *dbg = MYSQLND_G(dbg); +	if (!dbg) { +		MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(TSRMLS_C); +		if (!dbg) { +			return; +		} +	} +	 +	dbg->m->close(dbg); +	dbg->m->set_mode(dbg, mode); +	while (zend_stack_count(&dbg->call_stack)) { +		zend_stack_del_top(&dbg->call_stack); +	} +#endif +} +/* }}} */ + + +#if ZEND_DEBUG +#else +#define __zend_filename "/unknown/unknown" +#define __zend_lineno   0 +#endif + + +/* {{{ _mysqlnd_emalloc */ +void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_emalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	ret = emalloc(size); +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pemalloc */ +void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_pemalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = pemalloc(size, persistent); +	DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_ecalloc */ +void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_ecalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + +	ret = ecalloc(nmemb, size); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pecalloc */ +void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_pecalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = pecalloc(nmemb, size, persistent); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_erealloc */ +void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_erealloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + +	ret = erealloc(ptr, new_size); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("new_ptr=%p", ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_perealloc */ +void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_perealloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent);  +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = perealloc(ptr, new_size, persistent); + +	DBG_INF_FMT("new_ptr=%p", ret);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} +	MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_EREALLOC_COUNT:STAT_MEM_REALLOC_COUNT); +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_efree */ +void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_efree_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p", ptr);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT); + +	efree(ptr); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_pefree */ +void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_pefree_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent);  +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	pefree(ptr, persistent); + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT); +	} +	DBG_VOID_RETURN; +} + + +/* {{{ _mysqlnd_malloc */ +void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_malloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + +	ret = malloc(size); + +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_calloc */ +void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_calloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + +	ret = calloc(nmemb, size); + +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_realloc */ +void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_realloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC)); + +	ret = realloc(ptr, new_size); + +	DBG_INF_FMT("new_ptr=%p", ret); + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_free */ +void _mysqlnd_free(void *ptr MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_free_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p", ptr);  + +	free(ptr); + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT); +	} +	DBG_VOID_RETURN; +} +/* }}} */ + + + + +/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */ + +#if PHP_MAJOR_VERSION >= 6 +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr)                                            \ +	*str = (char*)erealloc(*str, *len + 1 + 1);                          \ +	(*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen)                                   \ +	{                                                                    \ +		int l = vallen;                                                  \ +		*str = (char*)erealloc(*str, *len + l + 1);                      \ +		memcpy((*str) + *len, val, l);                                   \ +		*len += l;                                                       \ +	} + +#define TRACE_APPEND_USTRL(val, vallen) \ +	{ \ +		zval tmp, copy; \ +		int use_copy; \ +		ZVAL_UNICODEL(&tmp, val, vallen, 1); \ +		zend_make_printable_zval(&tmp, ©, &use_copy); \ +		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +		zval_dtor(©); \ +		zval_dtor(&tmp); \ +	} + +#define TRACE_APPEND_ZVAL(zv) \ +	if (Z_TYPE_P((zv)) == IS_UNICODE) { \ +		zval copy; \ +		int use_copy; \ +		zend_make_printable_zval((zv), ©, &use_copy); \ +		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +		zval_dtor(©); \ +	} else { \ +		TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \ +	} + +#define TRACE_APPEND_STR(val)                                            \ +	TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key)                                            \ +	if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ +		if (Z_TYPE_PP(tmp) == IS_UNICODE) { \ +			zval copy; \ +			int use_copy; \ +			zend_make_printable_zval(*tmp, ©, &use_copy); \ +	    TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +	    zval_dtor(©); \ +		} else { \ +	    TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \ +	  } \ +	} +/* }}} */ + +/* {{{ mysqlnd_build_trace_args */ +static int mysqlnd_build_trace_args(zval **arg, int num_args, va_list args, zend_hash_key *hash_key) +{ +	char **str; +	int *len; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); + +	/* the trivial way would be to do: +	 * conver_to_string_ex(arg); +	 * append it and kill the now tmp arg. +	 * but that could cause some E_NOTICE and also damn long lines. +	 */ + +	switch (Z_TYPE_PP(arg)) { +		case IS_NULL: +			TRACE_APPEND_STR("NULL, "); +			break; +		case IS_STRING: { +			int l_added; +			TRACE_APPEND_CHR('\''); +			if (Z_STRLEN_PP(arg) > 15) { +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_STRLEN_PP(arg); +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			while (--l_added) { +				if ((unsigned char)(*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_UNICODE: { +			int l_added; +			TSRMLS_FETCH(); + +			/* +			 * We do not want to apply current error mode here, since +			 * zend_make_printable_zval() uses output encoding converter. +			 * Temporarily set output encoding converter to escape offending +			 * chars with \uXXXX notation. +			 */ +			zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA); +			TRACE_APPEND_CHR('\''); +			if (Z_USTRLEN_PP(arg) > 15) { +				TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_USTRLEN_PP(arg); +				TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			/* +			 * Reset output encoding converter error mode. +			 */ +			zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode)); +			while (--l_added) { +				if ((unsigned char)(*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_BOOL: +			if (Z_LVAL_PP(arg)) { +				TRACE_APPEND_STR("true, "); +			} else { +				TRACE_APPEND_STR("false, "); +			} +			break; +		case IS_RESOURCE: +			TRACE_APPEND_STR("Resource id #"); +			/* break; */ +		case IS_LONG: { +			long lval = Z_LVAL_PP(arg); +			char s_tmp[MAX_LENGTH_OF_LONG + 1]; +			int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_DOUBLE: { +			double dval = Z_DVAL_PP(arg); +			char *s_tmp; +			int l_tmp; +			TSRMLS_FETCH(); + +			s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); +			l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			/* %G already handles removing trailing zeros from the fractional part, yay */ +			efree(s_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_ARRAY: +			TRACE_APPEND_STR("Array, "); +			break; +		case IS_OBJECT: { +			zstr class_name; +			zend_uint class_name_len; +			int dup; +			TSRMLS_FETCH(); + +			TRACE_APPEND_STR("Object("); + +			dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			if (UG(unicode)) { +				zval tmp; + +				ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1); +				convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv))); +				TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp)); +				zval_dtor(&tmp); +			} else { +				TRACE_APPEND_STRL(class_name.s, class_name_len); +			} +			if(!dup) { +				efree(class_name.v); +			} + +			TRACE_APPEND_STR("), "); +			break; +		} +		default: +			break; +	} +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +static int mysqlnd_build_trace_string(zval **frame, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char *s_tmp, **str; +	int *len, *num; +	long line; +	HashTable *ht = Z_ARRVAL_PP(frame); +	zval **file, **tmp; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); +	sprintf(s_tmp, "#%d ", (*num)++); +	TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +	efree(s_tmp); +	if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { +		if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { +			line = Z_LVAL_PP(tmp); +		} else { +			line = 0; +		} +		TRACE_APPEND_ZVAL(*file); +		s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1); +		sprintf(s_tmp, "(%ld): ", line); +		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +		efree(s_tmp); +	} else { +		TRACE_APPEND_STR("[internal function]: "); +	} +	TRACE_APPEND_KEY("class"); +	TRACE_APPEND_KEY("type"); +	TRACE_APPEND_KEY("function"); +	TRACE_APPEND_CHR('('); +	if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { +		int last_len = *len; +		zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp), (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); +		if (last_len != *len) { +			*len -= 2; /* remove last ', ' */ +		} +	} +	TRACE_APPEND_STR(")\n"); +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +#else /* PHP 5*/ + + +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr)                                            \ +	*str = (char*)erealloc(*str, *len + 1 + 1);                          \ +	(*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen)                                   \ +	{                                                                    \ +		int l = vallen;                                                  \ +		*str = (char*)erealloc(*str, *len + l + 1);                      \ +		memcpy((*str) + *len, val, l);                                   \ +		*len += l;                                                       \ +	} + +#define TRACE_APPEND_STR(val)                                            \ +	TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key)                                            \ +	if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ +	    TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \ +	} + +/* }}} */ + + +static int mysqlnd_build_trace_args(zval **arg, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char **str; +	int *len; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); + +	/* the trivial way would be to do: +	 * conver_to_string_ex(arg); +	 * append it and kill the now tmp arg. +	 * but that could cause some E_NOTICE and also damn long lines. +	 */ + +	switch (Z_TYPE_PP(arg)) { +		case IS_NULL: +			TRACE_APPEND_STR("NULL, "); +			break; +		case IS_STRING: { +			int l_added; +			TRACE_APPEND_CHR('\''); +			if (Z_STRLEN_PP(arg) > 15) { +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_STRLEN_PP(arg); +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			while (--l_added) { +				if ((*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_BOOL: +			if (Z_LVAL_PP(arg)) { +				TRACE_APPEND_STR("true, "); +			} else { +				TRACE_APPEND_STR("false, "); +			} +			break; +		case IS_RESOURCE: +			TRACE_APPEND_STR("Resource id #"); +			/* break; */ +		case IS_LONG: { +			long lval = Z_LVAL_PP(arg); +			char s_tmp[MAX_LENGTH_OF_LONG + 1]; +			int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_DOUBLE: { +			double dval = Z_DVAL_PP(arg); +			char *s_tmp; +			int l_tmp; +			TSRMLS_FETCH(); + +			s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); +			l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			/* %G already handles removing trailing zeros from the fractional part, yay */ +			efree(s_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_ARRAY: +			TRACE_APPEND_STR("Array, "); +			break; +		case IS_OBJECT: { +			char *class_name; +			zend_uint class_name_len; +			int dup; +			TSRMLS_FETCH(); + +			TRACE_APPEND_STR("Object("); + +			dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			TRACE_APPEND_STRL(class_name, class_name_len); +			if(!dup) { +				efree(class_name); +			} + +			TRACE_APPEND_STR("), "); +			break; +		} +		default: +			break; +	} +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int mysqlnd_build_trace_string(zval **frame, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char *s_tmp, **str; +	int *len, *num; +	long line; +	HashTable *ht = Z_ARRVAL_PP(frame); +	zval **file, **tmp; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); +	sprintf(s_tmp, "#%d ", (*num)++); +	TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +	efree(s_tmp); +	if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { +		if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { +			line = Z_LVAL_PP(tmp); +		} else { +			line = 0; +		} +		s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1); +		sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line); +		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +		efree(s_tmp); +	} else { +		TRACE_APPEND_STR("[internal function]: "); +	} +	TRACE_APPEND_KEY("class"); +	TRACE_APPEND_KEY("type"); +	TRACE_APPEND_KEY("function"); +	TRACE_APPEND_CHR('('); +	if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { +		int last_len = *len; +		zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp), (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); +		if (last_len != *len) { +			*len -= 2; /* remove last ', ' */ +		} +	} +	TRACE_APPEND_STR(")\n"); +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ +#endif + + +char * mysqlnd_get_backtrace(TSRMLS_D) +{ +	zval *trace; +	char *res = estrdup(""), **str = &res, *s_tmp; +	int res_len = 0, *len = &res_len, num = 0; + +	MAKE_STD_ZVAL(trace); +	zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC); + +	zend_hash_apply_with_arguments(Z_ARRVAL_P(trace), (apply_func_args_t)mysqlnd_build_trace_string, 3, str, len, &num); +	zval_ptr_dtor(&trace); + +	s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1); +	sprintf(s_tmp, "#%d {main}", num); +	TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +	efree(s_tmp); + +	res[res_len] = '\0'; + +	return res; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h new file mode 100644 index 0000000000..120d74aaa5 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_debug.h @@ -0,0 +1,145 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_DEBUG_H +#define MYSQLND_DEBUG_H + +#include "zend_stack.h" + +#define MYSQLND_DEBUG_MEMORY 1 + +struct st_mysqlnd_debug_methods +{ +	enum_func_status (*open)(MYSQLND_DEBUG *self, zend_bool reopen); +	void			 (*set_mode)(MYSQLND_DEBUG *self, const char * const mode); +	enum_func_status (*log)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, +							unsigned int level, const char * type, const char *message); +	enum_func_status (*log_va)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, +							   unsigned int level, const char * type, const char *format, ...); +	zend_bool (*func_enter)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, +							char * func_name, uint func_name_len); +	enum_func_status (*func_leave)(MYSQLND_DEBUG *self, unsigned int line, const char * const file); +	enum_func_status (*close)(MYSQLND_DEBUG *self); +	enum_func_status (*free)(MYSQLND_DEBUG *self); +}; + +struct st_mysqlnd_debug +{ +	php_stream	*stream; +#ifdef ZTS +	TSRMLS_D; +#endif +	unsigned int flags; +	unsigned int nest_level_limit; +	int pid; +	char * file_name; +	zend_stack call_stack; +	HashTable not_filtered_functions; +	struct st_mysqlnd_debug_methods *m; +}; + + +MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D); + +#define MYSQLND_MEM_D 	TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC + + +void *	_mysqlnd_emalloc(size_t size MYSQLND_MEM_D); +void *	_mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D); +void *	_mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D); +void *	_mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D); +void *	_mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D); +void *	_mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D); +void 	_mysqlnd_efree(void *ptr MYSQLND_MEM_D); +void 	_mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D); +void *	_mysqlnd_malloc(size_t size MYSQLND_MEM_D); +void *	_mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D); +void *	_mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D); +void	_mysqlnd_free(void *ptr MYSQLND_MEM_D); + +char *	mysqlnd_get_backtrace(TSRMLS_D); + +#if PHP_DEBUG && !defined(PHP_WIN32) +#define DBG_INF(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", (msg)); } while (0) +#define DBG_ERR(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", (msg)); } while (0) +#define DBG_INF_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0) +#define DBG_ERR_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0) + +#define DBG_ENTER(func_name) zend_bool dbg_skip_trace = TRUE; if (MYSQLND_G(dbg)) dbg_skip_trace = !MYSQLND_G(dbg)->m->func_enter(MYSQLND_G(dbg), __LINE__, __FILE__, func_name, strlen(func_name)); +#define DBG_RETURN(value)	do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return (value); } while (0) +#define DBG_VOID_RETURN		do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return; } while (0) + + + +#else +static inline void DBG_INF(char *msg) {} +static inline void DBG_ERR(char *msg) {} +static inline void DBG_INF_FMT(char *format, ...) {} +static inline void DBG_ERR_FMT(char *format, ...) {} +static inline void DBG_ENTER(char *func_name) {} +#define DBG_RETURN(value)	return (value) +#define DBG_VOID_RETURN		return; +#endif + + +#if MYSQLND_DEBUG_MEMORY + +#define mnd_emalloc(size)				_mysqlnd_emalloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pemalloc(size, pers)		_mysqlnd_pemalloc((size), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_ecalloc(nmemb, size)		_mysqlnd_ecalloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pecalloc(nmemb, size, p)	_mysqlnd_pecalloc((nmemb), (size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_erealloc(ptr, new_size)		_mysqlnd_erealloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_perealloc(ptr, new_size, p)	_mysqlnd_perealloc((ptr), (new_size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_efree(ptr)					_mysqlnd_efree((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pefree(ptr, pers)			_mysqlnd_pefree((ptr), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_malloc(size)				_mysqlnd_malloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_calloc(nmemb, size)			_mysqlnd_calloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_realloc(ptr, new_size)		_mysqlnd_realloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_free(ptr)					_mysqlnd_free((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) + +#else + +#define mnd_emalloc(size)				emalloc((size)) +#define mnd_pemalloc(size, pers)		pemalloc((size), (pers)) +#define mnd_ecalloc(nmemb, size)		ecalloc((nmemb), (size)) +#define mnd_pecalloc(nmemb, size, p)	pecalloc((nmemb), (size), (p)) +#define mnd_erealloc(ptr, new_size)		erealloc((ptr), (new_size)) +#define mnd_perealloc(ptr, new_size, p)	perealloc((ptr), (new_size), (p)) +#define mnd_efree(ptr)					efree((ptr)) +#define mnd_pefree(ptr, pers)			pefree((ptr), (pers)) +#define mnd_malloc(size)				malloc((size)) +#define mnd_calloc(nmemb, size)			calloc((nmemb), (size)) +#define mnd_realloc(ptr, new_size)		realloc((ptr), (new_size)) +#define mnd_free(ptr)					free((ptr)) + +#endif + +#endif /* MYSQLND_DEBUG_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h new file mode 100644 index 0000000000..f8a3a8b57d --- /dev/null +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -0,0 +1,367 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#ifndef MYSQLND_ENUM_N_DEF_H +#define MYSQLND_ENUM_N_DEF_H + + +#define MYSQLND_ERRMSG_SIZE			512 +#define MYSQLND_SQLSTATE_LENGTH		5 +#define MYSQLND_SQLSTATE_NULL		"00000" + +#define MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED	16 +#define MYSQLND_SERVER_QUERY_NO_INDEX_USED		32 + +#define MYSQLND_NO_DATA			100 +#define MYSQLND_DATA_TRUNCATED	101 + +#define SHA1_MAX_LENGTH 20 +#define SCRAMBLE_LENGTH 20 +#define SCRAMBLE_LENGTH_323 8 + +#define CLIENT_LONG_PASSWORD		1		/* new more secure passwords */ +#define CLIENT_FOUND_ROWS			2		/* Found instead of affected rows */ +#define CLIENT_LONG_FLAG			4		/* Get all column flags */ +#define CLIENT_CONNECT_WITH_DB		8		/* One can specify db on connect */ +#define CLIENT_NO_SCHEMA			16		/* Don't allow database.table.column */ +#define CLIENT_COMPRESS				32		/* Can use compression protocol */ +#define CLIENT_ODBC					64		/* Odbc client */ +#define CLIENT_LOCAL_FILES			128		/* Can use LOAD DATA LOCAL */ +#define CLIENT_IGNORE_SPACE			256		/* Ignore spaces before '(' */ +#define CLIENT_PROTOCOL_41			512		/* New 4.1 protocol */ +#define CLIENT_INTERACTIVE			1024	/* This is an interactive client */ +#define CLIENT_SSL					2048	/* Switch to SSL after handshake */ +#define CLIENT_IGNORE_SIGPIPE		4096	/* IGNORE sigpipes */ +#define CLIENT_TRANSACTIONS			8192	/* Client knows about transactions */ +#define CLIENT_RESERVED				16384	/* Old flag for 4.1 protocol */ +#define CLIENT_SECURE_CONNECTION	32768	/* New 4.1 authentication */ +#define CLIENT_MULTI_STATEMENTS		(1UL << 16) /* Enable/disable multi-stmt support */ +#define CLIENT_MULTI_RESULTS		(1UL << 17) /* Enable/disable multi-results */ + +typedef enum mysqlnd_extension +{ +	MYSQLND_MYSQL = 0, +	MYSQLND_MYSQLI, +} enum_mysqlnd_extension; + +enum +{ +	MYSQLND_FETCH_ASSOC = 1, +	MYSQLND_FETCH_NUM = 2, +	MYSQLND_FETCH_BOTH = 1|2, +}; + +/* Follow libmysql convention */ +typedef enum func_status +{ +	PASS = 0, +	FAIL = 1, +} enum_func_status; + +typedef enum mysqlnd_query_type +{ +	QUERY_UPSERT, +	QUERY_SELECT, +	QUERY_LOAD_LOCAL +} enum_mysqlnd_query_type; + +typedef enum mysqlnd_res_type +{ +	MYSQLND_RES_NORMAL = 1, +	MYSQLND_RES_PS_BUF, +	MYSQLND_RES_PS_UNBUF +} enum_mysqlnd_res_type; + +typedef enum mysqlnd_option +{ +	MYSQL_OPT_CONNECT_TIMEOUT, +	MYSQL_OPT_COMPRESS, +	MYSQL_OPT_NAMED_PIPE, +	MYSQL_INIT_COMMAND, +	MYSQL_READ_DEFAULT_FILE, +	MYSQL_READ_DEFAULT_GROUP, +	MYSQL_SET_CHARSET_DIR, +	MYSQL_SET_CHARSET_NAME, +	MYSQL_OPT_LOCAL_INFILE, +	MYSQL_OPT_PROTOCOL, +	MYSQL_SHARED_MEMORY_BASE_NAME, +	MYSQL_OPT_READ_TIMEOUT, +	MYSQL_OPT_WRITE_TIMEOUT, +	MYSQL_OPT_USE_RESULT, +	MYSQL_OPT_USE_REMOTE_CONNECTION, +	MYSQL_OPT_USE_EMBEDDED_CONNECTION, +	MYSQL_OPT_GUESS_CONNECTION, +	MYSQL_SET_CLIENT_IP, +	MYSQL_SECURE_AUTH, +	MYSQL_REPORT_DATA_TRUNCATION, +	MYSQL_OPT_RECONNECT, +	MYSQL_OPT_SSL_VERIFY_SERVER_CERT, +#if PHP_MAJOR_VERSION >= 6 +	MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200, +#endif +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +	MYSQLND_OPT_INT_AND_YEAR_AS_INT = 201, +#endif +	MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202, +	MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203, +} enum_mysqlnd_option; + + +typedef enum mysqlnd_field_types +{ +	MYSQL_TYPE_DECIMAL, +	MYSQL_TYPE_TINY, +	MYSQL_TYPE_SHORT, +	MYSQL_TYPE_LONG, +	MYSQL_TYPE_FLOAT, +	MYSQL_TYPE_DOUBLE, +	MYSQL_TYPE_NULL, +	MYSQL_TYPE_TIMESTAMP, +	MYSQL_TYPE_LONGLONG, +	MYSQL_TYPE_INT24, +	MYSQL_TYPE_DATE, +	MYSQL_TYPE_TIME, +	MYSQL_TYPE_DATETIME, +	MYSQL_TYPE_YEAR, +	MYSQL_TYPE_NEWDATE, +	MYSQL_TYPE_VARCHAR, +	MYSQL_TYPE_BIT, +	MYSQL_TYPE_NEWDECIMAL=246, +	MYSQL_TYPE_ENUM=247, +	MYSQL_TYPE_SET=248, +	MYSQL_TYPE_TINY_BLOB=249, +	MYSQL_TYPE_MEDIUM_BLOB=250, +	MYSQL_TYPE_LONG_BLOB=251, +	MYSQL_TYPE_BLOB=252, +	MYSQL_TYPE_VAR_STRING=253, +	MYSQL_TYPE_STRING=254, +	MYSQL_TYPE_GEOMETRY=255 +} enum_mysqlnd_field_types; + +/* Please update this if there is a new type after MYSQL_TYPE_GEOMETRY */ +#define MYSQL_TYPE_LAST		MYSQL_TYPE_GEOMETRY + + +typedef enum mysqlnd_server_option +{ +	MYSQL_OPTION_MULTI_STATEMENTS_ON, +	MYSQL_OPTION_MULTI_STATEMENTS_OFF +} enum_mysqlnd_server_option; + + +#define FIELD_TYPE_DECIMAL		MYSQL_TYPE_DECIMAL +#define FIELD_TYPE_NEWDECIMAL	MYSQL_TYPE_NEWDECIMAL +#define FIELD_TYPE_TINY			MYSQL_TYPE_TINY +#define FIELD_TYPE_SHORT		MYSQL_TYPE_SHORT +#define FIELD_TYPE_LONG			MYSQL_TYPE_LONG +#define FIELD_TYPE_FLOAT		MYSQL_TYPE_FLOAT +#define FIELD_TYPE_DOUBLE		MYSQL_TYPE_DOUBLE +#define FIELD_TYPE_NULL			MYSQL_TYPE_NULL +#define FIELD_TYPE_TIMESTAMP	MYSQL_TYPE_TIMESTAMP +#define FIELD_TYPE_LONGLONG		MYSQL_TYPE_LONGLONG +#define FIELD_TYPE_INT24		MYSQL_TYPE_INT24 +#define FIELD_TYPE_DATE			MYSQL_TYPE_DATE +#define FIELD_TYPE_TIME			MYSQL_TYPE_TIME +#define FIELD_TYPE_DATETIME		MYSQL_TYPE_DATETIME +#define FIELD_TYPE_YEAR			MYSQL_TYPE_YEAR +#define FIELD_TYPE_NEWDATE		MYSQL_TYPE_NEWDATE +#define FIELD_TYPE_ENUM			MYSQL_TYPE_ENUM +#define FIELD_TYPE_SET			MYSQL_TYPE_SET +#define FIELD_TYPE_TINY_BLOB	MYSQL_TYPE_TINY_BLOB +#define FIELD_TYPE_MEDIUM_BLOB	MYSQL_TYPE_MEDIUM_BLOB +#define FIELD_TYPE_LONG_BLOB	MYSQL_TYPE_LONG_BLOB +#define FIELD_TYPE_BLOB			MYSQL_TYPE_BLOB +#define FIELD_TYPE_VAR_STRING	MYSQL_TYPE_VAR_STRING +#define FIELD_TYPE_STRING		MYSQL_TYPE_STRING +#define FIELD_TYPE_CHAR			MYSQL_TYPE_TINY +#define FIELD_TYPE_INTERVAL		MYSQL_TYPE_ENUM +#define FIELD_TYPE_GEOMETRY		MYSQL_TYPE_GEOMETRY +#define FIELD_TYPE_BIT			MYSQL_TYPE_BIT + +#define NOT_NULL_FLAG			    1 +#define PRI_KEY_FLAG			    2 +#define UNIQUE_KEY_FLAG			    4 +#define MULTIPLE_KEY_FLAG		    8 +#define BLOB_FLAG				   16 +#define UNSIGNED_FLAG			   32 +#define ZEROFILL_FLAG			   64 +#define BINARY_FLAG				  128 +#define ENUM_FLAG				  256 +#define AUTO_INCREMENT_FLAG		  512 +#define TIMESTAMP_FLAG			 1024 +#define SET_FLAG				 2048 +#define NO_DEFAULT_VALUE_FLAG	 4096 +#define PART_KEY_FLAG			16384 +#define GROUP_FLAG				32768 +#define NUM_FLAG				32768 + +#define IS_PRI_KEY(n)	((n) & PRI_KEY_FLAG) +#define IS_NOT_NULL(n)	((n) & NOT_NULL_FLAG) +#define IS_BLOB(n)		((n) & BLOB_FLAG) +#define IS_NUM(t)		((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL) + + +/* see mysqlnd_charset.c for more information */ +#define MYSQLND_BINARY_CHARSET_NR	63 + + +/* +          /-----> CONN_CLOSE  <---------------\ +         |           ^                         \ +         |           |                         \ +	CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA +	    ^                                      | +		\-------------------------------------/ +*/ +typedef enum mysqlnd_connection_state +{ +	CONN_ALLOCED = 0, +	CONN_READY, +	CONN_QUERY_SENT, +	CONN_SENDING_LOAD_DATA, +	CONN_FETCHING_DATA, +	CONN_NEXT_RESULT_PENDING, +	CONN_QUIT_SENT, /* object is "destroyed" at this stage */ +} enum_mysqlnd_connection_state; + + +typedef enum mysqlnd_stmt_state +{ +	MYSQLND_STMT_INITTED = 0, +	MYSQLND_STMT_PREPARED, +	MYSQLND_STMT_EXECUTED, +	MYSQLND_STMT_WAITING_USE_OR_STORE, +	MYSQLND_STMT_USE_OR_STORE_CALLED, +	MYSQLND_STMT_USER_FETCHING, /* fetch_row_buff or fetch_row_unbuf */ +} enum_mysqlnd_stmt_state; + + +typedef enum param_bind_flags +{ +	MYSQLND_PARAM_BIND_BLOB_USED = 1 +} enum_param_bind_flags; + + +/* PS */ +enum mysqlnd_stmt_attr +{ +	STMT_ATTR_UPDATE_MAX_LENGTH, +	STMT_ATTR_CURSOR_TYPE, +	STMT_ATTR_PREFETCH_ROWS +}; + +enum myslqnd_cursor_type +{ +	CURSOR_TYPE_NO_CURSOR= 0, +	CURSOR_TYPE_READ_ONLY= 1, +	CURSOR_TYPE_FOR_UPDATE= 2, +	CURSOR_TYPE_SCROLLABLE= 4 +}; + +typedef enum mysqlnd_connection_close_type +{ +	MYSQLND_CLOSE_EXPLICIT = 0, +	MYSQLND_CLOSE_IMPLICIT, +	MYSQLND_CLOSE_DISCONNECTED, +	MYSQLND_CLOSE_LAST	/* for checking, should always be last */ +} enum_connection_close_type; + +typedef enum mysqlnd_collected_stats +{ +	STAT_BYTES_SENT, +	STAT_BYTES_RECEIVED, +	STAT_PACKETS_SENT, +	STAT_PACKETS_RECEIVED, +	STAT_PROTOCOL_OVERHEAD_IN, +	STAT_PROTOCOL_OVERHEAD_OUT, +	STAT_RSET_QUERY, +	STAT_NON_RSET_QUERY, +	STAT_NO_INDEX_USED, +	STAT_BAD_INDEX_USED, +	STAT_BUFFERED_SETS, +	STAT_UNBUFFERED_SETS, +	STAT_PS_BUFFERED_SETS, +	STAT_PS_UNBUFFERED_SETS, +	STAT_FLUSHED_NORMAL_SETS, +	STAT_FLUSHED_PS_SETS, +	STAT_PS_PREPARED_NEVER_EXECUTED, +	STAT_PS_PREPARED_ONCE_USED, +	STAT_ROWS_FETCHED_FROM_SERVER_NORMAL, +	STAT_ROWS_FETCHED_FROM_SERVER_PS, +	STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL, +	STAT_ROWS_BUFFERED_FROM_CLIENT_PS, +	STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF, +	STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF, +	STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF, +	STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF, +	STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR, +	STAT_ROWS_SKIPPED_NORMAL, +	STAT_ROWS_SKIPPED_PS, +	STAT_COPY_ON_WRITE_SAVED, +	STAT_COPY_ON_WRITE_PERFORMED, +	STAT_CMD_BUFFER_TOO_SMALL, +	STAT_CONNECT_SUCCESS, +	STAT_CONNECT_FAILURE, +	STAT_CONNECT_REUSED, +	STAT_RECONNECT, +	STAT_PCONNECT_SUCCESS, +	STAT_OPENED_CONNECTIONS, +	STAT_OPENED_PERSISTENT_CONNECTIONS, +	STAT_CLOSE_EXPLICIT, +	STAT_CLOSE_IMPLICIT, +	STAT_CLOSE_DISCONNECT, +	STAT_CLOSE_IN_MIDDLE, +	STAT_FREE_RESULT_EXPLICIT, +	STAT_FREE_RESULT_IMPLICIT, +	STAT_STMT_CLOSE_EXPLICIT, +	STAT_STMT_CLOSE_IMPLICIT, +	STAT_MEM_EMALLOC_COUNT, +	STAT_MEM_EMALLOC_AMMOUNT, +	STAT_MEM_ECALLOC_COUNT, +	STAT_MEM_ECALLOC_AMMOUNT, +	STAT_MEM_EREALLOC_COUNT, +	STAT_MEM_EREALLOC_AMMOUNT, +	STAT_MEM_EFREE_COUNT, +	STAT_MEM_MALLOC_COUNT, +	STAT_MEM_MALLOC_AMMOUNT, +	STAT_MEM_CALLOC_COUNT, +	STAT_MEM_CALLOC_AMMOUNT, +	STAT_MEM_REALLOC_COUNT, +	STAT_MEM_REALLOC_AMMOUNT, +	STAT_MEM_FREE_COUNT, +	STAT_LAST /* Should be always the last */ +} enum_mysqlnd_collected_stats; + + +#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1 + + +#endif	/* MYSQLND_ENUM_N_DEF_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h new file mode 100644 index 0000000000..8f8e0e69bc --- /dev/null +++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h @@ -0,0 +1,121 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ + +*/ + +#ifndef MYSQLND_LIBMYSQL_COMPAT_H +#define MYSQLND_LIBMYSQL_COMPAT_H + +/* Global types and definitions*/ +#define MYSQL_NO_DATA			MYSQLND_NO_DATA +#define MYSQL_DATA_TRUNCATED	MYSQLND_DATA_TRUNCATED +#define MYSQL_STMT				MYSQLND_STMT +#define MYSQL_FIELD				MYSQLND_FIELD +#define MYSQL_RES				MYSQLND_RES +#define MYSQL_ROW				MYSQLND_ROW +#define MYSQL					MYSQLND +#define my_bool					zend_bool +#define my_ulonglong			mynd_ulonglong + +#define MYSQL_VERSION_ID		MYSQLND_VERSION_ID +#define MYSQL_SERVER_VERSION	MYSQLND_VERSION +#define MYSQL_ERRMSG_SIZE		MYSQLND_ERRMSG_SIZE +#define SQLSTATE_LENGTH			MYSQLND_SQLSTATE_LENGTH + +#define SERVER_QUERY_NO_GOOD_INDEX_USED	MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED	 +#define SERVER_QUERY_NO_INDEX_USED		MYSQLND_SERVER_QUERY_NO_INDEX_USED + + +/* functions */ +#define mysql_affected_rows(r)			mysqlnd_affected_rows((r)) +#define mysql_autocommit(r,m)			mysqlnd_autocommit((r),(m)) +#define mysql_change_user(r,a,b,c)		mysqlnd_change_user((r), (a), (b), (c)) +#define mysql_character_set_name(c)		mysqlnd_character_set_name((c)) +#define mysql_close(r)					mysqlnd_close((r), MYSQLND_CLOSE_EXPLICIT) +#define mysql_commit(r)					mysqlnd_commit((r)) +#define mysql_data_seek(r,o)			mysqlnd_data_seek((r),(o)) +#define mysql_debug(x)					mysqlnd_debug((x)) +#define mysql_dump_debug_info(r)		mysqlnd_dump_debug_info((r)) +#define mysql_errno(r)					mysqlnd_errno((r)) +#define mysql_error(r)					mysqlnd_error((r)) +#define mysql_escape_string(a,b,c)		mysqlnd_escape_string((a), (b), (c)) +#define mysql_fetch_field(r)			mysqlnd_fetch_field((r)) +#define mysql_fetch_field_direct(r,o)	mysqlnd_fetch_field_direct((r), (o)) +#define mysql_fetch_lengths(r)			mysqlnd_fetch_lengths((r)) +#define mysql_fetch_row(r)				mysqlnd_fetch_row((r)) +#define mysql_field_count(r)			mysqlnd_field_count((r)) +#define mysql_field_seek(r,o)			mysqlnd_field_seek((r), (o)) +#define mysql_field_tell(r)				mysqlnd_field_tell((r)) +#define mysql_init(a)					mysqlnd_init((a)) +#define mysql_insert_id(r)				mysqlnd_insert_id((r)) +#define mysql_kill(r,n)					mysqlnd_kill((r), (n)) +#define mysql_list_dbs(c, wild)			mysqlnd_list_dbs((c), (wild)) +#define mysql_list_fields(c, tab, wild)	mysqlnd_list_fields((c), (tab), (wild)) +#define mysql_list_processes(c)			mysqlnd_list_processes((c)) +#define mysql_list_tables(c, wild)		mysqlnd_list_tables((c), (wild)) +#define mysql_more_results(r)			mysqlnd_more_results((r)) +#define mysql_next_result(r)			mysqlnd_next_result((r)) +#define mysql_num_fields(r)				mysqlnd_num_fields((r)) +#define mysql_num_rows(r)				mysqlnd_num_rows((r)) +#define mysql_ping(r)					mysqlnd_ping((r)) +#define mysql_real_escape_string(r,a,b,c) mysqlnd_real_escape_string((r), (a), (b), (c)) +#define mysql_real_query(r,a,b)			mysqlnd_query((r), (a), (b)) +#define mysql_rollback(r)				mysqlnd_rollback((r)) +#define mysql_select_db(r,a)			mysqlnd_select_db((r), (a) ,strlen((a))) +#define mysql_set_server_option(r,o)	mysqlnd_set_server_option((r), (o)) +#define mysql_set_character_set(r,a)	mysqlnd_set_character_set((r), (a)) +#define mysql_sqlstate(r)				mysqlnd_sqlstate((r)) +#define mysql_stmt_affected_rows(s)		mysqlnd_stmt_affected_rows((s)) +#define mysql_stmt_field_count(s)		mysqlnd_stmt_field_count((s)) +#define mysql_stmt_param_count(s)		mysqlnd_stmt_param_count((s)) +#define mysql_stmt_num_rows(s)			mysqlnd_stmt_num_rows((s)) +#define mysql_stmt_insert_id(s)			mysqlnd_stmt_insert_id((s)) +#define mysql_stmt_close(s)				mysqlnd_stmt_close((s)) +#define mysql_stmt_errno(s)				mysqlnd_stmt_errno((s)) +#define mysql_stmt_error(s)				mysqlnd_stmt_error((s)) +#define mysql_stmt_sqlstate(s)			mysqlnd_stmt_sqlstate((s)) +#define mysql_stmt_prepare(s,q,l)		mysqlnd_stmt_prepare((s), (q), (l)) +#define mysql_stmt_execute(s)			mysqlnd_stmt_execute((s)) +#define mysql_stmt_reset(s)				mysqlnd_stmt_reset((s)) +#define mysql_stmt_store_result(s)		mysqlnd_stmt_store_result((s)) +#define mysql_stmt_free_result(s)		mysqlnd_stmt_free_result((s)) +#define mysql_stmt_data_seek(s,r)		mysqlnd_stmt_data_seek((s), (r)) +#define mysql_stmt_send_long_data(s,p,d,l) mysqlnd_stmt_send_long_data((s), (p), (d), (l)) +#define mysql_stmt_attr_get(s,a,v)		mysqlnd_stmt_attr_get((s), (a), (v)) +#define mysql_stmt_attr_set(s,a,v)		mysqlnd_stmt_attr_set((s), (a), (v)) +#define mysql_stmt_param_metadata(s)	mysqlnd_stmt_param_metadata((s)) +#define mysql_stmt_result_metadata(s)	mysqlnd_stmt_result_metadata((s)) +#define mysql_thread_safe()				mysqlnd_thread_safe() +#define mysql_info(r)					mysqlnd_info((r)) +#define mysql_options(r,a,b)			mysqlnd_options((r), (a), (b)) +#define mysql_stmt_init(r)				mysqlnd_stmt_init((r)) +#define mysql_free_result(r)			mysqlnd_free_result((r), FALSE) +#define mysql_store_result(r)			mysqlnd_store_result((r)) +#define mysql_use_result(r)				mysqlnd_use_result((r)) +#define mysql_thread_id(r)				mysqlnd_thread_id((r)) +#define mysql_get_client_info()			mysqlnd_get_client_info() +#define mysql_get_client_version()		mysqlnd_get_client_version() +#define mysql_get_host_info(r)			mysqlnd_get_host_info((r)) +#define mysql_get_proto_info(r)			mysqlnd_get_proto_info((r)) +#define mysql_get_server_info(r)		mysqlnd_get_server_info((r)) +#define mysql_get_server_version(r)		mysqlnd_get_server_version((r)) +#define mysql_warning_count(r)			mysqlnd_warning_count((r)) +#define mysql_eof(r)					(((r)->unbuf && (r)->unbuf->eof_reached) || (r)->data) + +#endif /* MYSQLND_LIBMYSQL_COMPAT_H */ diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c new file mode 100644 index 0000000000..2e5466aa07 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_loaddata.c @@ -0,0 +1,263 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_globals.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" + +enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, +										enum php_mysql_packet_type ok_packet, +										zend_bool silent, enum php_mysqlnd_server_command command +									  	TSRMLS_DC); + + +#define ALLOC_CALLBACK_ARGS(a, b, c)\ +if (c) {\ +	a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ +	for (i = b; i < c; i++) {\ +		a[i] = mnd_emalloc(sizeof(zval *));\ +		MAKE_STD_ZVAL(*a[i]);\ +	}\ +} + +#define FREE_CALLBACK_ARGS(a, b, c)\ +if (a) {\ +	for (i=b; i < c; i++) {\ +		zval_ptr_dtor(a[i]);\ +		mnd_efree(a[i]);\ +	}\ +	mnd_efree(a);\ +} + +/* {{{ mysqlnd_local_infile_init */ +static +int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS_DC) +{ +	MYSQLND_INFILE_INFO		*info; +	php_stream_context 		*context = NULL; + +	DBG_ENTER("mysqlnd_local_infile_init"); + +	*ptr= info= ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); + +	/* check open_basedir */ +	if (PG(open_basedir)) { +		if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) { +			strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file"); +			info->error_no = CR_UNKNOWN_ERROR; +			DBG_RETURN(1); +		} +	} + +	info->filename = filename; +	info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context); + +	if (info->fd == NULL) { +		snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename); +		info->error_no = MYSQLND_EE_FILENOTFOUND;  +		DBG_RETURN(1); +	} + +	DBG_RETURN(0); +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_read */ +static +int mysqlnd_local_infile_read(void *ptr, char *buf, uint buf_len TSRMLS_DC) +{ +	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr; +	int			count; + +    DBG_ENTER("mysqlnd_local_infile_read"); + +	count = (int)php_stream_read(info->fd, buf, buf_len); + +	if (count < 0) { +		strcpy(info->error_msg, "Error reading file"); +		info->error_no = CR_UNKNOWN_ERROR; +	} + +	DBG_RETURN(count); +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_error */ +static +int	mysqlnd_local_infile_error(void *ptr, char *error_buf, uint error_buf_len TSRMLS_DC) +{ +	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr; + +	DBG_ENTER("mysqlnd_local_infile_error"); + +	if (info) { +		strncpy(error_buf, info->error_msg, error_buf_len); +		DBG_INF_FMT("have info, %d", info->error_no); +		DBG_RETURN(info->error_no); +	} + +	strncpy(error_buf, "Unknown error", error_buf_len); +	DBG_INF_FMT("no info, %d", CR_UNKNOWN_ERROR); +	DBG_RETURN(CR_UNKNOWN_ERROR); +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_end */ +static +void mysqlnd_local_infile_end(void *ptr TSRMLS_DC) +{ +	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr; + +	if (info) { +		/* php_stream_close segfaults on NULL */ +		if (info->fd) { +			php_stream_close(info->fd); +			info->fd = NULL; +		} +		mnd_efree(info); +	} +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_default */ +PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn) +{ +	conn->infile.local_infile_init = mysqlnd_local_infile_init; +	conn->infile.local_infile_read = mysqlnd_local_infile_read; +	conn->infile.local_infile_error = mysqlnd_local_infile_error; +	conn->infile.local_infile_end = mysqlnd_local_infile_end; +} +/* }}} */ + +/* {{{ mysqlnd_set_local_infile_handler */ +PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname) +{ +	if (!conn->infile.callback) { +		MAKE_STD_ZVAL(conn->infile.callback); +	} else { +		zval_dtor(conn->infile.callback); +	} +	ZVAL_STRING(conn->infile.callback, (char*) funcname, 1); +} +/* }}} */ + + +static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file"; + + +/* {{{ mysqlnd_handle_local_infile */ +enum_func_status +mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC) +{ +	char				*buf; +	char				empty_packet[MYSQLND_HEADER_SIZE]; +	enum_func_status	result = FAIL; +	uint				buflen = 4096; +	void				*info = NULL; +	int					bufsize; +	size_t				ret; +	MYSQLND_INFILE		infile; + +	DBG_ENTER("mysqlnd_handle_local_infile"); + +	if (!(conn->options.flags & CLIENT_LOCAL_FILES)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden"); +		/* write empty packet to server */ +		ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); +		*is_warning = TRUE; +		goto infile_error; +	} + +	infile = conn->infile; +	/* allocate buffer for reading data */ +	buf = (char *)mnd_ecalloc(1, buflen); +	 +	*is_warning = FALSE; + +	/* init handler: allocate read buffer and open file */ +	if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) { +		*is_warning = TRUE; +		/* error occured */ +		strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); +		conn->error_info.error_no = +				infile.local_infile_error(info, conn->error_info.error, +										  sizeof(conn->error_info.error) TSRMLS_CC); +		/* write empty packet to server */ +		ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); +		goto infile_error; +	} + +	/* read data */ +	while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, +												buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { +		if ((ret = mysqlnd_stream_write_w_header(conn, buf, bufsize TSRMLS_CC)) < 0) { +			DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); +			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); +			goto infile_error; +		} +	} + +	/* send empty packet for eof */ +	if ((ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC)) < 0) { +		SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); +		goto infile_error; +	} + +	/* error during read occured */ +	if (bufsize < 0) { +		*is_warning = TRUE; +		DBG_ERR_FMT("Bufsize < 0, warning,  %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); +		strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); +		conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error, +															 sizeof(conn->error_info.error) TSRMLS_CC); +		goto infile_error; +	} + +	result = PASS; + +infile_error: +	/* get response from server and update upsert values */ +	if (FAIL == mysqlnd_simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY TSRMLS_CC)) { +		result = FAIL; +		goto infile_error; +	} + +	(*conn->infile.local_infile_end)(info TSRMLS_CC); +	mnd_efree(buf); +	DBG_INF_FMT("%s", result == PASS? "PASS":"FAIL"); +	DBG_RETURN(result); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_palloc.c b/ext/mysqlnd/mysqlnd_palloc.c new file mode 100644 index 0000000000..fba769038a --- /dev/null +++ b/ext/mysqlnd/mysqlnd_palloc.c @@ -0,0 +1,565 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_palloc.h" +#include "mysqlnd_debug.h" + +/* Used in mysqlnd_debug.c */ +char * mysqlnd_palloc_zval_ptr_dtor_name = "mysqlnd_palloc_zval_ptr_dtor"; +char * mysqlnd_palloc_get_zval_name = "mysqlnd_palloc_get_zval"; + + +#ifdef ZTS +#define LOCK_PCACHE(cache)  tsrm_mutex_lock((cache)->LOCK_access) +#define UNLOCK_PCACHE(cache) tsrm_mutex_unlock((cache)->LOCK_access) +#else +#define LOCK_PCACHE(cache) +#define UNLOCK_PCACHE(cache) +#endif + + +#if PHP_MAJOR_VERSION < 6 +#define IS_UNICODE_DISABLED  (1) +#else +#define IS_UNICODE_DISABLED  (!UG(unicode)) +#endif + + +/* {{{ _mysqlnd_palloc_init_cache */ +PHPAPI MYSQLND_ZVAL_PCACHE* _mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC) +{ +	MYSQLND_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_ZVAL_PCACHE)); +	unsigned int i; + +	DBG_ENTER("_mysqlnd_palloc_init_cache"); +	DBG_INF_FMT("cache=%p size=%u", ret, cache_size); + +#ifdef ZTS +	ret->LOCK_access = tsrm_mutex_alloc(); +#endif + +	ret->max_items = cache_size; +	ret->free_items = cache_size; +	ret->references = 1; + +	/* 1. First initialize the free list part of the structure */ +	/* One more for empty position of last_added - always 0x0, bounds checking */ +	ret->free_list.ptr_line = calloc(ret->max_items + 1, sizeof(mysqlnd_zval *)); +	ret->free_list.last_added = ret->free_list.ptr_line + ret->max_items; + +	/* 3. Allocate and initialize our zvals and initialize the free list */ +	ret->block = calloc(ret->max_items, sizeof(mysqlnd_zval)); +	ret->last_in_block = &(ret->block[ret->max_items]); +	for (i = 0; i < ret->max_items; i++) { +		/* 1. Initialize */ +		INIT_PZVAL(&(ret->block[i].zv)); +		ZVAL_NULL(&(ret->block[i].zv)); +		/* Assure it will never be freed before MSHUTDOWN */ +		ZVAL_ADDREF(&(ret->block[i].zv)); +		/* 2. Add to the free list  */ +		*(--ret->free_list.last_added) = &(ret->block[i]); +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_get_cache_reference */ +MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_get_cache_reference(MYSQLND_ZVAL_PCACHE * const cache) +{ +	if (cache) { +		LOCK_PCACHE(cache); +		cache->references++; +		UNLOCK_PCACHE(cache); +	} +	return cache; +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_free_cache */ +/* +  As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with +  copy_ctor but scrap what they point to with zval_dtor() and then just free our +  pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them +  to the free list after usage. We ZVAL_NULL() them when we allocate them in the  +  constructor of the cache. +*/ +void _mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC) +{ +	DBG_ENTER("_mysqlnd_palloc_free_cache"); +	DBG_INF_FMT("cache=%p", cache); + +#ifdef ZTS +	tsrm_mutex_free(cache->LOCK_access); +#endif + +	/* Data in pointed by 'block' was cleaned in RSHUTDOWN */ +	mnd_free(cache->block); +	mnd_free(cache->free_list.ptr_line); +	mnd_free(cache); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_palloc_init_thd_cache */ +PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC) +{ +	MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE)); +	DBG_ENTER("_mysqlnd_palloc_init_thd_cache"); +	DBG_INF_FMT("ret = %p", ret); + +	ret->parent = mysqlnd_palloc_get_cache_reference(cache); + +#ifdef ZTS +	ret->thread_id = tsrm_thread_id(); +#endif + +	ret->references = 1; + +	/* 1. Initialize the GC list */ +	ret->gc_list.ptr_line = calloc(cache->max_items, sizeof(mysqlnd_zval *)); +	/* Backward and forward looping is possible */ +	ret->gc_list.last_added = ret->gc_list.ptr_line; + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_get_thd_cache_reference */ +MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache) +{ +	if (cache) { +		++cache->references; +		mysqlnd_palloc_get_cache_reference(cache->parent); +	} +	return cache; +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_free_cache */ +/* +  As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with +  copy_ctor but scrap what they point to with zval_dtor() and then just free our +  pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them +  to the free list after usage. We ZVAL_NULL() them when we allocate them in the  +  constructor of the cache. +*/ +static +void mysqlnd_palloc_free_thd_cache(MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_palloc_free_thd_cache"); +	DBG_INF_FMT("cache=%p", cache); + +	mnd_free(cache->gc_list.ptr_line); +	mnd_free(cache); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_palloc_free_thd_cache_reference */ +PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC) +{ +	DBG_ENTER("_mysqlnd_palloc_free_thd_cache_reference"); +	if (*cache) { +		DBG_INF_FMT("cache=%p refs=%d", *cache, (*cache)->references); +		--(*cache)->parent->references; + +		if (--(*cache)->references == 0) { +			mysqlnd_palloc_free_thd_cache(*cache TSRMLS_CC); +		} +		*cache = NULL; +	} +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* +  The cache line is a big contiguous array of zval pointers. +  Because the CPU cache will cache starting from an address, and not +  before it, then we have to organize our structure according to this. +  Thus, if 'last_added' is valid pointer (not NULL) then last_added is +  increased. When zval is cached, if there is room, last_added is decreased +  and then the zval pointer will be assigned to it. This means that some +  positions may become hot points and stay in the cache. +  Imagine we have 5 pointers in a line +  1. last_added = list_item->ptr_line + cache->max_items; +  2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL +  3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL +  4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL +	0x0 +	0x0 +	0x0 +	0x0 +	0x0 +	--- +	empty_position, always 0x0 <-- last_added + +  5. free_zval -> if (free_items++ != max_items) {// we can add more +				    *(--last_added) = zval_ptr; +				 } +	(memory addresses increase downwards) +	0x0 +	0x0 +	0x0 +	0x0  +	0xA <-- last_added +	--- +	0x0 + +  6. free_zval -> if (free_items++ != max_items) {// we can add more +				   *(--last_added) = zval_ptr; +				 } +	0x0 +	0x0 +	0x0 +	0xB <-- last_added +	0xA  +	--- +	0x0 + +  7. free_zval -> if (free_items++ != max_items) {// we can add more +				   *(--last_added) = zval_ptr; +				 } +	0x0 +	0x0 +	0xC <-- last_added +	0xB +	0xA +	--- +	0x0 + +  8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +	0x0 +	0x0 +	0x0 +	0xB <-- last_added +	0xA +	--- +	0x0 + +  9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +	0x0 +	0x0 +	0x0 +	0x0 +	0xA <-- last_added +	--- +	0x0 + +  10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; +	0x0 +	0x0 +	0x0 +	0x0 +	0x0 +	--- +	0x0 <-- last_added + +*/ + + +/* {{{ mysqlnd_palloc_get_zval */ +void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bool *allocated TSRMLS_DC) +{ +	void *ret = NULL; + +	DBG_ENTER("mysqlnd_palloc_get_zval"); +	DBG_INF_FMT("cache=%p *last_added=%p free_items=%d", +				thd_cache, thd_cache? thd_cache->parent->free_list.last_added:NULL, +				thd_cache->parent->free_items); + +	if (thd_cache) { +		MYSQLND_ZVAL_PCACHE *cache = thd_cache->parent; +		LOCK_PCACHE(cache); +		 +		if ((ret = *cache->free_list.last_added)) { +			*cache->free_list.last_added++ = NULL; +			*allocated = FALSE; +#ifdef ZTS +			((mysqlnd_zval *) ret)->thread_id = thd_cache->thread_id; +#endif +			--cache->free_items; +			++cache->get_hits; +		} else { +			++cache->get_misses; +		} +		UNLOCK_PCACHE(cache); +	} +	if (!ret) { +		/* +		  We allocate a bit more. The user of this function will use it, but at +		  end it will use only the zval part. Because the zval part is first then +		  when freeing the zval part the whole allocated block will be cleaned, not +		  only the zval part (by the Engine when destructing the zval). +		*/ +		ALLOC_ZVAL(ret); +		INIT_PZVAL((zval *) ret); +		*allocated = TRUE; +	} else { +		/* This will set the refcount to 1, increase it, to keep the variable */ +		INIT_PZVAL(&((mysqlnd_zval *) ret)->zv); +		ZVAL_ADDREF(&(((mysqlnd_zval *)ret)->zv)); +	} + +	DBG_INF_FMT("allocated=%d ret=%p", *allocated, ret); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_zval_ptr_dtor */ +void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd_cache, +								  enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC) +{ +	MYSQLND_ZVAL_PCACHE *cache; +	DBG_ENTER("mysqlnd_palloc_zval_ptr_dtor"); +	DBG_INF_FMT("cache=%p parent_block=%p last_in_block=%p *zv=%p refc=%d type=%d ", +				thd_cache, +				thd_cache->parent? thd_cache->parent->block:NULL, +				thd_cache->parent? thd_cache->parent->last_in_block:NULL, +				*zv, ZVAL_REFCOUNT(*zv), type); +	*copy_ctor_called = FALSE; +	/* Check whether cache is used and the zval is from the cache */ +	if (!thd_cache || !(cache = thd_cache->parent) || ((char *)*zv < (char *)thd_cache->parent->block || +													   (char *)*zv > (char *)thd_cache->parent->last_in_block)) { +		/* +		  This zval is not from the cache block. +		  Thus the refcount is -1 than of a zval from the cache, +		  because the zvals from the cache are owned by it. +		*/ +		if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) { +			; /* do nothing, zval_ptr_dtor will do the job*/ +		} else if (ZVAL_REFCOUNT(*zv) > 1) { +			/* +			  Not a prepared statement, then we have to +			  call copy_ctor and then zval_ptr_dtor() + +			  In Unicode mode the destruction  of the zvals should not call +			  zval_copy_ctor() because then we will leak. +			  I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set +			  to check if we need to call copy_ctor(). + +			  If the type is IS_UNICODE, which can happen with PHP6, then we don't +			  need to copy_ctor, as the data doesn't point to our internal buffers. +			  If it's string (in PHP5 always) and in PHP6 if data is binary, then +			  it still points to internal buffers and has to be copied. +			*/ +			if (Z_TYPE_PP(zv) == IS_STRING) { +				zval_copy_ctor(*zv); +			} +			*copy_ctor_called = TRUE; +		} else { +			if (Z_TYPE_PP(zv) == IS_STRING) { +				ZVAL_NULL(*zv); +			} +		} +		zval_ptr_dtor(zv); +		DBG_VOID_RETURN; +	} + +	/* The zval is from our cache */ +	/* refcount is always > 1, because we call ZVAL_ADDREF(). Thus test refcount > 2 */ +	if (ZVAL_REFCOUNT(*zv) > 2) { +		/* +		  Because the zval is first element in mysqlnd_zval structure, then we can +		  do upcasting from zval to mysqlnd_zval here. Because we know that this +		  zval is part of our pre-allocated block. + +		  Now check whether this zval points to ZE allocated memory or to our +		  buffers. If it points to the internal buffers, call copy_ctor() +		  which will do estrndup for strings. And nothing for null, int, double. + +		  This branch will be skipped for PS, because there is no need to copy +		  what is pointed by them, as they don't point to the internal buffers. +		*/ +		if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_INT_BUFFER) { +			zval_copy_ctor(*zv); +			*copy_ctor_called = TRUE; +			((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_EXT_BUFFER; +		} +		/* +		  This will decrease the counter of the user-level (mysqlnd). When the engine +		  layer (the script) has finished working this this zval, when the variable is +		  no more used, out of scope whatever, then it will try zval_ptr_dtor() but +		  and the refcount will reach 1 and the engine won't try to destruct the +		  memory allocated by us. +		*/ +		zval_ptr_dtor(zv); + +		/* +		  Unfortunately, we can't return this variable to the free_list +		  because it's still used. And this cleaning up will happen at request +		  shutdown :(. +		*/ +		LOCK_PCACHE(cache); +		++cache->put_misses; +		*(thd_cache->gc_list.last_added++) = (mysqlnd_zval *)*zv; +		UNLOCK_PCACHE(cache); +	} else { +		/* No user reference */ +		if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_EXT_BUFFER) { +			/* PS are here and also in Unicode mode, for non-binary  */ +			zval_dtor(*zv); +		} +		LOCK_PCACHE(cache); +		++cache->put_hits; +		++cache->free_items; +		((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_FREE; +		ZVAL_DELREF(*zv);	/* Make it 1 */ +		ZVAL_NULL(*zv); +#ifdef ZTS +		memset(&((mysqlnd_zval *)*zv)->thread_id, 0, sizeof(THREAD_T)); +#endif +		*(--cache->free_list.last_added) = (mysqlnd_zval *)*zv; + +		UNLOCK_PCACHE(cache); +	} +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_palloc_rinit */ +PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC) +{ +	return mysqlnd_palloc_init_thd_cache(cache); +} +/* }}} */ + + +/* {{{ _mysqlnd_palloc_rshutdown */ +PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache TSRMLS_DC) +{ +	MYSQLND_ZVAL_PCACHE *cache; +	mysqlnd_zval **p; + +	DBG_ENTER("_mysqlnd_palloc_rshutdown"); +	DBG_INF_FMT("cache=%p", thd_cache); + +	if (!thd_cache || !(cache = thd_cache->parent)) { +		return; +	} + +	/* +	  Keep in mind that for pthreads pthread_equal() should be used to be +	  fully standard compliant. However, the PHP code all-around, incl. the +	  the Zend MM uses direct comparison. +	*/ +	p = thd_cache->gc_list.ptr_line; +	while (p < thd_cache->gc_list.last_added) { +		zval_dtor(&(*p)->zv); +		p++; +	} + +	p = thd_cache->gc_list.ptr_line; +	LOCK_PCACHE(cache); +	while (p < thd_cache->gc_list.last_added) { +		(*p)->point_type = MYSQLND_POINTS_FREE; +		*(--cache->free_list.last_added) = *p; +		++cache->free_items; +#ifdef ZTS +		memset(&((*p)->thread_id), 0, sizeof(THREAD_T)); +#endif	 +		p++; +	} +	UNLOCK_PCACHE(cache); + +	mysqlnd_palloc_free_thd_cache_reference(&thd_cache); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_palloc_rshutdown */ +PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value) +{ +	if (cache) { +#if PHP_MAJOR_VERSION >= 6 +		TSRMLS_FETCH(); +#endif + +		LOCK_PCACHE(cache); +		array_init(return_value); +#if PHP_MAJOR_VERSION >= 6 +		if (UG(unicode)) { +			UChar *ustr; +			int ulen; + +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_hits", sizeof("put_hits") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_misses", sizeof("put_misses") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_hits", sizeof("get_hits") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_misses", sizeof("get_misses") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "size", sizeof("size") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "free_items", sizeof("free_items") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "references", sizeof("references") TSRMLS_CC); +			add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); +			efree(ustr); +		} else +#endif +		{ +			add_assoc_long_ex(return_value, "put_hits",		sizeof("put_hits"),		cache->put_hits); +			add_assoc_long_ex(return_value, "put_misses",	sizeof("put_misses"),	cache->put_misses); +			add_assoc_long_ex(return_value, "get_hits",		sizeof("get_hits"),		cache->get_hits); +			add_assoc_long_ex(return_value, "get_misses",	sizeof("get_misses"),	cache->get_misses); +			add_assoc_long_ex(return_value, "size",			sizeof("size"),			cache->max_items); +			add_assoc_long_ex(return_value, "free_items",	sizeof("free_items"),	cache->free_items); +			add_assoc_long_ex(return_value, "references",	sizeof("references"),	cache->references); +		} +		UNLOCK_PCACHE(cache); +	} else { +		ZVAL_NULL(return_value); +	} +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_palloc.h b/ext/mysqlnd/mysqlnd_palloc.h new file mode 100644 index 0000000000..8ef0eaab87 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_palloc.h @@ -0,0 +1,114 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#ifndef MYSQLND_PALLOC_H +#define MYSQLND_PALLOC_H + +/* Used in mysqlnd_debug.c */ +extern char * mysqlnd_palloc_zval_ptr_dtor_name; +extern char * mysqlnd_palloc_get_zval_name; + + +/* Session caching allocator */ +struct st_mysqlnd_zval_list { +	zval 	**ptr_line; +	zval	**last_added; +}; + +typedef struct st_mysqlnd_zval_cache	MYSQLND_ZVAL_CACHE; + +struct st_mysqlnd_zval_cache { +	struct st_mysqlnd_zval_list	*free_list; +	unsigned int		free_items; +	unsigned int		max_items; +	unsigned int		references; +	unsigned long		get_hits; +	unsigned long		get_misses; +	unsigned long		put_hits; +	unsigned long		put_full_misses; +	unsigned long		put_refcount_misses; +}; + + +enum mysqlnd_zval_ptr_type +{ +	MYSQLND_POINTS_INT_BUFFER, +	MYSQLND_POINTS_EXT_BUFFER, +	MYSQLND_POINTS_FREE +}; + +/* Persistent caching allocator */ +typedef struct st_mysqlnd_zval { +	/* Should be first */ +	zval		zv; +	enum mysqlnd_zval_ptr_type	point_type; +#ifdef ZTS +	THREAD_T	thread_id; +#endif +} mysqlnd_zval; + + +typedef struct st_mysqlnd_ndzval_list { +	mysqlnd_zval 	**ptr_line;		/* we allocate this, all are pointers to the block */ +	mysqlnd_zval	**last_added;	/* this points to the ptr_line, and moves left-right. It's our stack */ +} mysqlnd_ndzval_list; + + +struct st_mysqlnd_zval_pcache { +	mysqlnd_zval		*block; +	mysqlnd_zval		*last_in_block; +	mysqlnd_ndzval_list	free_list;	/* Fetch from here */ + +#ifdef ZTS +	MUTEX_T 			LOCK_access; +#endif +	unsigned int		references; + +	/* These are just for statistics and not used for operational purposes */ +	unsigned int		free_items; +	unsigned int		max_items; + +	unsigned long		get_hits; +	unsigned long		get_misses; +	unsigned long		put_hits; +	unsigned long		put_misses; +}; + +struct st_mysqlnd_thread_zval_pcache { +	struct st_mysqlnd_zval_pcache *parent; + +	unsigned int		references; +#ifdef ZTS +	THREAD_T			thread_id; +#endif +	mysqlnd_ndzval_list	gc_list;		/* GC these from time to time */ +}; + +#endif /* MYSQLND_PALLOC_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_portability.h b/ext/mysqlnd/mysqlnd_portability.h new file mode 100644 index 0000000000..37aeb67e21 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_portability.h @@ -0,0 +1,515 @@ +/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB  +This file is public domain and comes with NO WARRANTY of any kind */ + +/* +  Parts of the original, which are not applicable to mysqlnd have been removed. +   +  With small modifications, mostly casting but adding few more macros by +  Andrey Hristov <andrey@mysql.com> . The additions are in the public domain and +  were added to improve the header file, to get it more consistent. +*/ + +/* Comes from global.h as OFFSET, renamed to STRUCT_OFFSET */ +#define STRUCT_OFFSET(t, f)   ((size_t)(char *)&((t *)0)->f) + +#ifndef __attribute +#if !defined(__GNUC__) +#define __attribute(A) +#endif +#endif + +#ifdef __CYGWIN__ +/* We use a Unix API, so pretend it's not Windows */ +#undef WIN +#undef WIN32 +#undef _WIN +#undef _WIN32 +#undef _WIN64 +#undef __WIN__ +#undef __WIN32__ +#endif /* __CYGWIN__ */ + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#  include <ext/mysqlnd/config-win.h> +#else  +#  include "ext/mysqlnd/php_mysqlnd_config.h" +#endif /* _WIN32... */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#if SIZEOF_LONG_LONG > 4 && !defined(_LONG_LONG) +#define _LONG_LONG 1        /* For AIX string library */ +#endif + + +/* Go around some bugs in different OS and compilers */ +#if defined(_HPUX_SOURCE) && defined(HAVE_SYS_STREAM_H) +#include <sys/stream.h>        /* HPUX 10.20 defines ulong here. UGLY !!! */ +#define HAVE_ULONG +#endif + + +#if SIZEOF_LONG_LONG > 4 +#define HAVE_LONG_LONG 1 +#endif + +/* Typdefs for easyier portability */ + +#ifndef HAVE_INT8 +#ifndef HAVE_INT8_T +typedef signed char int8;			/* Signed integer >= 8    bits */ +#else +typedef int8_t int8;				/* Signed integer >= 8    bits */ +#endif +#endif + +#ifndef HAVE_UINT8 +#ifndef HAVE_UINT8_T +typedef unsigned char uint8;		/* Unsigned integer >= 8    bits */ +#else +typedef uint8_t uint8;				/* Signed integer >= 8    bits */ +#endif +#endif + +#ifndef HAVE_INT16 +#ifndef HAVE_INT16_T +typedef signed short int16;			/* Signed integer >= 16 bits */ +#else +typedef int16_t int16;				/* Signed integer >= 16 bits */ +#endif +#endif + +#ifndef HAVE_UINT16 +#ifndef HAVE_UINT16_T +typedef unsigned short uint16;		/* Signed integer >= 16 bits */ +#else +typedef uint16_t uint16;			/* Signed integer >= 16 bits */ +#endif +#endif + +#ifndef HAVE_UCHAR +typedef unsigned char uchar;		/* Short for unsigned char */ +#endif + + +#if defined(HAVE_INT32_T) && defined(HAVE_UINT32_T) +typedef int32_t int32; +typedef uint32_t uint32; + +#elif SIZEOF_INT == 4 + +#ifndef HAVE_INT32 +typedef signed int int32; +#endif +#ifndef HAVE_UINT32 +typedef unsigned int uint32; +#endif + +#elif SIZEOF_LONG == 4 + +#ifndef HAVE_INT32 +typedef signed long int32; +#endif +#ifndef HAVE_UINT32 +typedef unsigned long uint32; +#endif + +#else +error "Neither int or long is of 4 bytes width" +#endif + +#if !defined(HAVE_ULONG) && !defined(__USE_MISC) && !defined(ulong) +typedef unsigned long    ulong;    /* Short for unsigned long */ +#endif +#ifndef longlong_defined +#if defined(HAVE_LONG_LONG) && SIZEOF_LONG != 8 +typedef unsigned long long int ulonglong; /* ulong or unsigned long long */ +typedef long long int longlong; +#else +typedef unsigned long    ulonglong;    /* ulong or unsigned long long */ +typedef long  longlong; +#endif +#endif + + +#define int1store(T,A)	do { *((zend_uchar*) (T)) = (A); } while(0) +#define uint1korr(A)	(*(((uint8*)(A)))) + +/* Bit values are sent in reverted order of bytes, compared to normal !!! */ +#define bit_uint2korr(A) ((uint16) (((uint16) (((uchar*) (A))[1])) +\ +									((uint16) (((uchar*) (A))[0]) << 8))) +#define bit_uint3korr(A) ((uint32) (((uint32) (((uchar*) (A))[2])) +\ +									(((uint32) (((uchar*) (A))[1])) << 8) +\ +									(((uint32) (((uchar*) (A))[0])) << 16))) + +#define bit_uint4korr(A) ((uint32) (((uint32) (((uchar*) (A))[3])) +\ +									(((uint32) (((uchar*) (A))[2])) << 8) +\ +									(((uint32) (((uchar*) (A))[1])) << 16) +\ +									(((uint32) (((uchar*) (A))[0])) << 24))) + +#define bit_uint5korr(A)  ((ulonglong)(((uint32) ((uchar) (A)[4])) +\ +                                  (((uint32) ((uchar) (A)[3])) << 8) +\ +                                  (((uint32) ((uchar) (A)[2])) << 16) +\ +                                  (((uint32) ((uchar) (A)[1])) << 24)) +\ +                               (((ulonglong) ((uchar) (A)[0])) << 32)) + +#define bit_uint6korr(A)	((ulonglong)(((uint32) (((uchar*) (A))[5])) +\ +									(((uint32) (((uchar*) (A))[4])) << 8) +\ +									(((uint32) (((uchar*) (A))[3])) << 16) +\ +									(((uint32) (((uchar*) (A))[2])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[1])) +\ +									(((uint32) (((uchar*) (A))[0]) << 8)))) << 32)) + +#define bit_uint7korr(A)	((ulonglong)(((uint32) (((uchar*) (A))[6])) +\ +									(((uint32) (((uchar*) (A))[5])) << 8) +\ +									(((uint32) (((uchar*) (A))[4])) << 16) +\ +									(((uint32) (((uchar*) (A))[3])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[2])) +\ +									(((uint32) (((uchar*) (A))[1])) << 8) +\ +									(((uint32) (((uchar*) (A))[0])) << 16))) << 32)) + + +#define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\ +									(((uint32) (((uchar*) (A))[6])) << 8) +\ +									(((uint32) (((uchar*) (A))[5])) << 16) +\ +									(((uint32) (((uchar*) (A))[4])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[3])) +\ +									(((uint32) (((uchar*) (A))[2])) << 8) +\ +									(((uint32) (((uchar*) (A))[1])) << 16) +\ +									(((uint32) (((uchar*) (A))[0])) << 24))) << 32)) + + +/* +** Define-funktions for reading and storing in machine independent format +**  (low byte first) +*/ + +/* Optimized store functions for Intel x86, non-valid for WIN64 */ +#if defined(__i386__) && !defined(_WIN64) +#define sint2korr(A)    (*((int16 *) (A))) +#define sint3korr(A)    ((int32) ((((uchar) (A)[2]) & 128) ? \ +                   (((uint32) 255L << 24) | \ +                   (((uint32) (uchar) (A)[2]) << 16) |\ +                   (((uint32) (uchar) (A)[1]) << 8) | \ +                    ((uint32) (uchar) (A)[0])) : \ +                   (((uint32) (uchar) (A)[2]) << 16) |\ +                   (((uint32) (uchar) (A)[1]) << 8) | \ +                    ((uint32) (uchar) (A)[0]))) +#define sint4korr(A)  (*((long *) (A))) + +#define uint2korr(A)  (*((uint16 *) (A))) +#define uint3korr(A)  (uint32) (((uint32) ((uchar) (A)[0])) +\ +                               (((uint32) ((uchar) (A)[1])) << 8) +\ +                               (((uint32) ((uchar) (A)[2])) << 16)) +#define uint4korr(A)  (*((unsigned long *) (A))) + + + +#define uint8korr(A)    (*((ulonglong *) (A))) +#define sint8korr(A)    (*((longlong *) (A))) +#define int2store(T,A)    *((uint16*) (T))= (uint16) (A) +#define int3store(T,A)   { \ +                  *(T)=  (uchar) ((A));\ +                  *(T+1)=(uchar) (((uint) (A) >> 8));\ +                  *(T+2)=(uchar) (((A) >> 16)); } +#define int4store(T,A)    *((long *) (T))= (long) (A) +#define int5store(T,A)    { \ +              *((uchar *)(T))= (uchar)((A));\ +              *(((uchar *)(T))+1)=(uchar) (((A) >> 8));\ +              *(((uchar *)(T))+2)=(uchar) (((A) >> 16));\ +              *(((uchar *)(T))+3)=(uchar) (((A) >> 24)); \ +              *(((uchar *)(T))+4)=(uchar) (((A) >> 32)); } + +/* From Andrey Hristov, based on int5store() */ +#define int6store(T,A)    { \ +              *(((uchar *)(T)))= (uchar)((A));\ +              *(((uchar *)(T))+1))=(uchar) (((A) >> 8));\ +              *(((uchar *)(T))+2))=(uchar) (((A) >> 16));\ +              *(((uchar *)(T))+3))=(uchar) (((A) >> 24)); \ +              *(((uchar *)(T))+4))=(uchar) (((A) >> 32)); \ +              *(((uchar *)(T))+5))=(uchar) (((A) >> 40)); } + +#define int8store(T,A)    *((ulonglong *) (T))= (ulonglong) (A) + +typedef union { +  double v; +  long m[2]; +} doubleget_union; +#define doubleget(V,M)    { ((doubleget_union *)&(V))->m[0] = *((long*) (M)); \ +                            ((doubleget_union *)&(V))->m[1] = *(((long*) (M))+1); } +#define doublestore(T,V) { *((long *) (T))     = ((doubleget_union *)&(V))->m[0]; \ +                           *(((long *) (T))+1) = ((doubleget_union *)&(V))->m[1]; } +#define float4get(V,M)	{ *((float *) &(V)) = *((float*) (M)); } +#define float8get(V,M)	doubleget((V),(M)) +/* From Andrey Hristov based on doubleget */ +#define floatget(V,M)    memcpy((char*) &(V),(char*) (M),sizeof(float)) +#define floatstore       float4store +#define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float)) +#define float8store(V,M) doublestore((V),(M)) +#endif /* __i386__ */  + +#ifndef sint2korr +#define sint2korr(A)    (int16) (((int16) ((uchar) (A)[0])) +\ +                                 ((int16) ((int16) (A)[1]) << 8)) +#define sint3korr(A)    ((int32) ((((uchar) (A)[2]) & 128) ? \ +                  (((uint32) 255L << 24) | \ +                  (((uint32) (uchar) (A)[2]) << 16) |\ +                  (((uint32) (uchar) (A)[1]) << 8) | \ +                   ((uint32) (uchar) (A)[0])) : \ +                  (((uint32) (uchar) (A)[2]) << 16) |\ +                  (((uint32) (uchar) (A)[1]) << 8) | \ +                  ((uint32) (uchar) (A)[0]))) +#define sint4korr(A)  (int32) (((int32) ((uchar) (A)[0])) +\ +                              (((int32) ((uchar) (A)[1]) << 8)) +\ +                              (((int32) ((uchar) (A)[2]) << 16)) +\ +                              (((int32) ((int16) (A)[3]) << 24))) + +#define sint8korr(A)  (longlong) uint8korr(A) +#define uint2korr(A)  (uint16) (((uint16) ((uchar) (A)[0])) +\ +                               ((uint16) ((uchar) (A)[1]) << 8)) +#define uint3korr(A)  (uint32) (((uint32) ((uchar) (A)[0])) +\ +                               (((uint32) ((uchar) (A)[1])) << 8) +\ +                               (((uint32) ((uchar) (A)[2])) << 16)) +#define uint4korr(A)  (uint32) (((uint32) ((uchar) (A)[0])) +\ +                               (((uint32) ((uchar) (A)[1])) << 8) +\ +                               (((uint32) ((uchar) (A)[2])) << 16) +\ +                               (((uint32) ((uchar) (A)[3])) << 24)) +#define bit_uint5korr(A)  ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ +                                  (((uint32) ((uchar) (A)[1])) << 8) +\ +                                  (((uint32) ((uchar) (A)[2])) << 16) +\ +                                  (((uint32) ((uchar) (A)[3])) << 24)) +\ +                               (((ulonglong) ((uchar) (A)[4])) << 32)) +/* From Andrey Hristov, based on uint5korr */ +#define bit_uint6korr(A)	((ulonglong)(((uint32) (((uchar*) (A))[5])) +\ +									(((uint32) (((uchar*) (A))[4])) << 8) +\ +									(((uint32) (((uchar*) (A))[3])) << 16) +\ +									(((uint32) (((uchar*) (A))[2])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[1])) +\ +									(((uint32) (((uchar*) (A))[0]) << 8)))) << 32)) + +#define bit_uint7korr(A)	((ulonglong)(((uint32) (((uchar*) (A))[6])) +\ +									(((uint32) (((uchar*) (A))[5])) << 8) +\ +									(((uint32) (((uchar*) (A))[4])) << 16) +\ +									(((uint32) (((uchar*) (A))[3])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[2])) +\ +									(((uint32) (((uchar*) (A))[1])) << 8) +\ +									(((uint32) (((uchar*) (A))[0])) << 16))) << 32)) + + +#define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\ +									(((uint32) (((uchar*) (A))[6])) << 8) +\ +									(((uint32) (((uchar*) (A))[5])) << 16) +\ +									(((uint32) (((uchar*) (A))[4])) << 24)) +\ +									(((ulonglong) (((uint32) (((uchar*) (A))[3])) +\ +									(((uint32) (((uchar*) (A))[2])) << 8) +\ +									(((uint32) (((uchar*) (A))[1])) << 16) +\ +									(((uint32) (((uchar*) (A))[0])) << 24))) << 32)) + +#define uint8korr(A)	((ulonglong)(((uint32) ((uchar) (A)[0])) +\ +									(((uint32) ((uchar) (A)[1])) << 8) +\ +									(((uint32) ((uchar) (A)[2])) << 16) +\ +									(((uint32) ((uchar) (A)[3])) << 24)) +\ +									(((ulonglong) (((uint32) ((uchar) (A)[4])) +\ +									(((uint32) ((uchar) (A)[5])) << 8) +\ +									(((uint32) ((uchar) (A)[6])) << 16) +\ +									(((uint32) ((uchar) (A)[7])) << 24))) << 32)) + + +#define int2store(T,A)  do { uint def_temp= (uint) (A) ;\ +                  *((uchar*) (T))  =  (uchar)(def_temp); \ +                  *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0) +#define int3store(T,A)  do { /*lint -save -e734 */\ +                  *(((char *)(T)))   = (char) ((A));\ +                  *(((char *)(T))+1) = (char) (((A) >> 8));\ +                  *(((char *)(T))+2) = (char) (((A) >> 16)); \ +                  /*lint -restore */} while (0) +#define int4store(T,A)  do { \ +                  *(((char *)(T)))   = (char) ((A));\ +                  *(((char *)(T))+1) = (char) (((A) >> 8));\ +                  *(((char *)(T))+2) = (char) (((A) >> 16));\ +                  *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0) +#define int5store(T,A)  do { \ +                  *(((char *)(T)))   = (char)((A));\ +                  *(((char *)(T))+1) = (char)(((A) >> 8));\ +                  *(((char *)(T))+2) = (char)(((A) >> 16));\ +                  *(((char *)(T))+3) = (char)(((A) >> 24)); \ +                  *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0) +/* Based on int5store() from Andrey Hristov */ +#define int6store(T,A)  do { \ +                  *(((char *)(T)))   = (char)((A));\ +                  *(((char *)(T))+1) = (char)(((A) >> 8));\ +                  *(((char *)(T))+2) = (char)(((A) >> 16));\ +                  *(((char *)(T))+3) = (char)(((A) >> 24)); \ +                  *(((char *)(T))+4) = (char)(((A) >> 32)); \ +                  *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0) +#define int8store(T,A)        { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \ +                  int4store((T),def_temp); \ +                  int4store((T+4),def_temp2); \ +                } +#ifdef WORDS_BIGENDIAN +#define float4store(T,A) do { \ +                          *(((char *)(T)))   = (char) ((char *) &A)[3];\ +                          *(((char *)(T))+1) = (char) ((char *) &A)[2];\ +                          *(((char *)(T))+2) = (char) ((char *) &A)[1];\ +                          *(((char *)(T))+3) = (char) ((char *) &A)[0]; } while (0) + +#define float4get(V,M)   do { float def_temp;\ +                          ((char*) &def_temp)[0] = (M)[3];\ +                          ((char*) &def_temp)[1] = (M)[2];\ +                          ((char*) &def_temp)[2] = (M)[1];\ +                          ((char*) &def_temp)[3] = (M)[0];\ +                          (V)=def_temp; } while (0) +#define float8store(T,V)  do { \ +                           *(((char *)(T)))   = (char) ((char *) &(V))[7];\ +                           *(((char *)(T))+1) = (char) ((char *) &(V))[6];\ +                           *(((char *)(T))+2) = (char) ((char *) &(V))[5];\ +                           *(((char *)(T))+3) = (char) ((char *) &(V))[4];\ +                           *(((char *)(T))+4) = (char) ((char *) &(V))[3];\ +                           *(((char *)(T))+5) = (char) ((char *) &(V))[2];\ +                           *(((char *)(T))+6) = (char) ((char *) &(V))[1];\ +                           *(((char *)(T))+7) = (char) ((char *) &(V))[0]; } while (0) + +#define float8get(V,M)   do { double def_temp;\ +                          ((char*) &def_temp)[0] = (M)[7];\ +                          ((char*) &def_temp)[1] = (M)[6];\ +                          ((char*) &def_temp)[2] = (M)[5];\ +                          ((char*) &def_temp)[3] = (M)[4];\ +                          ((char*) &def_temp)[4] = (M)[3];\ +                          ((char*) &def_temp)[5] = (M)[2];\ +                          ((char*) &def_temp)[6] = (M)[1];\ +                          ((char*) &def_temp)[7] = (M)[0];\ +                          (V) = def_temp; \ +                         } while (0) +#else +#define float4get(V,M)   memcpy((char*) &(V),(char*) (M),sizeof(float)) +#define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float)) + +#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) +#define doublestore(T,V)  do { \ +                         *(((char *)(T)))= ((char *) &(V))[4];\ +                         *(((char *)(T))+1)=(char) ((char *) &(V))[5];\ +                         *(((char *)(T))+2)=(char) ((char *) &(V))[6];\ +                         *(((char *)(T))+3)=(char) ((char *) &(V))[7];\ +                         *(((char *)(T))+4)=(char) ((char *) &(V))[0];\ +                         *(((char *)(T))+5)=(char) ((char *) &(V))[1];\ +                         *(((char *)(T))+6)=(char) ((char *) &(V))[2];\ +                         *(((char *)(T))+7)=(char) ((char *) &(V))[3];} while (0) +#define doubleget(V,M) do { double def_temp;\ +                         ((char*) &def_temp)[0]=(M)[4];\ +                         ((char*) &def_temp)[1]=(M)[5];\ +                         ((char*) &def_temp)[2]=(M)[6];\ +                         ((char*) &def_temp)[3]=(M)[7];\ +                         ((char*) &def_temp)[4]=(M)[0];\ +                         ((char*) &def_temp)[5]=(M)[1];\ +                         ((char*) &def_temp)[6]=(M)[2];\ +                         ((char*) &def_temp)[7]=(M)[3];\ +                         (V) = def_temp; } while (0) +#endif /* __FLOAT_WORD_ORDER */ + +#define float8get(V,M)   doubleget((V),(M)) +#define float8store(V,M) doublestore((V),(M)) +#endif /* WORDS_BIGENDIAN */ + +#endif /* sint2korr */ + +/* Define-funktions for reading and storing in machine format from/to +   short/long to/from some place in memory V should be a (not +   register) variable, M is a pointer to byte */ + +#ifdef WORDS_BIGENDIAN + +#define ushortget(V,M)  { V = (uint16) (((uint16) ((uchar) (M)[1]))+\ +                                        ((uint16) ((uint16) (M)[0]) << 8)); } +#define shortget(V,M)   { V = (short) (((short) ((uchar) (M)[1]))+\ +                                       ((short) ((short) (M)[0]) << 8)); } +#define longget(V,M)    do { int32 def_temp;\ +              ((char*) &def_temp)[0]=(M)[0];\ +              ((char*) &def_temp)[1]=(M)[1];\ +              ((char*) &def_temp)[2]=(M)[2];\ +              ((char*) &def_temp)[3]=(M)[3];\ +              (V)=def_temp; } while (0) +#define ulongget(V,M)    do { uint32 def_temp;\ +              ((char*) &def_temp)[0]=(M)[0];\ +              ((char*) &def_temp)[1]=(M)[1];\ +              ((char*) &def_temp)[2]=(M)[2];\ +              ((char*) &def_temp)[3]=(M)[3];\ +              (V)=def_temp; }  while (0) +#define shortstore(T,A) do { \ +              uint def_temp=(uint) (A) ;\ +              *(((char *)(T))+1)=(char)(def_temp); \ +              *(((char *)(T))+0)=(char)(def_temp >> 8); } while (0) +#define longstore(T,A)  do { \ +              *(((char *)(T))+3)=(char)((A));\ +              *(((char *)(T))+2)=(char)(((A) >> 8));\ +              *(((char *)(T))+1)=(char)(((A) >> 16));\ +              *(((char *)(T))+0)=(char)(((A) >> 24)); }  while (0) + +#define doubleget(V,M)		memcpy((char*) &(V),(char*)  (M), sizeof(double)) +#define doublestore(T,V)	memcpy((char*)  (T),(char*) &(V), sizeof(double)) +#define longlongget(V,M)	memcpy((char*) &(V),(char*)  (M), sizeof(ulonglong)) +#define longlongstore(T,V)	memcpy((char*)  (T),(char*) &(V), sizeof(ulonglong)) + +#else + +#define ushortget(V,M)  { V = uint2korr((M)); } +#define shortget(V,M)   { V = sint2korr((M)); } +#define longget(V,M)    { V = sint4korr((M)); } +#define ulongget(V,M)   { V = uint4korr((M)); } +#define shortstore(T,V)   int2store((T),(V)) +#define longstore(T,V)    int4store((T),(V)) +#ifndef doubleget +#define doubleget(V,M)    memcpy((char*) &(V),(char*) (M),sizeof(double)) +#define doublestore(T,V)  memcpy((char*) (T),(char*) &(V),sizeof(double)) +#endif /* doubleget */ +#define longlongget(V,M)   memcpy((char*) &(V),(char*) (M),sizeof(ulonglong)) +#define longlongstore(T,V) memcpy((char*) (T),(char*) &(V),sizeof(ulonglong)) + +#endif /* WORDS_BIGENDIAN */ + + +#ifdef PHP_WIN32 +#define MYSQLND_LLU_SPEC "%I64u" +#define MYSQLND_LL_SPEC "%I64d" +#ifndef L64 +#define L64(x) x##i64 +#endif +typedef unsigned __int64 my_uint64; +typedef __int64 my_int64; +typedef unsigned __int64 mynd_ulonglong; +typedef __int64 mynd_longlong; +#else +#define MYSQLND_LLU_SPEC "%llu" +#define MYSQLND_LL_SPEC "%lld" +#ifndef L64 +#define L64(x) x##LL +#endif +#ifndef HAVE_UINT64_T +typedef unsigned long long my_uint64; +typedef unsigned long long mynd_ulonglong; +#else +typedef uint64_t my_uint64; +typedef uint64_t mynd_ulonglong; +#endif +#ifndef HAVE_INT64_T +typedef long long my_int64; +typedef long long mynd_longlong; +#else +typedef int64_t my_int64; +typedef int64_t mynd_longlong; +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h new file mode 100644 index 0000000000..83bc0665cb --- /dev/null +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -0,0 +1,191 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_PRIV_H +#define MYSQLND_PRIV_H + +#ifdef ZTS +#include "TSRM.h" +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef pestrndup +#define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length))) +#endif + + +#define MYSQLND_CLASS_METHODS_START(class) static \ +									 struct st_##class##_methods mysqlnd_##class##_methods = { +#define MYSQLND_CLASS_METHODS_END  				}							  +#define MYSQLND_METHOD(class, method) 			php_##class##_##method##_pub +#define MYSQLND_METHOD_PRIVATE(class, method)	php_##class##_##method##_priv + +#if PHP_MAJOR_VERSION < 6 +#define mysqlnd_array_init(arg, field_count) \ +{ \ +	ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\ +	zend_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0); \ +	Z_TYPE_P(arg) = IS_ARRAY;\ +} +#else +#define mysqlnd_array_init(arg, field_count) \ +{ \ +	ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\ +	zend_u_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0, 0);\ +	Z_TYPE_P(arg) = IS_ARRAY;\ +} +#endif + + + +#define SERVER_STATUS_IN_TRANS				1	/* Transaction has started */ +#define SERVER_STATUS_AUTOCOMMIT			2	/* Server in auto_commit mode */ +#define SERVER_MORE_RESULTS_EXISTS			8	/* Multi query - next query exists */ +/* +  The server was able to fulfill the clients request and opened a +  read-only non-scrollable cursor for a query. This flag comes +  in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. +*/ +#define SERVER_STATUS_CURSOR_EXISTS			64 +/* +  This flag is sent when a read-only cursor is exhausted, in reply to +  COM_STMT_FETCH command. +*/ +#define SERVER_STATUS_LAST_ROW_SENT			128 +#define SERVER_STATUS_DB_DROPPED			256 /* A database was dropped */ +#define SERVER_STATUS_NO_BACKSLASH_ESCAPES	512 + + + +/* Client Error codes */ +#define CR_UNKNOWN_ERROR		2000 +#define CR_CONNECTION_ERROR		2002 +#define CR_SERVER_GONE_ERROR	2006 +#define CR_OUT_OF_MEMORY		2008 +#define CR_SERVER_LOST			2013 +#define CR_COMMANDS_OUT_OF_SYNC	2014 +#define CR_CANT_FIND_CHARSET	2019 +#define CR_MALFORMED_PACKET		2027 +#define CR_NOT_IMPLEMENTED		2054 +#define CR_NO_PREPARE_STMT		2030 +#define CR_PARAMS_NOT_BOUND		2031 +#define CR_INVALID_PARAMETER_NO	2034 +#define CR_INVALID_BUFFER_USE	2035 + +#define MYSQLND_EE_FILENOTFOUND	 7890 + +#define UNKNOWN_SQLSTATE		"HY000" + +#define MAX_CHARSET_LEN			32 + + +#define SET_ERROR_AFF_ROWS(s)	(s)->upsert_status.affected_rows = (mynd_ulonglong) ~0 + +/* Error handling */ +#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \ +	{\ +		if ((buf)) { \ +			pefree((buf), (persistent)); \ +		} \ +		(buf) = (message); \ +		(buf_len) = (len); \ +		/* Transfer ownership*/ \ +		(message) = NULL; \ +	} + +#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \ +	{\ +		if ((buf)) { \ +			pefree((buf), (persistent)); \ +			(buf) = NULL; \ +		} \ +		(buf_len) = 0; \ +	} + + +#define SET_EMPTY_ERROR(error_info) \ +	{ \ +		error_info.error_no = 0; \ +		error_info.error[0] = '\0'; \ +		strncpy(error_info.sqlstate, "00000", sizeof("00000") - 1); \ +	} + +#define SET_CLIENT_ERROR(error_info, a, b, c) \ +	{ \ +		error_info.error_no = a; \ +		strncpy(error_info.sqlstate, b, sizeof(error_info.sqlstate)); \ +		strncpy(error_info.error, c, sizeof(error_info.error)); \ +	} + +#define SET_STMT_ERROR(stmt, a, b, c)	SET_CLIENT_ERROR(stmt->error_info, a, b, c) + + + +/* PS stuff */ +typedef void (*ps_field_fetch_func)(zval *zv, const MYSQLND_FIELD * const field, +									uint pack_len, zend_uchar **row, +									zend_bool everything_as_unicode TSRMLS_DC); +struct st_mysqlnd_perm_bind { +	ps_field_fetch_func func; +	/* should be signed int */ +	int					pack_len; +	unsigned int		php_type; +	zend_bool			is_possibly_blob; +	zend_bool			can_ret_as_str_in_uni; +}; + +extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; + +extern const char * mysqlnd_out_of_sync; +extern const char * mysqlnd_server_gone; + + +enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC); + + +void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */ + +void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, +								uint pack_len, zend_uchar **row, zend_bool as_unicode, +								unsigned int byte_count TSRMLS_DC); + + + +int mysqlnd_set_sock_no_delay(php_stream *stream); +#endif	/* MYSQLND_PRIV_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c new file mode 100644 index 0000000000..00dc46761f --- /dev/null +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -0,0 +1,1740 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_result.h" +#include "mysqlnd_result_meta.h" +#include "mysqlnd_statistics.h" +#include "mysqlnd_debug.h" + + +#define MYSQLND_SILENT + + +const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types"; +const char * const mysqlnd_stmt_not_prepared = "Statement not prepared"; + +/* Exported by mysqlnd.c */ +enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, +										const char * const arg, size_t arg_len, +										enum php_mysql_packet_type ok_packet, +										zend_bool silent TSRMLS_DC); + +/* Exported by mysqlnd_ps_codec.c */ +zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, +												  zend_bool *free_buffer TSRMLS_DC); + + +MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC); + +enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, +												unsigned int flags, +												zend_bool *fetched_anything TSRMLS_DC); + +enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, +											   unsigned int flags, +											   zend_bool *fetched_anything TSRMLS_DC); + +void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); + + +/* {{{ mysqlnd_stmt::store_result */ +static MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	enum_func_status ret; +	MYSQLND *conn = stmt->conn; +	MYSQLND_RES *result; +	zend_bool to_cache = FALSE; + +	DBG_ENTER("mysqlnd_stmt::store_result"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	/* be compliant with libmysql - NULL will turn */ +	if (!stmt->field_count) { +		DBG_RETURN(NULL); +	} + +	if (stmt->cursor_exists) { +		/* Silently convert buffered to unbuffered, for now */ +		MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); +		DBG_RETURN(res); +	} + +	/* Nothing to store for UPSERT/LOAD DATA*/ +	if (conn->state != CONN_FETCHING_DATA || +		stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) +	{ +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, +						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_RETURN(NULL); +	} + +	stmt->default_rset_handler = stmt->m->store_result; + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PS_BUFFERED_SETS); + +	result = stmt->result; +	result->type			= MYSQLND_RES_PS_BUF; +	result->m.fetch_row		= mysqlnd_fetch_stmt_row_buffered; +	result->m.fetch_lengths	= NULL;/* makes no sense */ +	result->zval_cache		= mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + +	/* Create room for 'next_extend' rows */ + +	/* Not set for SHOW statements at PREPARE stage */ +	if (result->conn) { +		result->conn->m->free_reference(result->conn TSRMLS_CC); +		result->conn = NULL;	/* store result does not reference  the connection */ +	} + +	ret = mysqlnd_store_result_fetch_data(conn, result, result->meta, +										  TRUE, stmt->update_max_length, +										  to_cache TSRMLS_CC); + +	if (PASS == ret) { +		/* libmysql API docs say it should be so for SELECT statements */ +		stmt->upsert_status.affected_rows = stmt->result->data->row_count; + +		stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; +	} else { +		conn->error_info = result->data->error_info; +		stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); +		mnd_efree(stmt->result); +		stmt->result = NULL; +		stmt->state = MYSQLND_STMT_PREPARED; +	} + +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::get_result */ +static MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	MYSQLND *conn = stmt->conn; +	MYSQLND_RES *result; + +	DBG_ENTER("mysqlnd_stmt::get_result"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	/* be compliant with libmysql - NULL will turn */ +	if (!stmt->field_count) { +		DBG_RETURN(NULL); +	} + +	if (stmt->cursor_exists) { +		/* Silently convert buffered to unbuffered, for now */ +		MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); +		DBG_RETURN(res); +	} + +	/* Nothing to store for UPSERT/LOAD DATA*/ +	if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) { +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, +						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_RETURN(NULL); +	} + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); +	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); + +	result = mysqlnd_result_init(stmt->result->field_count, +								 mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC);	 + +	result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + +	/* Not set for SHOW statements at PREPARE stage */ +	if (stmt->result->conn) { +		stmt->result->conn->m->free_reference(stmt->result->conn TSRMLS_CC); +		stmt->result->conn = NULL;	/* store result does not reference  the connection */ +	} + +	if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { +		stmt->upsert_status.affected_rows = result->data->row_count;	 +		stmt->state = MYSQLND_STMT_PREPARED; +		result->type = MYSQLND_RES_PS_BUF; +	} else { +		stmt->error_info = conn->error_info; +		stmt->state = MYSQLND_STMT_PREPARED; +	} + +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_skip_metadata */ +static enum_func_status +mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) +{ +	/* Follows parameter metadata, we have just to skip it, as libmysql does */ +	unsigned int i = 0; +	php_mysql_packet_res_field field_packet; + +	DBG_ENTER("mysqlnd_stmt_skip_metadata"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); +	field_packet.skip_parsing = TRUE; +	for (;i < stmt->param_count; i++) { +		if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) { +			PACKET_FREE_ALLOCA(field_packet); +			DBG_RETURN(FAIL); +		} +	} +	PACKET_FREE_ALLOCA(field_packet); + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_read_prepare_response */ +static enum_func_status +mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) +{ +	php_mysql_packet_prepare_response prepare_resp; + +	DBG_ENTER("mysqlnd_stmt_read_prepare_response"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET); +	if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) { +		PACKET_FREE_ALLOCA(prepare_resp); +		return FAIL; +	} + +	if (0xFF == prepare_resp.error_code) { +		stmt->error_info = stmt->conn->error_info = prepare_resp.error_info; +		return FAIL; +	} + +	stmt->stmt_id = prepare_resp.stmt_id; +	stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp.warning_count; +	stmt->field_count = stmt->conn->field_count = prepare_resp.field_count; +	stmt->param_count = prepare_resp.param_count; +	PACKET_FREE_ALLOCA(prepare_resp); + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_prepare_read_eof */ +static enum_func_status +mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) +{ +	php_mysql_packet_eof fields_eof; +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_stmt_prepare_read_eof"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); +	if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, stmt->conn))) { +		if (stmt->result) { +			stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); +			mnd_efree(stmt->result); +			memset(stmt, 0, sizeof(MYSQLND_STMT)); +			stmt->state = MYSQLND_STMT_INITTED; +		} +	} else { +		stmt->upsert_status.server_status = fields_eof.server_status; +		stmt->upsert_status.warning_count = fields_eof.warning_count; +		stmt->state = MYSQLND_STMT_PREPARED; +	} +	PACKET_FREE_ALLOCA(fields_eof); + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::prepare */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * const query, +									  unsigned int query_len TSRMLS_DC) +{ +	MYSQLND_STMT *stmt_to_prepare = stmt; + +	DBG_ENTER("mysqlnd_stmt::prepare"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	SET_ERROR_AFF_ROWS(stmt); +	SET_ERROR_AFF_ROWS(stmt->conn); + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	if (stmt->state > MYSQLND_STMT_INITTED) { +		/* See if we have to clean the wire */ +		if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { +			/* Do implicit use_result and then flush the result */ +			stmt->default_rset_handler = stmt->m->use_result; +			stmt->default_rset_handler(stmt TSRMLS_CC); +		} +		/* No 'else' here please :) */ +		if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { +			stmt->result->m.skip_result(stmt->result TSRMLS_CC); +		} +		/* +		  Create a new test statement, which we will prepare, but if anything +		  fails, we will scrap it. +		*/ +		stmt_to_prepare = mysqlnd_stmt_init(stmt->conn); +	} + +	if (FAIL == mysqlnd_simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, +									   query_len, PROT_LAST, FALSE TSRMLS_CC) || +		FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) { +		goto fail; +	} + +	if (stmt_to_prepare->param_count) { +		if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) || +			FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) +		{ +			goto fail; +		} +	} + +	/* +	  Read metadata only if there is actual result set. +	  Beware that SHOW statements bypass the PS framework and thus they send +	  no metadata at prepare. +	*/ +	if (stmt_to_prepare->field_count) { +		MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count, NULL TSRMLS_CC); +		/* Allocate the result now as it is needed for the reading of metadata */ +		stmt_to_prepare->result = result;  + +		result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn); + +		result->type = MYSQLND_RES_PS_BUF; + +		if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) || +			FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) { +			goto fail; +		} +	} + +	if (stmt_to_prepare != stmt) { +		/* Free old buffers, binding and resources on server */ +		stmt->m->close(stmt, TRUE TSRMLS_CC); + +		memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); + +		/* Now we will have a clean new statement object */ +		mnd_efree(stmt_to_prepare); +	} +	stmt->state = MYSQLND_STMT_PREPARED; +	DBG_INF("PASS"); +	DBG_RETURN(PASS); + +fail: +	if (stmt_to_prepare != stmt) { +		stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC); +	} +	stmt->state = MYSQLND_STMT_INITTED; + +	DBG_INF("FAIL"); +	DBG_RETURN(FAIL); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::execute */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	enum_func_status ret; +	MYSQLND		*conn = stmt->conn; +	zend_uchar	*request; +	size_t		request_len; +	zend_bool	free_request; + +	DBG_ENTER("mysqlnd_stmt::execute"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	SET_ERROR_AFF_ROWS(stmt); +	SET_ERROR_AFF_ROWS(stmt->conn); +	 +	if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) { +		if (stmt->result_bind && +			stmt->result_zvals_separated_once == TRUE &&  +			stmt->state >= MYSQLND_STMT_USER_FETCHING) +		{ +			/* +			  We need to copy the data from the buffers which we will clean. +			  The bound variables point to them only if the user has started +			  to fetch data (MYSQLND_STMT_USER_FETCHING). +			  We need to check 'result_zvals_separated_once' or we will leak +			  in the following scenario +			  prepare("select 1 from dual"); +			  execute(); +			  fetch(); <-- no binding, but that's not a problem +			  bind_result(); +			  execute(); <-- here we will leak because we separate without need +			*/ +			unsigned int i; +			for (i = 0; i < stmt->field_count; i++) { +				if (stmt->result_bind[i].bound == TRUE) { +					zval_copy_ctor(stmt->result_bind[i].zv); +				} +			} +		} + +		/* +		  Executed, but the user hasn't started to fetch +		  This will clean also the metadata, but after the EXECUTE call we will +		  have it again. +		*/ +		stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); +	} else if (stmt->state < MYSQLND_STMT_PREPARED) { +		/* Only initted - error */ +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +						 mysqlnd_out_of_sync); +		SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_INF("FAIL"); +		DBG_RETURN(FAIL); +	} + +	if (stmt->param_count && !stmt->param_bind) { +		SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, +						 "No data supplied for parameters in prepared statement"); +		DBG_INF("FAIL"); +		DBG_RETURN(FAIL); +	} + +	request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request TSRMLS_CC); +	 +	/* support for buffer types should be added here ! */ + +	ret = mysqlnd_simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, +								 PROT_LAST /* we will handle the response packet*/, +								 FALSE TSRMLS_CC); + +	if (free_request) { +		mnd_efree(request); +	} + +	if (ret == FAIL) { +		stmt->error_info = conn->error_info; +		DBG_INF("FAIL"); +		DBG_RETURN(FAIL); +	} +	stmt->execute_count++; + +	ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); +	if (ret == FAIL) { +		stmt->error_info = conn->error_info; +		stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; +		if (conn->state == CONN_QUIT_SENT) { +			/* close the statement here, the connection has been closed */ +		} +	} else { +		SET_EMPTY_ERROR(stmt->error_info); +		SET_EMPTY_ERROR(stmt->conn->error_info); +		stmt->send_types_to_server = 0; +		stmt->upsert_status = conn->upsert_status; +		stmt->state = MYSQLND_STMT_EXECUTED; +		if (conn->last_query_type == QUERY_UPSERT) { +			stmt->upsert_status = conn->upsert_status; +			DBG_INF("PASS"); +			DBG_RETURN(PASS); +		} else if (conn->last_query_type == QUERY_LOAD_LOCAL) { +			DBG_INF("PASS"); +			DBG_RETURN(PASS); +		} + +		stmt->result->type = MYSQLND_RES_PS_BUF; +		if (!stmt->result->conn) { +			/* +			  For SHOW we don't create (bypasses PS in server) +			  a result set at prepare and thus a connection was missing +			*/ +			stmt->result->conn = stmt->conn->m->get_reference(stmt->conn); +		} + +		/* Update stmt->field_count as SHOW sets it to 0 at prepare */ +		stmt->field_count = stmt->result->field_count = conn->field_count; +		stmt->result->lengths = NULL; +		if (stmt->field_count) { +			stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE; +			/* +			  We need to set this because the user might not call +			  use_result() or store_result() and we should be able to scrap the +			  data on the line, if he just decides to close the statement. +			*/ +			DBG_INF_FMT("server_status=%d cursor=%d\n", stmt->upsert_status.server_status, +						stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); + +			if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) { +				stmt->cursor_exists = TRUE; +				conn->state = CONN_READY; +				/* Only cursor read */ +				stmt->default_rset_handler = stmt->m->use_result; +				DBG_INF("use_result"); +			} else if (stmt->flags & CURSOR_TYPE_READ_ONLY) { +				/* +				  We have asked for CURSOR but got no cursor, because the condition +				  above is not fulfilled. Then... + +				  This is a single-row result set, a result set with no rows, EXPLAIN, +				  SHOW VARIABLES, or some other command which either a) bypasses the +				  cursors framework in the server and writes rows directly to the +				  network or b) is more efficient if all (few) result set rows are +				  precached on client and server's resources are freed. +				*/ +				/* preferred is buffered read */ +				stmt->default_rset_handler = stmt->m->store_result; +				DBG_INF("store_result"); +			} else { +				/* preferred is unbuffered read */ +				stmt->default_rset_handler = stmt->m->use_result; +				DBG_INF("use_result"); +			} +		} +	} + +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_fetch_stmt_row_buffered */ +enum_func_status +mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, +								zend_bool *fetched_anything TSRMLS_DC) +{ +	unsigned int i; +	MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + +	DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	/* If we haven't read everything */ +	if (result->data->data_cursor && +		(result->data->data_cursor - result->data->data) < result->data->row_count) +	{ +		/* The user could have skipped binding - don't crash*/ +		if (stmt->result_bind) { +			zval **current_row = *result->data->data_cursor; +			for (i = 0; i < result->field_count; i++) { +				/* Clean what we copied last time */ +#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +				zval_dtor(stmt->result_bind[i].zv); +#endif +				/* copy the type */ +				if (stmt->result_bind[i].bound == TRUE) { +					DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(current_row[i])); +					if (Z_TYPE_P(current_row[i]) != IS_NULL) { +						/* +						  Copy the value. +						  Pre-condition is that the zvals in the result_bind buffer +						  have been  ZVAL_NULL()-ed or to another simple type +						  (int, double, bool but not string). Because of the reference +						  counting the user can't delete the strings the variables point to. +						*/ + +						Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]); +						stmt->result_bind[i].zv->value = current_row[i]->value; +#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +						zval_copy_ctor(stmt->result_bind[i].zv); +#endif +					} else { +						ZVAL_NULL(stmt->result_bind[i].zv); +					} +				} +			} +		} +		result->data->data_cursor++; +		*fetched_anything = TRUE; +		/* buffered result sets don't have a connection */ +		MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF); +		DBG_INF("row fetched"); +	} else { +		result->data->data_cursor = NULL; +		*fetched_anything = FALSE; +		DBG_INF("no more data"); +	} +	DBG_INF("PASS"); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_fetch_row_unbuffered */ +static enum_func_status +mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, +								zend_bool *fetched_anything TSRMLS_DC) +{ +	enum_func_status ret; +	MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; +	unsigned int i, field_count = result->field_count; +	php_mysql_packet_row *row_packet = result->row_packet; + +	DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); + +	if (result->unbuf->eof_reached) { +		/* No more rows obviously */ +		*fetched_anything = FALSE; +		DBG_INF("eof reached"); +		DBG_RETURN(PASS); +	} +	if (result->conn->state != CONN_FETCHING_DATA) { +		SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, +						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_ERR("command out of sync"); +		DBG_RETURN(FAIL); +	} +	/* Let the row packet fill our buffer and skip additional malloc + memcpy */ +	row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE; + +	/* +	  If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to +	  mysqlnd_unbuffered_free_last_data() before it. The function returns always true. +	*/ +	if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { +		result->unbuf->row_count++; +		*fetched_anything = TRUE; + +		if (!row_packet->skip_extraction) { +			mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); + +			DBG_INF("extracting data"); +			result->unbuf->last_row_data = row_packet->fields; +			result->unbuf->last_row_buffer = row_packet->row_buffer; +			row_packet->fields = NULL; +			row_packet->row_buffer = NULL; + +			for (i = 0; i < field_count; i++) { +				if (stmt->result_bind[i].bound == TRUE) { +					zval *data = result->unbuf->last_row_data[i]; +					/* +					  stmt->result_bind[i].zv has been already destructed +					  in mysqlnd_unbuffered_free_last_data() +					*/ + +#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +					zval_dtor(stmt->result_bind[i].zv); +#endif +					if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { +						stmt->result_bind[i].zv->value = data->value; +#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +						zval_copy_ctor(stmt->result_bind[i].zv); +#endif						 +						if ( +							(Z_TYPE_P(data) == IS_STRING +#if PHP_MAJOR_VERSION >= 6 +							|| Z_TYPE_P(data) == IS_UNICODE +#endif +							) +							 && (result->meta->fields[i].max_length < Z_STRLEN_P(data))) +						{ +							result->meta->fields[i].max_length = Z_STRLEN_P(data); +						} +					} +				} +			} +			MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF); +		} else { +			DBG_INF("skipping extraction"); +			/* +			  Data has been allocated and usually mysqlnd_unbuffered_free_last_data() +			  frees it but we can't call this function as it will cause problems with +			  the bound variables. Thus we need to do part of what it does or Zend will +			  report leaks. +			*/ +			mnd_efree(row_packet->row_buffer); +			row_packet->row_buffer = NULL; +		} +	} else if (ret == FAIL) { +		if (row_packet->error_info.error_no) { +			stmt->conn->error_info = row_packet->error_info;  +			stmt->error_info = row_packet->error_info;  +		} +		*fetched_anything = FALSE; +		result->conn->state = CONN_READY; +		result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */	 +	} else if (row_packet->eof) { +		DBG_INF("EOF"); +		/* Mark the connection as usable again */ +		result->unbuf->eof_reached = TRUE; +		result->conn->upsert_status.warning_count = row_packet->warning_count; +		result->conn->upsert_status.server_status = row_packet->server_status; +		/* +		  result->row_packet will be cleaned when +		  destroying the result object +		*/ +		if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { +			result->conn->state = CONN_NEXT_RESULT_PENDING; +		} else { +			result->conn->state = CONN_READY; +		} +		*fetched_anything = FALSE; +	} + +	DBG_INF_FMT("ret=%s fetched_anything=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::use_result */ +static MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) +{ +	MYSQLND_RES *result; +	MYSQLND *conn = stmt->conn; + +	DBG_ENTER("mysqlnd_stmt::use_result"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	if (!stmt->field_count || +		(!stmt->cursor_exists && conn->state != CONN_FETCHING_DATA) || +		(stmt->cursor_exists && conn->state != CONN_READY) || +		(stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)) +	{ +		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, +						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_ERR("command out of sync"); +		DBG_RETURN(NULL); +	} + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_PS_UNBUFFERED_SETS); + +	result					= stmt->result; +	result->type			= MYSQLND_RES_PS_UNBUF; +	result->m.fetch_row		= stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: +													mysqlnd_stmt_fetch_row_unbuffered; +	result->m.fetch_lengths	= NULL; /* makes no sense */ +	result->zval_cache		= mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + +	result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + +	DBG_INF_FMT("cursor=%d zval_cache=%p", stmt->cursor_exists, result->zval_cache); +	/* +	  Will be freed in the mysqlnd_internal_free_result_contents() called +	  by the resource destructor. mysqlnd_fetch_row_unbuffered() expects +	  this to be not NULL. +	*/ +	PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *); +	result->row_packet->field_count = result->field_count; +	result->row_packet->binary_protocol = TRUE; +	result->row_packet->fields_metadata = stmt->result->meta->fields; +	result->row_packet->bit_fields_count = result->meta->bit_fields_count; +	result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; +	result->lengths = NULL; + +	stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; + +	/* No multithreading issues as we don't share the connection :) */ + +	DBG_INF_FMT("%p", result); +	DBG_RETURN(result); +} +/* }}} */ + + +#define STMT_ID_LENGTH 4 + +/* {{{ mysqlnd_fetch_row_cursor */ +enum_func_status +mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, +							  zend_bool *fetched_anything TSRMLS_DC) +{ +	enum_func_status ret; +	MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; +	zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */]; +	php_mysql_packet_row *row_packet = result->row_packet; + +	DBG_ENTER("mysqlnd_fetch_stmt_row_cursor"); +	DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags); + +	if (!stmt) { +		DBG_ERR("no statement"); +		DBG_RETURN(FAIL); +	} + +	if (stmt->state < MYSQLND_STMT_USER_FETCHING) { +		/* Only initted - error */ +		SET_CLIENT_ERROR(stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +						mysqlnd_out_of_sync); +		DBG_ERR("command out of sync"); +		DBG_RETURN(FAIL); +	} + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	int4store(buf, stmt->stmt_id); +	int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */ + +	if (FAIL == mysqlnd_simple_command(stmt->conn, COM_STMT_FETCH, (char *)buf, sizeof(buf), +									   PROT_LAST /* we will handle the response packet*/, +									   FALSE TSRMLS_CC)) { +		stmt->error_info = stmt->conn->error_info; +		DBG_RETURN(FAIL); +	} + +	row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE; + +	if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { +		unsigned int i, field_count = result->field_count; +		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); + +		result->unbuf->last_row_data = row_packet->fields; +		result->unbuf->last_row_buffer = row_packet->row_buffer; +		row_packet->fields = NULL; +		row_packet->row_buffer = NULL; +		if (!row_packet->skip_extraction) { +			/* If no result bind, do nothing. We consumed the data */ +			for (i = 0; i < field_count; i++) { +				if (stmt->result_bind[i].bound == TRUE) { +					zval *data = result->unbuf->last_row_data[i]; +					/* +					  stmt->result_bind[i].zv has been already destructed +					  in mysqlnd_unbuffered_free_last_data() +					*/ +					DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(stmt->result_bind[i].zv)); +					if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { +						stmt->result_bind[i].zv->value = data->value; +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +						zval_copy_ctor(stmt->result_bind[i].zv); +#endif +						if ((Z_TYPE_P(data) == IS_STRING +#if PHP_MAJOR_VERSION >= 6 +							|| Z_TYPE_P(data) == IS_UNICODE +#endif +							) +							 && (result->meta->fields[i].max_length < Z_STRLEN_P(data))) +						{ +							result->meta->fields[i].max_length = Z_STRLEN_P(data); +						} +					} +				} +			} +		} +		result->unbuf->row_count++; +		*fetched_anything = TRUE; +		/* We asked for one row, the next one should be EOF, eat it */ +		ret = PACKET_READ(row_packet, result->conn); +		if (row_packet->row_buffer) { +			mnd_efree(row_packet->row_buffer); +			row_packet->row_buffer = NULL; +		} +		MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR); +	} else { +		*fetched_anything = FALSE; + +		stmt->upsert_status.warning_count = +			stmt->conn->upsert_status.warning_count = +				row_packet->warning_count; + +		stmt->upsert_status.server_status =  +			stmt->conn->upsert_status.server_status = +				row_packet->server_status; + +		result->unbuf->eof_reached = row_packet->eof; +	} +	stmt->upsert_status.warning_count = +		stmt->conn->upsert_status.warning_count = +			row_packet->warning_count; +	stmt->upsert_status.server_status =  +		stmt->conn->upsert_status.server_status = +			row_packet->server_status; + +	DBG_INF_FMT("ret=%s fetched=%d s_status=%d warns=%d eof=%d", +				ret == PASS? "PASS":"FAIL", *fetched_anything, +				row_packet->server_status, row_packet->warning_count, +				result->unbuf->eof_reached); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::fetch */ +PHPAPI enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, +									zend_bool * const fetched_anything TSRMLS_DC) +{ +	enum_func_status ret; +	DBG_ENTER("mysqlnd_stmt::fetch"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	if (!stmt->result || +		stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) { +		SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + +		DBG_ERR("command out of sync"); +		DBG_RETURN(FAIL); +	} else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { +		/* Execute only once. We have to free the previous contents of user's bound vars */ + +		stmt->default_rset_handler(stmt TSRMLS_CC); +	} +	stmt->state = MYSQLND_STMT_USER_FETCHING; + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	DBG_INF_FMT("result_bind=%p separated_once=%d", stmt->result_bind, stmt->result_zvals_separated_once); +	/* +	  The user might have not bound any variables for result. +	  Do the binding once she does it. +	*/ +	if (stmt->result_bind && !stmt->result_zvals_separated_once) { +		unsigned int i; +		/* +		  mysqlnd_stmt_store_result() has been called free the bind +		  variables to prevent leaking of their previous content. +		*/ +		for (i = 0; i < stmt->result->field_count; i++) { +			if (stmt->result_bind[i].bound == TRUE) { +				zval_dtor(stmt->result_bind[i].zv); +				ZVAL_NULL(stmt->result_bind[i].zv); +			} +		} +		stmt->result_zvals_separated_once = TRUE; +	} + +	ret = stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::reset */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	MYSQLND * conn = stmt->conn; +	zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; + +	DBG_ENTER("mysqlnd_stmt::reset"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	if (stmt->stmt_id) { +		if (stmt->param_bind) { +			unsigned int i; +			DBG_INF("resetting long data"); +			/* Reset Long Data */ +			for (i = 0; i < stmt->param_count; i++) { +				if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) { +					stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; +				} +			} +		} + +		/* +		  If the user decided to close the statement right after execute() +		  We have to call the appropriate use_result() or store_result() and +		  clean. +		*/ +		if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { +			DBG_INF("fetching result set header"); +			stmt->default_rset_handler(stmt TSRMLS_CC); +			stmt->state = MYSQLND_STMT_USER_FETCHING; +		} + +		if (stmt->result) { +			DBG_INF("skipping result"); +			stmt->result->m.skip_result(stmt->result TSRMLS_CC); +		} +		/* Now the line should be free, if it wasn't */ + +		int4store(cmd_buf, stmt->stmt_id); +		if (conn->state == CONN_READY && +			FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf, +												  sizeof(cmd_buf), PROT_OK_PACKET, +												  FALSE TSRMLS_CC))) { +			stmt->error_info = conn->error_info; +		} +		stmt->upsert_status = conn->upsert_status; + +		stmt->state = MYSQLND_STMT_PREPARED; +	} +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::send_long_data */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_no, +							 				 const char * const data, unsigned long length TSRMLS_DC) +{ +	enum_func_status ret = FAIL; +	MYSQLND * conn = stmt->conn; +	zend_uchar *cmd_buf; +	size_t packet_len; +	enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; +	 +	DBG_ENTER("mysqlnd_stmt::send_long_data"); +	DBG_INF_FMT("stmt=%lu param_no=%d data_len=%lu", stmt->stmt_id, param_no, length); + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	if (stmt->state < MYSQLND_STMT_PREPARED) { +		SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); +		DBG_ERR("not prepared"); +		DBG_RETURN(FAIL); +	} +	if (!stmt->param_bind) { +		SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); +		DBG_ERR("command out of sync"); +		DBG_RETURN(FAIL); +	} + +	if (param_no >= stmt->param_count) { +		SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number"); +		DBG_ERR("invalid param_no"); +		DBG_RETURN(FAIL); +	} +	if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) { +		SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob); +		DBG_ERR("param_no is not of a blob type"); +		DBG_RETURN(FAIL); +	} + +	/* +	  XXX:	Unfortunately we have to allocate additional buffer to be able the +			additional data, which is like a header inside the payload. +			This should be optimised, but it will be a pervasive change, so +			mysqlnd_simple_command() will accept not a buffer, but actually MYSQLND_STRING* +			terminated by NULL, to send. If the strings are not big, we can collapse them +			on the buffer every connection has, but otherwise we will just send them +			one by one to the wire. +	*/ + +	if (conn->state == CONN_READY) { +		stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; +		cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length); + +		int4store(cmd_buf, stmt->stmt_id); +		int2store(cmd_buf + STMT_ID_LENGTH, param_no); +		memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); + +		/* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ +		ret = mysqlnd_simple_command(conn, cmd, (char *)cmd_buf, packet_len, +									 PROT_LAST , FALSE TSRMLS_CC); +		mnd_efree(cmd_buf); +		if (FAIL == ret) { +			stmt->error_info = conn->error_info; +		} +		/* +		  Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not +		  sent response packets. According to documentation the only way to get an error +		  is to have out-of-memory on the server-side. However, that's not true, as if +		  max_allowed_packet_size is smaller than the chunk being sent to the server, the +		  latter will complain with an error message. However, normally we don't expect +		  an error message, thus we continue. When sending the next command, which expects +		  response we will read the unexpected data and error message will look weird. +		  Therefore we do non-blocking read to clean the line, if there is a need. +		  Nevertheless, there is a built-in protection when sending a command packet, that +		  checks if the line is clear - useful for debug purposes and to be switched off +		  in release builds. + +		  Maybe we can make it automatic by checking what's the value of +		  max_allowed_packet_size on the server and resending the data. +		*/ +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND +#if HAVE_USLEEP && !defined(PHP_WIN32) +		usleep(120000); +#endif +		if ((packet_len = php_mysqlnd_consume_uneaten_data(conn, cmd TSRMLS_CC))) { +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error " +							 "while sending long data. Probably max_allowed_packet_size " +							 "is smaller than the data. You have to increase it or send " +							 "smaller chunks of data. Answer was %u bytes long.", packet_len); +			SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, +							"Server responded to COM_STMT_SEND_LONG_DATA."); +			ret = FAIL; +		} +#endif +	} + +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_stmt_bind_param */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, +										 MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) +{ +	unsigned int i = 0; + +	DBG_ENTER("mysqlnd_stmt::bind_param"); +	DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); + +	if (stmt->state < MYSQLND_STMT_PREPARED) { +		SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); +		DBG_ERR("not prepared"); +		DBG_RETURN(FAIL); +	} + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	if (stmt->param_count) { +		if (!param_bind) { +			SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, +							 "Re-binding (still) not supported"); +			DBG_ERR("Re-binding (still) not supported"); +			DBG_RETURN(FAIL); +		} else if (stmt->param_bind) { +			DBG_INF("Binding"); +			/* +			  There is already result bound. +			  Forbid for now re-binding!! +			*/ +			for (i = 0; i < stmt->param_count; i++) { +				/* For BLOBS zv is NULL */ +				if (stmt->param_bind[i].zv) { +					/* +					  We may have the last reference, then call zval_ptr_dtor() +					  or we may leak memory. +					*/ +					zval_ptr_dtor(&stmt->param_bind[i].zv); +					stmt->param_bind[i].zv = NULL; +				} +			} +			mnd_efree(stmt->param_bind); +		} + +		stmt->param_bind = param_bind; +		for (i = 0; i < stmt->param_count; i++) { +			/* The client will use stmt_send_long_data */ +			DBG_INF_FMT("%d is of type %d", i, stmt->param_bind[i].type); +			if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) { +				/* Prevent from freeing */ +				ZVAL_ADDREF(stmt->param_bind[i].zv); +				/* Don't update is_ref, or we will leak during conversion */ +			} else { +				stmt->param_bind[i].zv = NULL; +				stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; +			} +		} +		stmt->send_types_to_server = 1; +	} +	DBG_INF("PASS"); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::bind_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, +										  MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC) +{ +	uint i = 0; + +	DBG_ENTER("mysqlnd_stmt::bind_result"); +	DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	if (stmt->state < MYSQLND_STMT_PREPARED) { +		SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); +		if (result_bind) { +			mnd_efree(result_bind); +		} +		DBG_ERR("not prepared"); +		DBG_RETURN(FAIL); +	} + +	if (stmt->field_count) { +		if (!result_bind) { +			DBG_ERR("no result bind passed"); +			DBG_RETURN(FAIL); +		} + +		mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + +		stmt->result_bind = result_bind; +		for (i = 0; i < stmt->field_count; i++) { +			/* Prevent from freeing */ +			ZVAL_ADDREF(stmt->result_bind[i].zv);		 +			/* +			  Don't update is_ref !!! it's not our job +			  Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt +			  will fail. +			*/ +			stmt->result_bind[i].bound = TRUE; +		} +	} else if (result_bind) { +		mnd_efree(result_bind); +	} +	DBG_INF("PASS"); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::insert_id */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt) +{ +	return stmt->upsert_status.last_insert_id; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::affected_rows */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const stmt) +{ +	return stmt->upsert_status.affected_rows; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::num_rows */ +static mynd_ulonglong +MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const stmt) +{ +	return stmt->result? mysqlnd_num_rows(stmt->result):0; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::warning_count */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const stmt) +{ +	return stmt->upsert_status.warning_count; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::field_count */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const stmt) +{ +	return stmt->field_count; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::param_count */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const stmt) +{ +	return stmt->param_count; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::errno */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const stmt) +{ +	return stmt->error_info.error_no; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::error */ +static const char * +MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const stmt) +{ +	return stmt->error_info.error; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::sqlstate */ +static const char * +MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt) +{ +	return stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::data_seek */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, mynd_ulonglong row TSRMLS_DC) +{ +	return stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::param_metadata */ +static MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt) +{ +	if (!stmt->param_count) { +		return NULL; +	} + +	return NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::result_metadata */ +static MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	MYSQLND_RES *result; + +	DBG_ENTER("mysqlnd_stmt::result_metadata"); +	DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count); + +	if (!stmt->field_count || !stmt->conn || !stmt->result || +		!stmt->result->meta) +	{ +		DBG_INF("NULL"); +		DBG_RETURN(NULL); +	} + +	/* +	  TODO: This implementation is kind of a hack, +			find a better way to do it. In different functions I have put +			fuses to check for result->m.fetch_row() being NULL. This should +			be handled in a better way. + +	  In the meantime we don't need a zval cache reference for this fake +	  result set, so we don't get one. +	*/ +	result = mysqlnd_result_init(stmt->field_count, NULL TSRMLS_CC); +	result->type = MYSQLND_RES_NORMAL; +	result->m.fetch_row = result->m.fetch_row_normal_unbuffered; +	result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); +	result->unbuf->eof_reached = TRUE; +	result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + +	DBG_INF_FMT("result=%p", result); +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::attr_set */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, +									   enum mysqlnd_stmt_attr attr_type, +									   const void * const value TSRMLS_DC) +{ +	unsigned long val = *(unsigned long *) value; +	DBG_ENTER("mysqlnd_stmt::attr_set"); +	DBG_INF_FMT("stmt=%lu attr_type=%u value=%lu", stmt->stmt_id, attr_type, val); + +	switch (attr_type) { +		case STMT_ATTR_UPDATE_MAX_LENGTH: +			/* +			  XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack +			  and mysqlnd won't be used out of the scope of PHP -> use ulong. +			*/ +			stmt->update_max_length = val? TRUE:FALSE; +			break; +		case STMT_ATTR_CURSOR_TYPE: { +			if (val > (unsigned long) CURSOR_TYPE_READ_ONLY) { +				SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); +				return FAIL; +			} +			stmt->flags = val; +			break; +		} +		case STMT_ATTR_PREFETCH_ROWS: { +			if (val == 0) { +				val = MYSQLND_DEFAULT_PREFETCH_ROWS; +			} else if (val > 1) { +				SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); +				return FAIL; +			} +			stmt->prefetch_rows = val; +			break; +		} +		default: +			SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); +			DBG_RETURN(FAIL); +	} +	DBG_INF("PASS"); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ _mysqlnd_stmt_attr_get */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, +									   enum mysqlnd_stmt_attr attr_type, +									   void * const value TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_stmt::attr_set"); +	DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type); + +	switch (attr_type) { +		case STMT_ATTR_UPDATE_MAX_LENGTH: +			*(zend_bool *) value= stmt->update_max_length; +			break; +		case STMT_ATTR_CURSOR_TYPE: +			*(unsigned long *) value= stmt->flags; +			break; +		case STMT_ATTR_PREFETCH_ROWS: +			*(unsigned long *) value= stmt->prefetch_rows; +			break; +		default: +			DBG_RETURN(FAIL); +	} +	DBG_INF_FMT("value=%lu", value); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::free_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_stmt::free_result"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	if (!stmt->result) { +		DBG_INF("no result"); +		DBG_RETURN(PASS); +	} + +	if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { +		DBG_INF("fetching result set header"); +		/* Do implicit use_result and then flush the result */ +		stmt->default_rset_handler = stmt->m->use_result; +		stmt->default_rset_handler(stmt TSRMLS_CC); +	} + +	if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { +		DBG_INF("skipping result"); +		/* Flush if anything is left and unbuffered set */ +		stmt->result->m.skip_result(stmt->result TSRMLS_CC); +		/* +		  Separate the bound variables, which point to the result set, then +		  destroy the set. +		*/ +		mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + +		/* Now we can destroy the result set */ +		stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); +	} + +	/* As the buffers have been freed, we should go back to PREPARED */ +	stmt->state = MYSQLND_STMT_PREPARED; + +	/* Line is free! */ +	stmt->conn->state = CONN_READY; + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_separate_result_bind */ +void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) +{ +	unsigned int i; + +	DBG_ENTER("mysqlnd_stmt_separate_result_bind"); +	DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", +				stmt->stmt_id, stmt->result_bind, stmt->field_count); + +	if (!stmt->result_bind) { +		DBG_VOID_RETURN; +	} + +	/* +	  Because only the bound variables can point to our internal buffers, then +	  separate or free only them. Free is possible because the user could have +	  lost reference. +	*/ +	for (i = 0; i < stmt->field_count; i++) { +		/* Let's try with no cache */ +		if (stmt->result_bind[i].bound == TRUE) { +			DBG_INF_FMT("%d has refcount=%u", i, ZVAL_REFCOUNT(stmt->result_bind[i].zv)); +			/* +			  We have to separate the actual zval value of the bound +			  variable from our allocated zvals or we will face double-free +			*/ +			if (ZVAL_REFCOUNT(stmt->result_bind[i].zv) > 1) { +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +				zval_copy_ctor(stmt->result_bind[i].zv); +#endif +				zval_ptr_dtor(&stmt->result_bind[i].zv); +			} else { +				/* +				  If it is a string, what is pointed will be freed +				  later in free_result(). We need to remove the variable to +				  which the user has lost reference. +				*/ +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF +				ZVAL_NULL(stmt->result_bind[i].zv); +#endif +				zval_ptr_dtor(&stmt->result_bind[i].zv); +			} +		} +	} +	mnd_efree(stmt->result_bind); +	stmt->result_bind = NULL; + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_internal_free_stmt_content */ +static +void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_internal_free_stmt_content"); +	DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", +				stmt->stmt_id, stmt->param_bind, stmt->param_count); + +	/* Destroy the input bind */ +	if (stmt->param_bind) { +		int i; +		/* +		  Because only the bound variables can point to our internal buffers, then +		  separate or free only them. Free is possible because the user could have +		  lost reference. +		*/ +		for (i = 0; i < stmt->param_count; i++) { +			/* For BLOBs zv is NULL */ +			if (stmt->param_bind[i].zv) { +				zval_ptr_dtor(&stmt->param_bind[i].zv); +			} +		} + +		mnd_efree(stmt->param_bind); +		stmt->param_bind = NULL; +	} + +	/* +	  First separate the bound variables, which point to the result set, then +	  destroy the set. +	*/ +	mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); +	/* Not every statement has a result set attached */ +	if (stmt->result) { +		stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); +		stmt->result = NULL; +	} +	if (stmt->cmd_buffer.buffer) { +		mnd_efree(stmt->cmd_buffer.buffer); +		stmt->cmd_buffer.buffer = NULL; +	} + +	if (stmt->conn) { +		stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); +		stmt->conn = NULL; +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::close */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +{ +	MYSQLND * conn = stmt->conn; +	zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; +	enum_mysqlnd_collected_stats stat = STAT_LAST; + +	DBG_ENTER("mysqlnd_stmt::close"); +	DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + +	SET_EMPTY_ERROR(stmt->error_info); +	SET_EMPTY_ERROR(stmt->conn->error_info); + +	/* +	  If the user decided to close the statement right after execute() +	  We have to call the appropriate use_result() or store_result() and +	  clean. +	*/ +	if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { +		DBG_INF("fetching result set header"); +		stmt->default_rset_handler(stmt TSRMLS_CC); +		stmt->state = MYSQLND_STMT_USER_FETCHING; +	} + +	/* unbuffered set not fetched to the end ? Clean the line */ +	if (stmt->result) { +		DBG_INF("skipping result"); +		stmt->result->m.skip_result(stmt->result TSRMLS_CC); +	} +	/* +	  After this point we are allowed to free the result set, +	  as we have cleaned the line +	*/ +	if (stmt->stmt_id) { +		MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?	STAT_FREE_RESULT_IMPLICIT: +														STAT_FREE_RESULT_EXPLICIT); + +		int4store(cmd_buf, stmt->stmt_id); +		if (conn->state == CONN_READY && +			FAIL == mysqlnd_simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf), +										   PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/, +										   FALSE TSRMLS_CC)) { +			stmt->error_info = conn->error_info; +			DBG_RETURN(FAIL); +		} +	} +	switch (stmt->execute_count) { +		case 0: +			stat = STAT_PS_PREPARED_NEVER_EXECUTED; +			break; +		case 1: +			stat = STAT_PS_PREPARED_ONCE_USED; +			break; +		default: +			break; +	} +	if (stat != STAT_LAST) { +		MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); +	} + +	mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::dtor */ +static enum_func_status +MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +{ +	enum_func_status ret; + +	DBG_ENTER("mysqlnd_stmt::close"); +	DBG_INF_FMT("stmt=%p", stmt); + +	MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?	STAT_STMT_CLOSE_IMPLICIT: +													STAT_STMT_CLOSE_EXPLICIT); + +	if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) { +		mnd_efree(stmt); +	} + +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +static +struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { +	MYSQLND_METHOD(mysqlnd_stmt, prepare), +	MYSQLND_METHOD(mysqlnd_stmt, execute), +	MYSQLND_METHOD(mysqlnd_stmt, use_result), +	MYSQLND_METHOD(mysqlnd_stmt, store_result), +	MYSQLND_METHOD(mysqlnd_stmt, get_result), +	MYSQLND_METHOD(mysqlnd_stmt, free_result), +	MYSQLND_METHOD(mysqlnd_stmt, data_seek), +	MYSQLND_METHOD(mysqlnd_stmt, reset), +	MYSQLND_METHOD(mysqlnd_stmt, close), +	MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor), + +	MYSQLND_METHOD(mysqlnd_stmt, fetch), + +	MYSQLND_METHOD(mysqlnd_stmt, bind_param), +	MYSQLND_METHOD(mysqlnd_stmt, bind_result), +	MYSQLND_METHOD(mysqlnd_stmt, send_long_data), +	MYSQLND_METHOD(mysqlnd_stmt, param_metadata), +	MYSQLND_METHOD(mysqlnd_stmt, result_metadata), + +	MYSQLND_METHOD(mysqlnd_stmt, insert_id), +	MYSQLND_METHOD(mysqlnd_stmt, affected_rows), +	MYSQLND_METHOD(mysqlnd_stmt, num_rows), + +	MYSQLND_METHOD(mysqlnd_stmt, param_count), +	MYSQLND_METHOD(mysqlnd_stmt, field_count), +	MYSQLND_METHOD(mysqlnd_stmt, warning_count), + +	MYSQLND_METHOD(mysqlnd_stmt, errno), +	MYSQLND_METHOD(mysqlnd_stmt, error), +	MYSQLND_METHOD(mysqlnd_stmt, sqlstate), + +	MYSQLND_METHOD(mysqlnd_stmt, attr_get), +	MYSQLND_METHOD(mysqlnd_stmt, attr_set),	 +}; + + +/* {{{ _mysqlnd_stmt_init */ +MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) +{ +	MYSQLND_STMT *stmt = mnd_ecalloc(1, sizeof(MYSQLND_STMT)); + +	DBG_ENTER("_mysqlnd_stmt_init"); +	DBG_INF_FMT("stmt=%p", stmt);  + +	stmt->m = &mysqlnd_stmt_methods; +	stmt->state = MYSQLND_STMT_INITTED; +	stmt->cmd_buffer.length = 4096; +	stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length); + +	stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; +	/* +	  Mark that we reference the connection, thus it won't be +	  be destructed till there is open statements. The last statement +	  or normal query result will close it then. +	*/ +	stmt->conn = conn->m->get_reference(conn); + +	DBG_RETURN(stmt); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c new file mode 100644 index 0000000000..ea04cfb5fa --- /dev/null +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -0,0 +1,854 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" + +#define MYSQLND_SILENT + + + +typedef int8  my_int8; +typedef uint8  my_uint8; + +typedef int16 my_int16; +typedef uint16 my_uint16; + +typedef int32   my_int32; +typedef uint32 my_uint32; + + +enum mysqlnd_timestamp_type +{ +  MYSQLND_TIMESTAMP_NONE= -2, +  MYSQLND_TIMESTAMP_ERROR= -1, +  MYSQLND_TIMESTAMP_DATE= 0, +  MYSQLND_TIMESTAMP_DATETIME= 1, +  MYSQLND_TIMESTAMP_TIME= 2 +}; + + +struct st_mysqlnd_time +{ +  unsigned int  year, month, day, hour, minute, second; +  unsigned long second_part; +  zend_bool     neg; +  enum mysqlnd_timestamp_type time_type; +}; + + + +struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; + +#define MYSQLND_PS_SKIP_RESULT_W_LEN	-1 +#define MYSQLND_PS_SKIP_RESULT_STR		-2 + +/* {{{ ps_fetch_from_1_to_8_bytes */ +void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, +								uint pack_len, zend_uchar **row, zend_bool as_unicode, +								unsigned int byte_count TSRMLS_DC) +{ +	char tmp[22]; +	size_t tmp_len = 0; +	zend_bool is_bit = field->type == MYSQL_TYPE_BIT; +	if (field->flags & UNSIGNED_FLAG) { +		my_uint64 uval = 0; + +		switch (byte_count) { +			case 8:uval = is_bit? (my_uint64) bit_uint8korr(*row):(my_uint64) uint8korr(*row);break; +			case 7:uval = bit_uint7korr(*row);break; +			case 6:uval = bit_uint6korr(*row);break; +			case 5:uval = bit_uint5korr(*row);break; +			case 4:uval = is_bit? (my_uint64) bit_uint4korr(*row):(my_uint64) uint4korr(*row);break; +			case 3:uval = is_bit? (my_uint64) bit_uint3korr(*row):(my_uint64) uint3korr(*row);break; +			case 2:uval = is_bit? (my_uint64) bit_uint2korr(*row):(my_uint64) uint2korr(*row);break; +			case 1:uval = (my_uint64) uint1korr(*row);break; +		} + +#if SIZEOF_LONG==4 +		if (uval > INT_MAX) { +			tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval); +		} else  +#endif /* #if SIZEOF_LONG==4 */ +		{ +			if (byte_count < 8 || uval <= L64(9223372036854775807)) { +				ZVAL_LONG(zv, uval); +			} else { +				tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval); +			} +		} +	} else { +		/* SIGNED */ +		my_int64 lval = 0; +		switch (byte_count) { +			case 8:lval = (my_int64) sint8korr(*row);break; +			/* +			  7, 6 and 5 are not possible. +			  BIT is only unsigned, thus only uint5|6|7 macroses exist +			*/ +			case 4:lval = (my_int64) sint4korr(*row);break; +			case 3:lval = (my_int64) sint3korr(*row);break; +			case 2:lval = (my_int64) sint2korr(*row);break; +			case 1:lval = (my_int64) *(my_int8*)*row;break; +		} + +#if SIZEOF_LONG==4 +	    if ((L64(2147483647) < (my_int64) lval) || (L64(-2147483648) > (my_int64) lval)) { +			tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval); +		} else  +#endif /* SIZEOF */ +		{ +			ZVAL_LONG(zv, lval); +		} +	} + +	if (tmp_len) { +#if PHP_MAJOR_VERSION >= 6 +		if (as_unicode) { +			ZVAL_UTF8_STRINGL(zv, tmp, tmp_len, ZSTR_DUPLICATE); +		} else +#endif +		{ +			ZVAL_STRINGL(zv, tmp, tmp_len, 1); +		}			 +	} +	(*row)+= byte_count; +} +/* }}} */ + + +/* {{{ ps_fetch_null */ +static +void ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, +				   uint pack_len, zend_uchar **row, +				   zend_bool as_unicode TSRMLS_DC) +{ +	ZVAL_NULL(zv); +} +/* }}} */ + + +/* {{{ ps_fetch_int8 */ +static +void ps_fetch_int8(zval *zv, const MYSQLND_FIELD * const field, +				   uint pack_len, zend_uchar **row, +				   zend_bool as_unicode TSRMLS_DC) +{ +	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 1 TSRMLS_CC); +#if 0 +	if (field->flags & UNSIGNED_FLAG) { +		ZVAL_LONG(zv, *(my_uint8*)*row); +	} else { +		ZVAL_LONG(zv, *(my_int8*)*row); +	} +	(*row)++; +#endif +} +/* }}} */ + + +/* {{{ ps_fetch_int16 */ +static +void ps_fetch_int16(zval *zv, const MYSQLND_FIELD * const field, +					uint pack_len, zend_uchar **row, +					zend_bool as_unicode TSRMLS_DC) +{ +	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 2 TSRMLS_CC); +#if 0 +	if (field->flags & UNSIGNED_FLAG) { +		ZVAL_LONG(zv, (my_uint16) sint2korr(*row)); +	} else { +		ZVAL_LONG(zv, (my_int16) sint2korr(*row));	 +	} +	(*row)+= 2; +#endif +} +/* }}} */ + + +/* {{{ ps_fetch_int32 */ +static +void ps_fetch_int32(zval *zv, const MYSQLND_FIELD * const field, +					uint pack_len, zend_uchar **row, +					zend_bool as_unicode TSRMLS_DC) +{ +	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 4 TSRMLS_CC); +#if 0 + +	if (field->flags & UNSIGNED_FLAG) { +		my_uint32 uval; + +		/* unsigned int (11) */ +		uval= (my_uint32) sint4korr(*row); +#if SIZEOF_LONG==4 +		if (uval > INT_MAX) { +			char *tmp, *p; +			int j=10; +			tmp= mnd_emalloc(11); +			p= &tmp[9]; +			do {  +				*p-- = (uval % 10) + 48; +				uval = uval / 10;							 +			} while (--j > 0); +			tmp[10]= '\0'; +			/* unsigned int > INT_MAX is 10 digits - ALWAYS */ +#if PHP_MAJOR_VERSION >= 6 +			if (!as_unicode) { +#endif +				ZVAL_STRING(zv, tmp, 0); +#if PHP_MAJOR_VERSION >= 6 +			} else { +				ZVAL_UTF8_STRINGL(zv, tmp, 10, ZSTR_AUTOFREE); +			} +#endif /* PHP_MAJOR_VERSION >= 6 */ +		} else  +#endif /* #if SIZEOF_LONG==4 */ +		{ +			ZVAL_LONG(zv, uval); +		} +	} else { +		ZVAL_LONG(zv, (my_int32) sint4korr(*row)); +	} +	(*row)+= 4; +#endif /* 0 */ +} +/* }}} */ + + +/* {{{ ps_fetch_int64 */ +static +void ps_fetch_int64(zval *zv, const MYSQLND_FIELD * const field, +					uint pack_len, zend_uchar **row, +					zend_bool as_unicode TSRMLS_DC) +{ +	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 8 TSRMLS_CC); +#if 0 + +	my_uint64 llval = (my_uint64) sint8korr(*row); +	zend_bool uns = field->flags & UNSIGNED_FLAG? TRUE:FALSE; +	 +#if SIZEOF_LONG==8   +	if (uns == TRUE && llval > 9223372036854775807L) { +#elif SIZEOF_LONG==4 +	if ((uns == TRUE && llval > L64(2147483647)) ||  +	    (uns == FALSE && ((L64( 2147483647) < (my_int64) llval) || +						  (L64(-2147483648) > (my_int64) llval)))) +	{ +#endif +		char tmp[22]; +		/* even though lval is declared as unsigned, the value +		 * may be negative. Therefor we cannot use MYSQLND_LLU_SPEC and must +		 * use MYSQLND_LL_SPEC. +		 */ +		sprintf((char *)&tmp, uns == TRUE? MYSQLND_LLU_SPEC : MYSQLND_LL_SPEC, llval); +#if PHP_MAJOR_VERSION >= 6 +		if (!as_unicode) { +#endif		 +			ZVAL_STRING(zv, tmp, 1); +#if PHP_MAJOR_VERSION >= 6 +		} else { +			ZVAL_UTF8_STRING(zv, tmp, ZSTR_DUPLICATE); +		} +#endif +	} else { +		/* This cast is safe, as we have checked the values above */ +		ZVAL_LONG(zv, (long) llval); +	} +  	(*row)+= 8; +#endif /* 0 */ +} +/* }}} */ + + +/* {{{ ps_fetch_float */ +static +void ps_fetch_float(zval *zv, const MYSQLND_FIELD * const field, +					uint pack_len, zend_uchar **row, +					zend_bool as_unicode TSRMLS_DC) +{ +	float value; +	float4get(value, *row); +	ZVAL_DOUBLE(zv, value); +  	(*row)+= 4; +} +/* }}} */ + + +/* {{{ ps_fetch_double */ +static +void ps_fetch_double(zval *zv, const MYSQLND_FIELD * const field, +					uint pack_len, zend_uchar **row, +					zend_bool as_unicode TSRMLS_DC) +{ +	double value; +	float8get(value, *row); +	ZVAL_DOUBLE(zv, value); +	(*row)+= 8; +} +/* }}} */ + + +/* {{{ ps_fetch_time */ +static +void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field, +				   uint pack_len, zend_uchar **row, +				   zend_bool as_unicode TSRMLS_DC) +{ +	struct st_mysqlnd_time t; +	unsigned int length; /* First byte encodes the length*/ +	char *to; + +	if ((length = php_mysqlnd_net_field_length(row))) { +		zend_uchar *to= *row; + +		t.time_type = MYSQLND_TIMESTAMP_TIME; +		t.neg			= (zend_bool) to[0]; + +		t.day			= (unsigned long) sint4korr(to+1); +		t.hour			= (unsigned int) to[5]; +		t.minute		= (unsigned int) to[6]; +		t.second		= (unsigned int) to[7]; +		t.second_part	= (length > 8) ? (unsigned long) sint4korr(to+8) : 0; +		t.year			= t.month= 0; +		if (t.day) { +			/* Convert days to hours at once */ +			t.hour += t.day*24; +			t.day	= 0; +		} + +		(*row) += length; +	} else { +		memset(&t, 0, sizeof(t)); +		t.time_type = MYSQLND_TIMESTAMP_TIME; + 	} + +	/* +	  QQ : How to make this unicode without copying two times the buffer - +	  Unicode equivalent of spprintf? +	*/ +	length = spprintf(&to, 0, "%s%02u:%02u:%02u", +					 (t.neg ? "-" : ""), t.hour, t.minute, t.second); + +#if PHP_MAJOR_VERSION >= 6 +	if (!as_unicode) { +#endif +		ZVAL_STRINGL(zv, to, length, 1); +		mnd_efree(to); +#if PHP_MAJOR_VERSION >= 6 +	} else { +		ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);	 +	} +#endif +} +/* }}} */ + + +/* {{{ ps_fetch_date */ +static +void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field, +				   uint pack_len, zend_uchar **row, +				   zend_bool as_unicode TSRMLS_DC) +{ +	struct st_mysqlnd_time t = {0}; +	unsigned int length; /* First byte encodes the length*/ +	char *to; + +	if ((length = php_mysqlnd_net_field_length(row))) { +		zend_uchar *to= *row; + +		t.time_type= MYSQLND_TIMESTAMP_DATE; +		t.neg= 0; + +		t.second_part = t.hour = t.minute = t.second = 0; + +		t.year	= (unsigned int) sint2korr(to); +		t.month = (unsigned int) to[2]; +		t.day	= (unsigned int) to[3]; + +		(*row)+= length; +	} else { +		memset(&t, 0, sizeof(t)); +		t.time_type = MYSQLND_TIMESTAMP_DATE; +	} + +	/* +	  QQ : How to make this unicode without copying two times the buffer - +	  Unicode equivalent of spprintf? +	*/ +	length = spprintf(&to, 0, "%04u-%02u-%02u", t.year, t.month, t.day); + +#if PHP_MAJOR_VERSION >= 6 +	if (!as_unicode) { +#endif +		ZVAL_STRINGL(zv, to, length, 1); +		mnd_efree(to); +#if PHP_MAJOR_VERSION >= 6 +	} else { +		ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);	 +	} +#endif +} +/* }}} */ + + +/* {{{ ps_fetch_datetime */ +static +void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field, +					   uint pack_len, zend_uchar **row, +					   zend_bool as_unicode TSRMLS_DC) +{ +	struct st_mysqlnd_time t; +	unsigned int length; /* First byte encodes the length*/ +	char *to; + +	if ((length = php_mysqlnd_net_field_length(row))) { +		zend_uchar *to= *row; + +		t.time_type = MYSQLND_TIMESTAMP_DATETIME; +		t.neg	 = 0; + +		t.year	 = (unsigned int) sint2korr(to); +		t.month = (unsigned int) to[2]; +		t.day	 = (unsigned int) to[3]; + +		if (length > 4) { +			t.hour	 = (unsigned int) to[4]; +			t.minute = (unsigned int) to[5]; +			t.second = (unsigned int) to[6]; +		} else { +      		t.hour = t.minute = t.second= 0; +		} +		t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0; + +		(*row)+= length; +	} else { +		memset(&t, 0, sizeof(t)); +		t.time_type = MYSQLND_TIMESTAMP_DATETIME; +	} + +	/* +	  QQ : How to make this unicode without copying two times the buffer - +	  Unicode equivalent of spprintf? +	*/ +	length = spprintf(&to, 0, "%04u-%02u-%02u %02u:%02u:%02u", +					  t.year, t.month, t.day, t.hour, t.minute, t.second); + +#if PHP_MAJOR_VERSION >= 6 +	if (!as_unicode) { +#endif +		ZVAL_STRINGL(zv, to, length, 1); +		mnd_efree(to); +#if PHP_MAJOR_VERSION >= 6 +	} else { +		ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);	 +	} +#endif +} +/* }}} */ + + +/* {{{ ps_fetch_string */ +static +void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field, +					 uint pack_len, zend_uchar **row, +					 zend_bool as_unicode TSRMLS_DC) +{ +	/* +	  For now just copy, before we make it possible +	  to write \0 to the row buffer +	*/ +	unsigned long length= php_mysqlnd_net_field_length(row); + +#if PHP_MAJOR_VERSION < 6 +	ZVAL_STRINGL(zv, (char *)*row, length, 1);	 +#else +	if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) { +		ZVAL_STRINGL(zv, (char *)*row, length, 1); +	} else { +		ZVAL_UTF8_STRINGL(zv, (char*)*row, length, ZSTR_DUPLICATE); +	} +#endif + +	(*row) += length; +} +/* }}} */ + + +/* {{{ ps_fetch_bit */ +static +void ps_fetch_bit(zval *zv, const MYSQLND_FIELD * const field, +				  uint pack_len, zend_uchar **row, +				  zend_bool as_unicode TSRMLS_DC) +{ +	unsigned long length= php_mysqlnd_net_field_length(row); +	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, length TSRMLS_CC); +} +/* }}} */ + + +/* {{{ _mysqlnd_init_ps_subsystem */ +void _mysqlnd_init_ps_subsystem() +{ +	memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions)); +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func		= ps_fetch_null; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len 	= 0; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type 	= IS_NULL; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func		= ps_fetch_int8; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len	= 1; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func		= ps_fetch_int16; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len	= 2; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func		= ps_fetch_int16; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len	= 2; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func		= ps_fetch_int32; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len	= 4; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func		= ps_fetch_int32; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len	= 4; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type 	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func	= ps_fetch_int64; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type = IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func		= ps_fetch_float; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len	= 4; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type	= IS_DOUBLE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func		= ps_fetch_double; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len	= 8; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni 	= TRUE; +	 +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func		= ps_fetch_time; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func		= ps_fetch_date; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func		= ps_fetch_date; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni 	= TRUE; +	 +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func	= ps_fetch_datetime; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func	= ps_fetch_datetime; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE; +	 +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func	= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni 	= TRUE; +	 +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func	= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func		= ps_fetch_bit; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len	= 8; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type	= IS_LONG; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func			= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len		= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type 	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type	= IS_STRING; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni 	= TRUE; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func		= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len	= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type	= IS_STRING; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func			= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len		= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type		= IS_STRING; + +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func	= ps_fetch_string; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR; +	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_execute_store_params */ +void +mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, +								  size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC) +{ +	unsigned int i = 0; +	unsigned left = (*buf_len - (*p - *buf)); +	unsigned int data_size = 0; + +/* 1. Store type information */ +	if (stmt->send_types_to_server) { + +		/* 2 bytes per type, and leave 20 bytes for future use */ +		if (left < ((stmt->param_count * 2) + 20)) { +			unsigned int offset = *p - *buf; +			zend_uchar *tmp_buf; +			*buf_len = offset + stmt->param_count * 2 + 20; +			tmp_buf = mnd_emalloc(*buf_len); +			memcpy(tmp_buf, *buf, offset); +			*buf = tmp_buf; +			 +			/* Update our pos pointer */ +			*p = *buf + offset; +		} +		for (i = 0; i < stmt->param_count; i++) { +			/* our types are not unsigned */ +#if SIZEOF_LONG==8   +			if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) { +				stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG; +			} +#endif +			int2store(*p, stmt->param_bind[i].type); +			*p+= 2; +		} +	} + +/* 2. Store data */ +	/* 2.1 Calculate how much space we need */ +	for (i = 0; i < stmt->param_count; i++) { +		if (stmt->param_bind[i].zv && +			Z_TYPE_P(stmt->param_bind[i].zv) == IS_NULL) { +			continue; +		} + +		switch (stmt->param_bind[i].type) { +			case MYSQL_TYPE_DOUBLE: +				data_size += 8; +				break; +#if SIZEOF_LONG==8   +			case MYSQL_TYPE_LONGLONG: +				data_size += 8; +				break; +#elif SIZEOF_LONG==4 +			case MYSQL_TYPE_LONG: +				data_size += 4; +				break; +#else +#error "Should not happen" +#endif +			case MYSQL_TYPE_LONG_BLOB: +				if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) { +					/* +					  User hasn't sent anything, we will send empty string. +					  Empty string has length of 0, encoded in 1 byte. No real +					  data will follow after it. +					*/ +					data_size++; +				} +				break; +			case MYSQL_TYPE_VAR_STRING: +				data_size += 8; /* max 8 bytes for size */ +				convert_to_string_ex(&stmt->param_bind[i].zv); +				data_size += Z_STRLEN_P(stmt->param_bind[i].zv); +				break; +		} + +	} + +	/* 2.2 Enlarge the buffer, if needed */ +	left = (*buf_len - (*p - *buf)); +	if (left < data_size) { +		unsigned int offset = *p - *buf; +		zend_uchar *tmp_buf; +		*buf_len = offset + data_size + 10; /* Allocate + 10 for safety */ +		tmp_buf = mnd_emalloc(*buf_len); +		memcpy(tmp_buf, *buf, offset); +		*buf = tmp_buf; +		/* Update our pos pointer */ +		*p = *buf + offset;	 +	} + +	/* 2.3 Store the actual data */ +	for (i = 0; i < stmt->param_count; i++) { +		zval *data = stmt->param_bind[i].zv; +		/* Handle long data */ +		if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) { +			(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7)); +		} else { +			switch (stmt->param_bind[i].type) { +				case MYSQL_TYPE_DOUBLE: +					convert_to_double_ex(&data); +					float8store(*p, Z_DVAL_P(data)); +					(*p) += 8; +					break; +#if SIZEOF_LONG==8   +				case MYSQL_TYPE_LONGLONG: +					convert_to_long_ex(&data); +					int8store(*p, Z_LVAL_P(data)); +					(*p) += 8; +					break; +#elif SIZEOF_LONG==4 +				case MYSQL_TYPE_LONG: +					convert_to_long_ex(&data); +					int4store(*p, Z_LVAL_P(data)); +					(*p) += 4; +					break; +#else +#error "Should not happen" +#endif +				case MYSQL_TYPE_LONG_BLOB: +					if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) { +						stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; +					} else { +						/* send_long_data() not called, send empty string */ +						*p = php_mysqlnd_net_store_length(*p, 0); +					} +					break; +				case MYSQL_TYPE_VAR_STRING: +					/* +					  If the user uses refs, it could be that the type has +					  has changed and we need to convert, again. Which is noop, +					  if the type hasn't changed. +					*/ +					convert_to_string_ex(&stmt->param_bind[i].zv); +					{ +						unsigned int len = Z_STRLEN_P(data); +						/* to is after p. The latter hasn't been moved */ +						*p = php_mysqlnd_net_store_length(*p, len); +						memcpy(*p, Z_STRVAL_P(data), len); +						(*p) += len; +					} + +					break; +				default: +					/* Won't happen, but set to NULL */ +					(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7)); +					break; +			} +		} +	} +} +/* }}} */ + + +/* {{{ mysqlnd_stmt_execute_generate_request */ +zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, +												  zend_bool *free_buffer TSRMLS_DC) +{ +	zend_uchar	*p = stmt->cmd_buffer.buffer, +				*cmd_buffer = stmt->cmd_buffer.buffer; +	size_t cmd_buffer_length = stmt->cmd_buffer.length; +	unsigned int	null_byte_offset, +					null_count= (stmt->param_count + 7) / 8; + +	int4store(p, stmt->stmt_id); +	p += 4; + +	/* flags is 4 bytes, we store just 1 */ +	int1store(p, (zend_uchar) stmt->flags); +	p++; + +	/* Make it all zero */ +	int4store(p, 0);  + +	int1store(p, 1); /* and send 1 for iteration count */ +	p+= 4; + + +	null_byte_offset = p - cmd_buffer; +	memset(p, 0, null_count); +	p += null_count; + + +	int1store(p, stmt->send_types_to_server);  +	p++; + +	mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC); + +	*free_buffer = (cmd_buffer != stmt->cmd_buffer.buffer); +	*request_len = (p - cmd_buffer); +	return cmd_buffer; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_qcache.c b/ext/mysqlnd/mysqlnd_qcache.c new file mode 100644 index 0000000000..a08c7d488d --- /dev/null +++ b/ext/mysqlnd/mysqlnd_qcache.c @@ -0,0 +1,141 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_statistics.h" + +#define MYSQLND_SILENT + +#ifdef ZTS +#define LOCK_QCACHE(cache)		tsrm_mutex_lock((cache)->LOCK_access) +#define UNLOCK_QCACHE(cache)	tsrm_mutex_unlock((cache)->LOCK_access) +#else +#define LOCK_QCACHE(cache) +#define UNLOCK_QCACHE(cache) +#endif + + +/* {{{ mysqlnd_qcache_init_cache */ +PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_init_cache() +{ +	MYSQLND_QCACHE *cache = calloc(1, sizeof(MYSQLND_QCACHE)); +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_qcache_init_cache %p]\n", cache); +#endif + +	cache->references = 1; +#ifdef ZTS +	cache->LOCK_access = tsrm_mutex_alloc(); +#endif +	cache->ht = malloc(sizeof(HashTable)); +	zend_hash_init(cache->ht, 10 /* init_elements */, NULL, NULL, TRUE /*pers*/); + +	return cache; +} +/* }}} */ + + +/* {{{ mysqlnd_qcache_get_cache_reference */ +PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_get_cache_reference(MYSQLND_QCACHE * const cache) +{ +	if (cache) { +#ifndef MYSQLND_SILENT +		php_printf("[mysqlnd_qcache_get_cache_reference %p will become %d]\n", cache, cache->references+1); +#endif +		LOCK_QCACHE(cache); +		cache->references++; +		UNLOCK_QCACHE(cache); +	} +	return cache; +} +/* }}} */ + + +/* {{{ mysqlnd_qcache_free_cache */ +/* +  As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with +  copy_ctor but scrap what they point to with zval_dtor() and then just free our +  pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them +  to the free list after usage. We ZVAL_NULL() them when we allocate them in the  +  constructor of the cache. +*/ +static +void mysqlnd_qcache_free_cache(MYSQLND_QCACHE *cache) +{ +#ifndef MYSQLND_SILENT +	php_printf("[mysqlnd_qcache_free_cache %p]\n", cache); +#endif + +#ifdef ZTS +	tsrm_mutex_free(cache->LOCK_access); +#endif +	zend_hash_destroy(cache->ht); +	free(cache->ht); +	free(cache); +} +/* }}} */ + + +/* {{{ mysqlnd_qcache_free_cache_reference */ +PHPAPI void mysqlnd_qcache_free_cache_reference(MYSQLND_QCACHE **cache) +{ +	if (*cache) { +		zend_bool to_free; +#ifndef MYSQLND_SILENT +		php_printf("[mysqlnd_qcache_free_cache_reference %p] refs=%d\n", *cache, (*cache)->references); +#endif +		LOCK_QCACHE(*cache); +		to_free = --(*cache)->references == 0; +		/* Unlock before destroying */ +		UNLOCK_QCACHE(*cache); +		if (to_free) { +			mysqlnd_qcache_free_cache(*cache); +		} +		*cache = NULL; +	} +} +/* }}} */ + + +/* {{{ mysqlnd_qcache_free_cache_reference */ +PHPAPI void mysqlnd_qcache_stats(const MYSQLND_QCACHE * const cache, zval *return_value) +{ +	if (cache) { +		LOCK_QCACHE(cache); +		array_init(return_value); +		add_assoc_long_ex(return_value, "references",	sizeof("references"),	cache->references); +		UNLOCK_QCACHE(cache); +	} else { +		ZVAL_NULL(return_value); +	} +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c new file mode 100644 index 0000000000..2d42d65469 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_result.c @@ -0,0 +1,1194 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_result.h" +#include "mysqlnd_result_meta.h" +#include "mysqlnd_statistics.h" +#include "mysqlnd_charset.h" +#include "mysqlnd_debug.h" +#include "ext/standard/basic_functions.h" + +#define MYSQLND_SILENT + + +/* {{{ mysqlnd_unbuffered_free_last_data */ +void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC) +{ +	MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf; + +	DBG_ENTER("mysqlnd_unbuffered_free_last_data"); + +	if (!unbuf) { +		DBG_VOID_RETURN; +	} + +	if (unbuf->last_row_data) { +		unsigned int i, ctor_called_count = 0; +		zend_bool copy_ctor_called; +		MYSQLND_STATS *global_stats = result->conn? &result->conn->stats:NULL; +		for (i = 0; i < result->field_count; i++) { +			mysqlnd_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]), +										 result->zval_cache, result->type, +										 ©_ctor_called TSRMLS_CC); +			if (copy_ctor_called) { +				ctor_called_count++; +			} +		} +		/* By using value3 macros we hold a mutex only once, there is no value2 */ +		MYSQLND_INC_CONN_STATISTIC_W_VALUE3(global_stats, +											STAT_COPY_ON_WRITE_PERFORMED, +											ctor_called_count, +											STAT_COPY_ON_WRITE_SAVED, +											result->field_count - ctor_called_count, +											STAT_COPY_ON_WRITE_PERFORMED, 0); +		 +		/* Free last row's zvals */ +		efree(unbuf->last_row_data); +		unbuf->last_row_data = NULL; +	} +	if (unbuf->last_row_buffer) { +		/* Nothing points to this buffer now, free it */ +		efree(unbuf->last_row_buffer); +		unbuf->last_row_buffer = NULL; +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + +/* {{{ mysqlnd_free_buffered_data */ +void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) +{ +	MYSQLND_THD_ZVAL_PCACHE  *zval_cache = result->zval_cache; +	MYSQLND_RES_BUFFERED *set = result->data; +	unsigned int field_count = result->field_count; +	unsigned int row; + +	DBG_ENTER("mysqlnd_free_buffered_data"); +	DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", result->data->row_count); + +	DBG_INF_FMT("before: real_usage=%lu  usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); +	for (row = 0; row < result->data->row_count; row++) { +		unsigned int col; +		zval **current_row = current_row = set->data[row]; +		zend_uchar *current_buffer = set->row_buffers[row]; + +		for (col = 0; col < field_count; col++) { +			zend_bool copy_ctor_called; +			mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache, +										 result->type, ©_ctor_called TSRMLS_CC); +#if MYSQLND_DEBUG_MEMORY +			DBG_INF_FMT("Copy_ctor_called=%d", copy_ctor_called); +#endif +			MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: +														   STAT_COPY_ON_WRITE_SAVED); +		} +#if MYSQLND_DEBUG_MEMORY +		DBG_INF("Freeing current_row & current_buffer"); +#endif +		pefree(current_row, set->persistent); +		pefree(current_buffer, set->persistent); +	} +	DBG_INF("Freeing data & row_buffer"); +	pefree(set->data, set->persistent); +	pefree(set->row_buffers, set->persistent); +	set->data			= NULL; +	set->row_buffers	= NULL; +	set->data_cursor	= NULL; +	set->row_count	= 0; +	if (set->qcache) { +		mysqlnd_qcache_free_cache_reference(&set->qcache); +	} +	DBG_INF("Freeing set"); +	pefree(set, set->persistent); + +	DBG_INF_FMT("after: real_usage=%lu  usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_res::free_result_buffers */ +void +MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::free_result_buffers"); +	DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->data? "buffered":"unknown")); + +	if (result->unbuf) { +		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); +		efree(result->unbuf); +		result->unbuf = NULL; +	} else if (result->data) { +		mysqlnd_free_buffered_data(result TSRMLS_CC); +		result->data = NULL; +	} + +	if (result->lengths) { +		efree(result->lengths); +		result->lengths = NULL; +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_internal_free_result_contents */ +static +void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_internal_free_result_contents"); + +	result->m.free_result_buffers(result TSRMLS_CC); + +	if (result->row_packet) { +		DBG_INF("Freeing packet"); +		PACKET_FREE(result->row_packet); +		result->row_packet = NULL; +	} + +	result->conn = NULL; + +	if (result->meta) { +		result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); +		result->meta = NULL; +	} + +	if (result->zval_cache) { +		DBG_INF("Freeing zval cache reference"); +		mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache); +		result->zval_cache = NULL; +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_internal_free_result */ +static +void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_internal_free_result"); +	/* +	  result->conn is an address if this is an unbuffered query. +	  In this case, decrement the reference counter in the connection +	  object and if needed free the latter. +	*/ +	if (result->conn) { +		result->conn->m->free_reference(result->conn TSRMLS_CC); +		result->conn = NULL; +	} + +	result->m.free_result_contents(result TSRMLS_CC); +	efree(result); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_res::read_result_metadata */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::read_result_metadata"); + +	/* +	  Make it safe to call it repeatedly for PS - +	  better free and allocate a new because the number of field might change  +	  (select *) with altered table. Also for statements which skip the PS +	  infrastructure! +	*/ +	if (result->meta) { +		result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); +		result->meta = NULL; +	} + +	result->meta = mysqlnd_result_meta_init(result->field_count TSRMLS_CC); + +	/* 1. Read all fields metadata */ + +	/* It's safe to reread without freeing */ +	if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) { +		result->m.free_result_contents(result TSRMLS_CC); +		DBG_RETURN(FAIL); +	} + +	/* +	  2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata() +		 should consume. +	  3. If there is a result set, it follows. The last packet will have 'eof' set +	  	 If PS, then no result set follows. +	*/ + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_query_read_result_set_header */ +enum_func_status +mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC) +{ +	enum_func_status ret; +	php_mysql_packet_rset_header rset_header; + +	DBG_ENTER("mysqlnd_query_read_result_set_header"); +	DBG_INF_FMT("stmt=%d", stmt? stmt->stmt_id:0); + +	ret = FAIL; +	PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET); +	do { +		if (FAIL == (ret = PACKET_READ_ALLOCA(rset_header, conn))) { +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header"); +			break; +		} + +		if (rset_header.error_info.error_no) { +			/* +			  Cover a protocol design error: error packet does not +			  contain the server status. Therefore, the client has no way +			  to find out whether there are more result sets of +			  a multiple-result-set statement pending. Luckily, in 5.0 an +			  error always aborts execution of a statement, wherever it is +			  a multi-statement or a stored procedure, so it should be +			  safe to unconditionally turn off the flag here. +			*/ +			conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS; +			conn->upsert_status.affected_rows = -1; +			/* +			  This will copy the error code and the messages, as they +			  are buffers in the struct +			*/ +			conn->error_info = rset_header.error_info; +			ret = FAIL; +			break; +		} +		conn->error_info.error_no = 0; + +		switch (rset_header.field_count) { +			case MYSQLND_NULL_LENGTH: {	/* LOAD DATA LOCAL INFILE */ +				zend_bool is_warning; +				DBG_INF("LOAD DATA"); +				conn->last_query_type = QUERY_LOAD_LOCAL; +				conn->state = CONN_SENDING_LOAD_DATA; +				ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file, &is_warning TSRMLS_CC); +				conn->state = (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT; +				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY); +				break; +			} +			case 0:				/* UPSERT */ +				DBG_INF("UPSERT"); +				conn->last_query_type = QUERY_UPSERT; +				conn->field_count = rset_header.field_count; +				conn->upsert_status.warning_count = rset_header.warning_count; +				conn->upsert_status.server_status = rset_header.server_status; +				conn->upsert_status.affected_rows = rset_header.affected_rows; +				conn->upsert_status.last_insert_id = rset_header.last_insert_id; +				SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, +								rset_header.info_or_local_file, rset_header.info_or_local_file_len, +								conn->persistent); +				/* Result set can follow UPSERT statement, check server_status */ +				if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { +					conn->state = CONN_NEXT_RESULT_PENDING; +				} else { +					conn->state = CONN_READY; +				} +				ret = PASS; +				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY); +				break; +			default:{			/* Result set	*/ +				php_mysql_packet_eof fields_eof; +				MYSQLND_RES *result; +				enum_mysqlnd_collected_stats stat = STAT_LAST; + +				DBG_INF("Result set pending"); +				SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent); + +				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_RSET_QUERY); +				memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); +				conn->last_query_type = QUERY_SELECT; +				conn->state = CONN_FETCHING_DATA; +				/* PS has already allocated it */ +				if (!stmt) { +					conn->field_count = rset_header.field_count; +					result = +						conn->current_result= +							mysqlnd_result_init(rset_header.field_count, +												mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) +												TSRMLS_CC); +				} else { +					if (!stmt->result) { +						DBG_INF("This is 'SHOW'/'EXPLAIN'-like query."); +						/* +						  This is 'SHOW'/'EXPLAIN'-like query. Current implementation of +						  prepared statements can't send result set metadata for these queries +						  on prepare stage. Read it now. +						*/ +						conn->field_count = rset_header.field_count; +						result = +							stmt->result = +								mysqlnd_result_init(rset_header.field_count, +													mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) +													TSRMLS_CC); +					} else { +						/* +						  Update result set metadata if it for some reason changed between +						  prepare and execute, i.e.: +						  - in case of 'SELECT ?' we don't know column type unless data was +							supplied to mysql_stmt_execute, so updated column type is sent +							now. +						  - if data dictionary changed between prepare and execute, for +							example a table used in the query was altered. +						  Note, that now (4.1.3) we always send metadata in reply to +						  COM_STMT_EXECUTE (even if it is not necessary), so either this or +						  previous branch always works. +						*/	 +					} +					result = stmt->result; +				} + +				if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) { +					/* For PS, we leave them in Prepared state */ +					if (!stmt) { +						efree(conn->current_result); +						conn->current_result = NULL; +					} +					DBG_ERR("Error ocurred while reading metadata"); +					break; +				} + +				/* Check for SERVER_STATUS_MORE_RESULTS if needed */ +				PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); +				if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) { +					DBG_ERR("Error ocurred while reading the EOF packet"); +					result->m.free_result_contents(result TSRMLS_CC); +					efree(result); +					if (!stmt) { +						conn->current_result = NULL; +					} else { +						stmt->result = NULL; +						memset(stmt, 0, sizeof(MYSQLND_STMT)); +						stmt->state = MYSQLND_STMT_INITTED; +					} +				} else { +					DBG_INF_FMT("warns=%u status=%u", fields_eof.warning_count, fields_eof.server_status); +					conn->upsert_status.warning_count = fields_eof.warning_count; +					conn->upsert_status.server_status = fields_eof.server_status; +					if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) { +						stat = STAT_BAD_INDEX_USED; +					} else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) { +						stat = STAT_NO_INDEX_USED; +					} +					if (stat != STAT_LAST) { +						char *backtrace = mysqlnd_get_backtrace(TSRMLS_C); +#if A0 +						php_log_err(backtrace TSRMLS_CC); +#endif +						efree(backtrace); +						MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); +					} +				} + +				PACKET_FREE_ALLOCA(fields_eof); + +				break; +			} +		} +	} while (0); +	PACKET_FREE_ALLOCA(rset_header); + +	DBG_INF(ret == PASS? "PASS":"FAIL"); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_fetch_lengths_buffered */ +/* +  Do lazy initialization for buffered results. As PHP strings have +  length inside, this function makes not much sense in the context +  of PHP, to be called as separate function. But let's have it for +  completeness. +*/ +static  +unsigned long * mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result) +{ +	int i; +	zval **previous_row; + +	/* +	  If: +	  - unbuffered result +	  - first row has not been read +	  - last_row has been read +	*/ +	if (result->data->data_cursor == NULL || +		result->data->data_cursor == result->data->data || +		((result->data->data_cursor - result->data->data) > result->data->row_count)) +	{ +		return NULL;/* No rows or no more rows */ +	} + +	previous_row = *(result->data->data_cursor - 1); +	for (i = 0; i < result->field_count; i++) { +		result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]); +	} + +	return result->lengths; +} +/* }}} */ + + +/* {{{ mysqlnd_fetch_lengths_unbuffered */ +static +unsigned long * mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result) +{ +	return result->lengths; +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_lengths */ +PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result) +{ +	return result->m.fetch_lengths? result->m.fetch_lengths(result):NULL; +} +/* }}} */ + + +/* {{{ mysqlnd_fetch_row_unbuffered */ +static enum_func_status +mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, +							zend_bool *fetched_anything TSRMLS_DC) +{ +	enum_func_status		ret; +	zval 					*row = (zval *) param; +	unsigned int			i, +							field_count = result->field_count; +	php_mysql_packet_row	*row_packet = result->row_packet; +	unsigned long			*lengths = result->lengths; + +	DBG_ENTER("mysqlnd_fetch_row_unbuffered"); +	DBG_INF_FMT("flags=%d", flags); + +	if (result->unbuf->eof_reached) { +		/* No more rows obviously */ +		*fetched_anything = FALSE; +		DBG_RETURN(PASS); +	} +	if (result->conn->state != CONN_FETCHING_DATA) { +		SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, +						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);  +		DBG_RETURN(FAIL); +	} +	/* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ +	row_packet->skip_extraction = row? FALSE:TRUE; + +	/* +	  If we skip rows (row == NULL) we have to +	  mysqlnd_unbuffered_free_last_data() before it. The function returns always true. +	*/ +	if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { +		result->unbuf->row_count++; +		*fetched_anything = TRUE; + +		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); + +		result->unbuf->last_row_data = row_packet->fields; +		result->unbuf->last_row_buffer = row_packet->row_buffer; +		row_packet->fields = NULL; +		row_packet->row_buffer = NULL; + +		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); + +		if (!row_packet->skip_extraction) { +			HashTable *row_ht = Z_ARRVAL_P(row); + +			for (i = 0; i < field_count; i++) { +				zval *data = result->unbuf->last_row_data[i]; +				int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); +				MYSQLND_RES_METADATA *meta = result->meta; + +				if (lengths) { +					lengths[i] = len; +				} + +				/* Forbid ZE to free it, we will clean it */ +				ZVAL_ADDREF(data); + +				if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) { +					ZVAL_ADDREF(data); +				} +				if (flags & MYSQLND_FETCH_NUM) { +					zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL); +				} +				if (flags & MYSQLND_FETCH_ASSOC) { +					/* zend_hash_quick_update needs length + trailing zero */ +					/* QQ: Error handling ? */ +					/* +					  zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether +					  the index is a numeric and convert it to it. This however means constant +					  hashing of the column name, which is not needed as it can be precomputed. +					*/ +					if (meta->zend_hash_keys[i].is_numeric == FALSE) { +#if PHP_MAJOR_VERSION >= 6 +						if (UG(unicode)) { +							zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, +													 meta->zend_hash_keys[i].ustr, +													 meta->zend_hash_keys[i].ulen + 1, +													 meta->zend_hash_keys[i].key, +													 (void *) &data, sizeof(zval *), NULL); +						} else +#endif +						{ +							zend_hash_quick_update(Z_ARRVAL_P(row), +												   meta->fields[i].name, +												   meta->fields[i].name_length + 1, +												   meta->zend_hash_keys[i].key, +												   (void *) &data, sizeof(zval *), NULL); +						} +					} else { +						zend_hash_index_update(Z_ARRVAL_P(row), +											   meta->zend_hash_keys[i].key, +											   (void *) &data, sizeof(zval *), NULL); +					} +				} +				if (meta->fields[i].max_length < len) { +					meta->fields[i].max_length = len; +				} +			} +		} +	} else if (ret == FAIL) { +		if (row_packet->error_info.error_no) { +			result->conn->error_info = row_packet->error_info; +			DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error);  +		} +		*fetched_anything = FALSE; +		result->conn->state = CONN_READY; +		result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ +	} else if (row_packet->eof) { +		/* Mark the connection as usable again */ +		DBG_INF_FMT("warns=%u status=%u", row_packet->warning_count, row_packet->server_status); +		result->unbuf->eof_reached = TRUE; +		result->conn->upsert_status.warning_count = row_packet->warning_count; +		result->conn->upsert_status.server_status = row_packet->server_status; +		/* +		  result->row_packet will be cleaned when +		  destroying the result object +		*/ +		if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { +			result->conn->state = CONN_NEXT_RESULT_PENDING; +		} else { +			result->conn->state = CONN_READY; +		} +		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); +		*fetched_anything = FALSE; +	} + +	DBG_INF_FMT("ret=%s fetched=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_res::use_result */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::use_result"); +	DBG_INF_FMT("ps=%d", ps); + +	result->type			= MYSQLND_RES_NORMAL; +	result->m.fetch_row		= result->m.fetch_row_normal_unbuffered; +	result->m.fetch_lengths	= mysqlnd_fetch_lengths_unbuffered; +	result->unbuf			= mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + +	/* +	  Will be freed in the mysqlnd_internal_free_result_contents() called +	  by the resource destructor. mysqlnd_fetch_row_unbuffered() expects +	  this to be not NULL. +	*/ +	PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *); +	result->row_packet->field_count = result->field_count; +	result->row_packet->binary_protocol = FALSE; +	result->row_packet->fields_metadata = result->meta->fields; +	result->row_packet->bit_fields_count = result->meta->bit_fields_count; +	result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; +	result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); + +	/* No multithreading issues as we don't share the connection :) */ + +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_fetch_row_buffered */ +static enum_func_status +mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, +						   zend_bool *fetched_anything TSRMLS_DC) +{ +	unsigned int i; +	zval *row = (zval *) param; + +	DBG_ENTER("mysqlnd_fetch_row_buffered"); +	DBG_INF_FMT("flags=%u row=%p", flags, row); + +	/* If we haven't read everything */ +	if (result->data->data_cursor && +		(result->data->data_cursor - result->data->data) < result->data->row_count) +	{ +		zval **current_row = *result->data->data_cursor; +		for (i = 0; i < result->field_count; i++) { +			zval *data = current_row[i]; + +			/* +			  Let us later know what to do with this zval. If ref_count > 1, we will just +			  decrease it, otherwise free it. zval_ptr_dtor() make this very easy job. +			*/ +			ZVAL_ADDREF(data); +			 +			if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) { +				ZVAL_ADDREF(data); +			} +			if (flags & MYSQLND_FETCH_NUM) { +				zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); +			} +			if (flags & MYSQLND_FETCH_ASSOC) { +				/* zend_hash_quick_update needs length + trailing zero */ +				/* QQ: Error handling ? */ +				/* +				  zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether +				  the index is a numeric and convert it to it. This however means constant +				  hashing of the column name, which is not needed as it can be precomputed. +				*/ +				if (result->meta->zend_hash_keys[i].is_numeric == FALSE) { +#if PHP_MAJOR_VERSION >= 6 +					if (UG(unicode)) { +						zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, +												 result->meta->zend_hash_keys[i].ustr, +												 result->meta->zend_hash_keys[i].ulen + 1, +												 result->meta->zend_hash_keys[i].key, +												 (void *) &data, sizeof(zval *), NULL); +					} else +#endif +					{ +						zend_hash_quick_update(Z_ARRVAL_P(row), +											   result->meta->fields[i].name, +											   result->meta->fields[i].name_length + 1, +											   result->meta->zend_hash_keys[i].key, +											   (void *) &data, sizeof(zval *), NULL); +					} +				} else { +					zend_hash_index_update(Z_ARRVAL_P(row), +										   result->meta->zend_hash_keys[i].key, +										   (void *) &data, sizeof(zval *), NULL); +				} +			} +		} +		result->data->data_cursor++; +		*fetched_anything = TRUE; +		MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); +	} else { +		result->data->data_cursor = NULL; +		*fetched_anything = FALSE; +		DBG_INF("EOF reached"); +	} +	DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything); +	DBG_RETURN(PASS); +} +/* }}} */ + + +#define STORE_RESULT_PREALLOCATED_SET 32 + +/* {{{ mysqlnd_store_result_fetch_data */ +enum_func_status +mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, +								MYSQLND_RES_METADATA *meta, +								zend_bool binary_protocol, +								zend_bool update_max_length, +								zend_bool to_cache TSRMLS_DC) +{ +	enum_func_status ret; +	php_mysql_packet_row row_packet; +	unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows; +	MYSQLND_RES_BUFFERED *set; + +	DBG_ENTER("mysqlnd_store_result_fetch_data"); +	DBG_INF_FMT("conn=%llu binary_proto=%d update_max_len=%d to_cache=%d", +				conn->thread_id, binary_protocol, update_max_length, to_cache); + +	free_rows = next_extend; + +	result->data	= set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); +	set->data		= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache); +	set->row_buffers= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *), to_cache); +	set->persistent	= to_cache; +	set->qcache		= to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL; +	set->references	= 1; + +	PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET); +	row_packet.field_count = meta->field_count; +	row_packet.binary_protocol = binary_protocol; +	row_packet.fields_metadata = meta->fields; +	row_packet.bit_fields_count		= meta->bit_fields_count; +	row_packet.bit_fields_total_len = meta->bit_fields_total_len; +	/* Let the row packet fill our buffer and skip additional malloc + memcpy */ +	while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) { +		int i; +		zval **current_row; + +		if (!free_rows) { +			mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */ +			total_rows += set->row_count; +			set->data = mnd_perealloc(set->data, total_rows * sizeof(zval **), set->persistent); + +			set->row_buffers = mnd_perealloc(set->row_buffers, +										 total_rows * sizeof(zend_uchar *), set->persistent); +		} +		free_rows--; +		current_row = set->data[set->row_count] = row_packet.fields; +		set->row_buffers[set->row_count] = row_packet.row_buffer; +		set->row_count++; + +		/* So row_packet's destructor function won't efree() it */ +		row_packet.fields = NULL; +		row_packet.row_buffer = NULL; + + +		if (update_max_length == TRUE) { +			for (i = 0; i < row_packet.field_count; i++) { +				/* +				  NULL fields are 0 length, 0 is not more than 0 +				  String of zero size, definitely can't be the next max_length. +				  Thus for NULL and zero-length we are quite efficient. +				*/ +				if (Z_TYPE_P(current_row[i]) >= IS_STRING) { +					unsigned long len = Z_STRLEN_P(current_row[i]); +					if (meta->fields[i].max_length < len) { +						meta->fields[i].max_length = len; +					} +				} +			} +		} +		/* +		  No need to FREE_ALLOCA as we can reuse the +		  'lengths' and 'fields' arrays. For lengths its absolutely safe. +		  'fields' is reused because the ownership of the strings has been +		  transfered above.  +		*/ +	} +	MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, +									   binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS: +														STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL, +									   set->row_count); + +	/* Finally clean */ +	if (row_packet.eof) {  +		conn->upsert_status.warning_count = row_packet.warning_count; +		conn->upsert_status.server_status = row_packet.server_status; +	} +	/* save some memory */ +	if (free_rows) { +		set->data = mnd_perealloc(set->data, +							  (size_t) set->row_count * sizeof(zval **), +							  set->persistent); +		set->row_buffers = mnd_perealloc(set->row_buffers, +									 (size_t) set->row_count * sizeof(zend_uchar *), +									 set->persistent); +	} + +	if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { +		conn->state = CONN_NEXT_RESULT_PENDING; +	} else { +		conn->state = CONN_READY; +	} + +	if (ret == FAIL) { +		set->error_info = row_packet.error_info; +	} else { +		/* Position at the first row */ +		set->data_cursor = set->data;	 + +		/* libmysql's documentation says it should be so for SELECT statements */ +		conn->upsert_status.affected_rows = result->data->row_count; +	} +	PACKET_FREE_ALLOCA(row_packet); + +	DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL", +				set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_res::store_result */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, +										  MYSQLND * const conn, +										  zend_bool ps_protocol TSRMLS_DC) +{ +	enum_func_status ret; +	zend_bool to_cache = FALSE; + +	DBG_ENTER("mysqlnd_res::store_result"); +	DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol); + +	result->conn			= NULL;	/* store result does not reference  the connection */ +	result->type			= MYSQLND_RES_NORMAL; +	result->m.fetch_row		= result->m.fetch_row_normal_buffered; +	result->m.fetch_lengths	= mysqlnd_fetch_lengths_buffered; + +	conn->state = CONN_FETCHING_DATA; + +	result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); + +	ret = mysqlnd_store_result_fetch_data(conn, result, result->meta, +										  ps_protocol, TRUE, to_cache TSRMLS_CC); +	if (PASS == ret) { +		/* libmysql's documentation says it should be so for SELECT statements */ +		conn->upsert_status.affected_rows = result->data->row_count; +	} else { +		conn->error_info = result->data->error_info; +		result->m.free_result_internal(result TSRMLS_CC); +		result = NULL; +	} + +	DBG_RETURN(result); +} +/* }}} */ + + +/* {{{ mysqlnd_res::skip_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) +{ +	zend_bool fetched_anything; + +	DBG_ENTER("mysqlnd_res::skip_result"); +	/* +	  Unbuffered sets +	  A PS could be prepared - there is metadata and thus a stmt->result but the +	  fetch_row function isn't actually set (NULL), thus we have to skip these. +	*/ +	if (!result->data && result->conn && result->unbuf && +		!result->unbuf->eof_reached && result->m.fetch_row) +	{ +		DBG_INF("skipping result"); +		/* We have to fetch all data to clean the line */ +		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, +									result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS: +																		STAT_FLUSHED_PS_SETS); + +		while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && +			   fetched_anything == TRUE) +		{ +			/* do nothing */; +		} +	} +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_res::free_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::free_result"); +	DBG_INF_FMT("implicit=%d", implicit); + +	result->m.skip_result(result TSRMLS_CC); +	MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL, +							   implicit == TRUE?	STAT_FREE_RESULT_IMPLICIT: +							   						STAT_FREE_RESULT_EXPLICIT); + +	result->m.free_result_internal(result TSRMLS_CC); +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_res::data_seek */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong row TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::data_seek"); +	DBG_INF_FMT("row=%lu", row); + +	if (!result->data) { +		return FAIL; +	} + +	/* libmysql just moves to the end, it does traversing of a linked list */ +	if (row >= result->data->row_count) { +		result->data->data_cursor = NULL; +	} else { +		result->data->data_cursor = result->data->data + row; +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_res::num_fields */ +mynd_ulonglong +MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res) +{ +	/* Be compatible with libmysql. We count row_count, but will return 0 */ +	return res->data? res->data->row_count:0; +} +/* }}} */ + + +/* {{{ mysqlnd_res::num_fields */ +unsigned int +MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res) +{ +	return res->field_count; +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_field */ +static MYSQLND_FIELD * +MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::fetch_field"); +	DBG_RETURN(result->meta? result->meta->m->fetch_field(result->meta TSRMLS_CC):NULL); +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_field_direct */ +static MYSQLND_FIELD * +MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(const MYSQLND_RES * const result, +												MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res::fetch_field_direct"); +	DBG_RETURN(result->meta? result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC):NULL); +} +/* }}} */ + + +/* {{{ mysqlnd_res::field_seek */ +static MYSQLND_FIELD_OFFSET +MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, +										MYSQLND_FIELD_OFFSET field_offset) +{ +	MYSQLND_FIELD_OFFSET return_value = 0; +	if (result->meta) { +		return_value = result->meta->current_field; +		result->meta->current_field = field_offset; +	} +	return return_value; +} +/* }}} */ + + +/* {{{ mysqlnd_res::field_tell */ +static MYSQLND_FIELD_OFFSET +MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result) +{ +	return result->meta? result->meta->m->field_tell(result->meta):0; +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_into */ +static void +MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, +										zval *return_value, +										enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC) +{ +	zend_bool fetched_anything; + +	DBG_ENTER("mysqlnd_res::fetch_into"); +	DBG_INF_FMT("flags=%u mysqlnd_extension=%d", flags, extension); + +	if (!result->m.fetch_row) { +		RETVAL_NULL(); +		DBG_VOID_RETURN; +	} +	/* +	  Hint Zend how many elements we will have in the hash. Thus it won't +	  extend and rehash the hash constantly. +	*/ +	mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2); +	if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row"); +		RETVAL_FALSE; +	} else if (fetched_anything == FALSE) { +		zval_dtor(return_value); +		switch (extension) { +			case MYSQLND_MYSQLI: +				RETVAL_NULL(); +				break; +			case MYSQLND_MYSQL: +				RETVAL_FALSE; +				break; +			default:exit(0); +		} +	} +	/* +	  return_value is IS_NULL for no more data and an array for data. Thus it's ok +	  to return here. +	*/ +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_all */ +static void +MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, +									   zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +{ +	zval  *row; +	ulong i = 0; + +	DBG_ENTER("mysqlnd_res::fetch_all"); +	DBG_INF_FMT("flags=%u", flags); + +	/* mysqlnd_res::fetch_all works with buffered resultsets only */ +	if (result->conn || !result->data || +		!result->data->row_count || !result->data->data_cursor || +		result->data->data_cursor >= result->data->data + result->data->row_count) +	{ +		RETVAL_NULL(); +		DBG_VOID_RETURN; +	}	 + +	mysqlnd_array_init(return_value, (uint) result->data->row_count); + +	while (result->data->data_cursor && +		   (result->data->data_cursor - result->data->data) < result->data->row_count) +	{ +		MAKE_STD_ZVAL(row); +		mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI); +		add_index_zval(return_value, i++, row); +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_res::fetch_into */ +static void +MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int offset, +											  zval *return_value TSRMLS_DC) +{ +	zval row; +	zval **entry; +	uint i = 0; + +	DBG_ENTER("mysqlnd_res::fetch_field_data"); +	DBG_INF_FMT("offset=%u", offset); + +	if (!result->m.fetch_row) { +		RETVAL_NULL(); +		DBG_VOID_RETURN; +	} +	/* +	  Hint Zend how many elements we will have in the hash. Thus it won't +	  extend and rehash the hash constantly. +	*/ +	INIT_PZVAL(&row); +	mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL); +	if (Z_TYPE(row) != IS_ARRAY) { +		zval_dtor(&row); +		RETVAL_NULL(); +		DBG_VOID_RETURN; +	} +	zend_hash_internal_pointer_reset(Z_ARRVAL(row)); +	while (i++ < offset) { +		zend_hash_move_forward(Z_ARRVAL(row)); +		zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry); +	} +	 +	zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry); + +	*return_value = **entry; +	zval_copy_ctor(return_value); +	ZVAL_REFCOUNT(return_value) = 1; +	zval_dtor(&row); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_result_init */ +MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC) +{ +	MYSQLND_RES *ret = mnd_ecalloc(1, sizeof(MYSQLND_RES)); + +	DBG_ENTER("mysqlnd_result_init"); +	DBG_INF_FMT("field_count=%u cache=%p", field_count, cache); + +	ret->field_count	= field_count; +	ret->zval_cache		= cache; + +	ret->m.use_result	= MYSQLND_METHOD(mysqlnd_res, use_result); +	ret->m.store_result	= MYSQLND_METHOD(mysqlnd_res, store_result); +	ret->m.free_result	= MYSQLND_METHOD(mysqlnd_res, free_result); +	ret->m.seek_data	= MYSQLND_METHOD(mysqlnd_res, data_seek); +	ret->m.num_rows		= MYSQLND_METHOD(mysqlnd_res, num_rows); +	ret->m.num_fields	= MYSQLND_METHOD(mysqlnd_res, num_fields); +	ret->m.fetch_into	= MYSQLND_METHOD(mysqlnd_res, fetch_into); +	ret->m.fetch_all	= MYSQLND_METHOD(mysqlnd_res, fetch_all); +	ret->m.fetch_field_data	= MYSQLND_METHOD(mysqlnd_res, fetch_field_data); +	ret->m.seek_field	= MYSQLND_METHOD(mysqlnd_res, field_seek); +	ret->m.field_tell	= MYSQLND_METHOD(mysqlnd_res, field_tell); +	ret->m.fetch_field	= MYSQLND_METHOD(mysqlnd_res, fetch_field); +	ret->m.fetch_field_direct = MYSQLND_METHOD(mysqlnd_res, fetch_field_direct); + +	ret->m.skip_result	= MYSQLND_METHOD(mysqlnd_res, skip_result); +	ret->m.free_result_buffers	= MYSQLND_METHOD(mysqlnd_res, free_result_buffers); +	ret->m.free_result_internal = mysqlnd_internal_free_result; +	ret->m.free_result_contents = mysqlnd_internal_free_result_contents; + +	ret->m.read_result_metadata = MYSQLND_METHOD(mysqlnd_res, read_result_metadata); +	ret->m.fetch_row_normal_buffered	= mysqlnd_fetch_row_buffered; +	ret->m.fetch_row_normal_unbuffered	= mysqlnd_fetch_row_unbuffered; + +	DBG_RETURN(ret); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h new file mode 100644 index 0000000000..eec92becff --- /dev/null +++ b/ext/mysqlnd/mysqlnd_result.h @@ -0,0 +1,48 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_RESULT_H +#define MYSQLND_RESULT_H + +MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC); + +void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC); + +enum_func_status +mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, +								MYSQLND_RES_METADATA *meta, +								zend_bool binary_protocol, +								zend_bool update_max_length, +								zend_bool to_cache TSRMLS_DC); + +enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC); + +#endif /* MYSQLND_RESULT_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c new file mode 100644 index 0000000000..bd80ae24b2 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_result_meta.c @@ -0,0 +1,440 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_result.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_debug.h" +#include "ext/standard/basic_functions.h" + + +/* {{{ php_mysqlnd_free_field_metadata */ +static +void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC) +{ +	if (meta) { +		if (meta->root) { +			mnd_pefree(meta->root, persistent); +			meta->root = NULL; +		} +		if (meta->def) { +			mnd_pefree(meta->def, persistent); +			meta->def = NULL; +		} +	} +} +/* }}} */ + + +/* {{{ mysqlnd_handle_numeric */ +/* +  The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c +  and modified for the needs of mysqlnd. +*/ +static +zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx) +{ +	register char *tmp=key; + +	if (*tmp=='-') { +		tmp++; +	} +	if ((*tmp>='0' && *tmp<='9')) { +		do { /* possibly a numeric index */ +			char *end=key+length-1; + +			if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */ +				break; +			} +			while (tmp<end) { +				if (!(*tmp>='0' && *tmp<='9')) { +					break; +				} +				tmp++; +			} +			if (tmp==end && *tmp=='\0') { /* a numeric index */ +				if (*key=='-') { +					*idx = strtol(key, NULL, 10); +					if (*idx!=LONG_MIN) { +						return TRUE; +					} +				} else { +					*idx = strtol(key, NULL, 10); +					if (*idx!=LONG_MAX) { +						return TRUE; +					} +				} +			} +		} while (0); +	} +	return FALSE; +} +/* }}} */ + + +#if PHP_MAJOR_VERSION >= 6 +/* {{{ mysqlnd_unicode_is_key_numeric */ +static +zend_bool mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx) +{ +	register UChar *tmp=key; + +	if (*tmp==0x2D /*'-'*/) { +		tmp++; +	} +	if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric index */ +		do { +			UChar *end=key+length-1; + +			if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros */ +				break; +			} +			while (tmp<end) { +				if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { +					break; +				} +				tmp++; +			} +			if (tmp==end && *tmp==0) { /* a numeric index */ +				if (*key==0x2D /*'-'*/) { +					*idx = zend_u_strtol(key, NULL, 10); +					if (*idx!=LONG_MIN) { +						return TRUE; +					} +				} else { +					*idx = zend_u_strtol(key, NULL, 10); +					if (*idx!=LONG_MAX) { +						return TRUE; +					} +				} +			} +		} while (0); +	} +	return FALSE; +} +/* }}} */ +#endif + + +/* {{{ mysqlnd_res_meta::read_metadata */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, +												MYSQLND *conn TSRMLS_DC) +{ +	int i = 0; +	php_mysql_packet_res_field field_packet; + +	DBG_ENTER("mysqlnd_res_meta::read_metadata"); + +	PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); +	for (;i < meta->field_count; i++) { +		long idx; + +		if (meta->fields[i].root) { +			/* We re-read metadata for PS */ +			mnd_efree(meta->fields[i].root); +			meta->fields[i].root = NULL; +		} + +		field_packet.metadata = &(meta->fields[i]); +		if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) { +			PACKET_FREE_ALLOCA(field_packet); +			DBG_RETURN(FAIL); +		} +		if (field_packet.stupid_list_fields_eof == TRUE) { +			break; +		} + +		if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) { +			DBG_ERR_FMT("Unknown type %d sent by the server.  Please send a report to the developers", +						meta->fields[i].type); +			php_error_docref(NULL TSRMLS_CC, E_WARNING, +							 "Unknown type %d sent by the server. " +							 "Please send a report to the developers", +							 meta->fields[i].type); +			PACKET_FREE_ALLOCA(field_packet); +			DBG_RETURN(FAIL); +		} +		if (meta->fields[i].type == MYSQL_TYPE_BIT) { +			size_t field_len; +			DBG_INF("BIT"); +			++meta->bit_fields_count; +			/* .length is in bits */ +			field_len = meta->fields[i].length / 8; +			/* +			  If there is rest, add one byte : +			  8 bits = 1 byte but 9 bits = 2 bytes +			*/ +			if (meta->fields[i].length % 8) { +				++field_len; +			} +			switch (field_len) { +				case 8: +				case 7: +				case 6: +				case 5: +					meta->bit_fields_total_len += 20;/* 21 digis, no sign*/ +					break; +				case 4: +					meta->bit_fields_total_len += 10;/* 2 000 000 000*/ +					break; +				case 3: +					meta->bit_fields_total_len += 8;/*  12 000 000*/ +					break; +				case 2: +					meta->bit_fields_total_len += 5;/* 32 500 */ +					break; +				case 1: +					meta->bit_fields_total_len += 3;/* 120 */ +					break; +			} +			 +		} + +#if PHP_MAJOR_VERSION >= 6 +		if (UG(unicode)) { +			UChar *ustr; +			int ulen; +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, +								   meta->fields[i].name, +								   meta->fields[i].name_length TSRMLS_CC); +			if ((meta->zend_hash_keys[i].is_numeric = +				 			mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx))) +			{ +				meta->zend_hash_keys[i].key = idx; +				mnd_efree(ustr); +			} else { +				meta->zend_hash_keys[i].ustr.u = ustr; +				meta->zend_hash_keys[i].ulen = ulen; +				meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1); +			} + +		} else  +#endif +		{ +			/* For BC we have to check whether the key is numeric and use it like this */ +			if ((meta->zend_hash_keys[i].is_numeric = +						mysqlnd_is_key_numeric(field_packet.metadata->name, +											   field_packet.metadata->name_length + 1, +											   &idx))) +			{ +				meta->zend_hash_keys[i].key = idx; +			} else { +				meta->zend_hash_keys[i].key = +						zend_get_hash_value(field_packet.metadata->name, +											field_packet.metadata->name_length + 1); +			} +		} +	} +	PACKET_FREE_ALLOCA(field_packet); + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::free */ +static void +MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC) +{ +	int i; +	MYSQLND_FIELD *fields; + +	DBG_ENTER("mysqlnd_res_meta::free"); +	DBG_INF_FMT("persistent=%d", persistent); + +	if ((fields = meta->fields)) { +		DBG_INF("Freeing fields metadata"); +		i = meta->field_count; +		while (i--) { +			php_mysqlnd_free_field_metadata(fields++, persistent TSRMLS_CC); +		} +		mnd_pefree(meta->fields, persistent); +		meta->fields = NULL; +	} + +	if (meta->zend_hash_keys) { +		DBG_INF("Freeing zend_hash_keys"); +#if PHP_MAJOR_VERSION >= 6 +		if (UG(unicode)) { +			for (i = 0; i < meta->field_count; i++) { +				if (meta->zend_hash_keys[i].ustr.v) { +					mnd_pefree(meta->zend_hash_keys[i].ustr.v, persistent); +				} +			} +		} +#endif +		mnd_pefree(meta->zend_hash_keys, persistent); +		meta->zend_hash_keys = NULL; +	} +	DBG_INF("Freeing metadata structure"); +	mnd_pefree(meta, persistent); + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_res::clone_metadata */ +static MYSQLND_RES_METADATA * +MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, +												 zend_bool persistent TSRMLS_DC) +{ +	unsigned int i; +	/* +1 is to have empty marker at the end */ +	MYSQLND_RES_METADATA *new_meta = mnd_pemalloc(sizeof(MYSQLND_RES_METADATA), persistent); +	MYSQLND_FIELD *new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); +	MYSQLND_FIELD *orig_fields = meta->fields; +	size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key); + +	DBG_ENTER("mysqlnd_res_meta::clone_metadata"); +	DBG_INF_FMT("persistent=%d", persistent); + +	new_meta->zend_hash_keys = mnd_pemalloc(len, persistent); +	memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len); +	new_meta->m = meta->m; + +	/* +	  This will copy also the strings and the root, which we will have +	  to adjust in the loop +	*/ +	memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD)); +	for (i = 0; i < meta->field_count; i++) { +		/* First copy the root, then field by field adjust the pointers */ +		new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent); +		memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len); + +		if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) { +			new_fields[i].name = new_fields[i].root + +								 (orig_fields[i].name - orig_fields[i].root); +		} +		if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) { +			new_fields[i].org_name = new_fields[i].root + +									 (orig_fields[i].org_name - orig_fields[i].root); +		} +		if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) { +			new_fields[i].table	= new_fields[i].root + +								  (orig_fields[i].table - orig_fields[i].root); +		} +		if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) { +			new_fields[i].org_table	= new_fields[i].root + +									  (orig_fields[i].org_table - orig_fields[i].root); +		} +		if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) { +			new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root); +		} +		if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) { +			new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root); +		} +		/* def is not on the root, if allocated at all */ +		if (orig_fields[i].def) { +			new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent); +			/* copy the trailing \0 too */ +			memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1); +		} +#if PHP_MAJOR_VERSION >= 6 +		if (new_meta->zend_hash_keys[i].ustr.u) { +			new_meta->zend_hash_keys[i].ustr.u = +					eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen); +		} +#endif +	} +	new_meta->current_field = 0; +	new_meta->field_count = meta->field_count; + +	new_meta->fields = new_fields; + +	DBG_RETURN(new_meta); +} +/* }}} */ + +/* {{{ mysqlnd_res_meta::fetch_field */ +static MYSQLND_FIELD * +MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res_meta::fetch_field"); +	if (meta->current_field >= meta->field_count) { +		DBG_RETURN(NULL); +	} +	DBG_RETURN(&meta->fields[meta->current_field++]); +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::fetch_field_direct */ +static MYSQLND_FIELD * +MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, +													 MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) +{ +	DBG_ENTER("mysqlnd_res_meta::fetch_field_direct"); +	DBG_INF_FMT("fieldnr=%d", fieldnr); +	DBG_RETURN(&meta->fields[fieldnr]); +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::field_tell */ +static MYSQLND_FIELD_OFFSET +MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta) +{ +	return meta->current_field; +} +/* }}} */ + + +MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta) +	MYSQLND_METHOD(mysqlnd_res_meta, fetch_field), +	MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct), +	MYSQLND_METHOD(mysqlnd_res_meta, field_tell), +	MYSQLND_METHOD(mysqlnd_res_meta, read_metadata), +	MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata), +	MYSQLND_METHOD(mysqlnd_res_meta, free), +MYSQLND_CLASS_METHODS_END; + + +/* {{{ mysqlnd_result_meta_init */ +MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC) +{ +	MYSQLND_RES_METADATA *ret; +	DBG_ENTER("mysqlnd_result_meta_init"); + +	/* +1 is to have empty marker at the end */ +	ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA)); +	ret->field_count = field_count; +	ret->fields = ecalloc(field_count + 1, sizeof(MYSQLND_FIELD)); +	ret->zend_hash_keys = ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key)); + +	ret->m = & mysqlnd_mysqlnd_res_meta_methods; +	DBG_INF_FMT("meta=%p", ret); +	DBG_RETURN(ret); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h new file mode 100644 index 0000000000..f1bfaee681 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_result_meta.h @@ -0,0 +1,40 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_RESULT_META_H +#define MYSQLND_RESULT_META_H + + +MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC); + + + +#endif /* MYSQLND_RESULT_META_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c new file mode 100644 index 0000000000..66d9bacd44 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_statistics.c @@ -0,0 +1,155 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_statistics.h" +#include "mysqlnd_debug.h" + + +#define STR_W_LEN(str)  str, (sizeof(str) - 1) + +/* {{{ mysqlnd_stats_values_names  + */ +const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = +{ +	{ STR_W_LEN("bytes_sent") }, +	{ STR_W_LEN("bytes_received") },  +	{ STR_W_LEN("packets_sent") }, +	{ STR_W_LEN("packets_received") }, +	{ STR_W_LEN("protocol_overhead_in") }, +	{ STR_W_LEN("protocol_overhead_out") }, +	{ STR_W_LEN("result_set_queries") }, +	{ STR_W_LEN("non_result_set_queries") }, +	{ STR_W_LEN("no_index_used") }, +	{ STR_W_LEN("bad_index_used") }, +	{ STR_W_LEN("buffered_sets") }, +	{ STR_W_LEN("unbuffered_sets") }, +	{ STR_W_LEN("ps_buffered_sets") }, +	{ STR_W_LEN("ps_unbuffered_sets") }, +	{ STR_W_LEN("flushed_normal_sets") }, +	{ STR_W_LEN("flushed_ps_sets") }, +	{ STR_W_LEN("ps_prepared_never_executed") }, +	{ STR_W_LEN("ps_prepared_once_executed") }, +	{ STR_W_LEN("rows_fetched_from_server_normal") }, +	{ STR_W_LEN("rows_fetched_from_server_ps") }, +	{ STR_W_LEN("rows_buffered_from_client_normal") }, +	{ STR_W_LEN("rows_buffered_from_client_ps") }, +	{ STR_W_LEN("rows_fetched_from_client_normal_buffered") }, +	{ STR_W_LEN("rows_fetched_from_client_normal_unbuffered") }, +	{ STR_W_LEN("rows_fetched_from_client_ps_buffered") }, +	{ STR_W_LEN("rows_fetched_from_client_ps_unbuffered") }, +	{ STR_W_LEN("rows_fetched_from_client_ps_cursor") }, +	{ STR_W_LEN("rows_skipped_normal") }, +	{ STR_W_LEN("rows_skipped_ps") }, +	{ STR_W_LEN("copy_on_write_saved") }, +	{ STR_W_LEN("copy_on_write_performed") }, +	{ STR_W_LEN("command_buffer_too_small") }, +	{ STR_W_LEN("connect_success") }, +	{ STR_W_LEN("connect_failure") }, +	{ STR_W_LEN("connection_reused") }, +	{ STR_W_LEN("reconnect") }, +	{ STR_W_LEN("pconnect_success") }, +	{ STR_W_LEN("active_connections") }, +	{ STR_W_LEN("active_persistent_connections") }, +	{ STR_W_LEN("explicit_close") }, +	{ STR_W_LEN("implicit_close") }, +	{ STR_W_LEN("disconnect_close") }, +	{ STR_W_LEN("in_middle_of_command_close") }, +	{ STR_W_LEN("explicit_free_result") }, +	{ STR_W_LEN("implicit_free_result") }, +	{ STR_W_LEN("explicit_stmt_close") }, +	{ STR_W_LEN("implicit_stmt_close") }, +	{ STR_W_LEN("mem_emalloc_count") }, +	{ STR_W_LEN("mem_emalloc_ammount") }, +	{ STR_W_LEN("mem_ecalloc_count") }, +	{ STR_W_LEN("mem_ecalloc_ammount") }, +	{ STR_W_LEN("mem_erealloc_count") }, +	{ STR_W_LEN("mem_erealloc_ammount") }, +	{ STR_W_LEN("mem_efree_count") }, +	{ STR_W_LEN("mem_malloc_count") }, +	{ STR_W_LEN("mem_malloc_ammount") }, +	{ STR_W_LEN("mem_calloc_count") }, +	{ STR_W_LEN("mem_calloc_ammount") }, +	{ STR_W_LEN("mem_realloc_calloc") }, +	{ STR_W_LEN("mem_realloc_ammount") }, +	{ STR_W_LEN("mem_free_count") } +}; +/* }}} */ + + +/* {{{ mysqlnd_fill_stats_hash */ +void +mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +{ +	unsigned int i; + +	mysqlnd_array_init(return_value, STAT_LAST); +	for (i = 0; i < STAT_LAST; i++) { +		char tmp[22]; +		 +		sprintf((char *)&tmp, MYSQLND_LLU_SPEC, stats->values[i]); +#if PHP_MAJOR_VERSION >= 6 +		if (UG(unicode)) { +			UChar *ustr, *tstr; +			int ulen, tlen; + +			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, mysqlnd_stats_values_names[i].s, +									mysqlnd_stats_values_names[i].l + 1 TSRMLS_CC); +			zend_string_to_unicode(UG(utf8_conv), &tstr, &tlen, tmp, strlen(tmp) + 1 TSRMLS_CC); +			add_u_assoc_unicode_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen, tstr, 1); +			efree(ustr); +			efree(tstr); +		} else +#endif		 +		{	 +			add_assoc_string_ex(return_value, mysqlnd_stats_values_names[i].s, +								mysqlnd_stats_values_names[i].l + 1, tmp, 1); +		} +	} +} +/* }}} */ + + +/* {{{ _mysqlnd_get_client_stats */ +PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +{ +	MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats; +	DBG_ENTER("_mysqlnd_get_client_stats"); +	if (!stats_ptr) { +		memset(&stats, 0, sizeof(stats)); +		stats_ptr = &stats; +	} +	mysqlnd_fill_stats_hash(stats_ptr, return_value TSRMLS_CC ZEND_FILE_LINE_CC); +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h new file mode 100644 index 0000000000..2122ac2411 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_statistics.h @@ -0,0 +1,209 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_STATISTICS_H +#define MYSQLND_STATISTICS_H + + +extern MYSQLND_STATS *mysqlnd_global_stats; + +typedef struct st_mysqlnd_string +{ +	char	*s; +	size_t	l; +} MYSQLND_STRING; + +extern const MYSQLND_STRING mysqlnd_stats_values_names[]; + +#ifdef ZTS + +#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ + 		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic)]++; \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +	}\ + } + +#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global stats increase w value [%s] [%s]", mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \ + 		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic1)] += (value1); \ +		mysqlnd_global_stats->values[(statistic2)] += (value2); \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +	}\ + } + +#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global&conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \ + 		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic)]--; \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \ +		} \ +	}\ + } + +#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ + 		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic)]++; \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \ +		} \ +	}\ + } + +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		my_uint64 v = (my_uint64) (value); \ +		DBG_INF_FMT("Global&Conn stat increase w value [%s]", mysqlnd_stats_values_names[statistic]); \ +		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic)] += v; \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)]+= v; \ +		} \ +	}\ + } + +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		my_uint64 v1 = (my_uint64) (value1); \ +		my_uint64 v2 = (my_uint64) (value2); \ +		my_uint64 v3 = (my_uint64) (value3); \ +								 \ +		tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ +		mysqlnd_global_stats->values[(statistic1)]+= v1; \ +		mysqlnd_global_stats->values[(statistic2)]+= v2; \ +		mysqlnd_global_stats->values[(statistic3)]+= v3; \ +		tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \ +		} \ +	} \ + } + + +#else /* NON-ZTS */ + +#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ +		mysqlnd_global_stats->values[(statistic)]++; \ +	} \ + } + +#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global stats increase w value [%s] [%s]", \ +			mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \ +		mysqlnd_global_stats->values[(statistic1)] += (value1); \ +		mysqlnd_global_stats->values[(statistic2)] += (value2); \ +	}\ + } + + +#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global&Conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \ +		mysqlnd_global_stats->values[(statistic)]--; \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \ +		} \ +	} \ + } + +#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ +		mysqlnd_global_stats->values[(statistic)]++; \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \ +		} \ +	} \ + } + +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ + { \ +	my_uint64 v = (my_uint64) (value); \ +	DBG_INF_FMT("Global&Conn stats increase w value [%s]", mysqlnd_stats_values_names[statistic]); \ + 	if (MYSQLND_G(collect_statistics)) { \ +		mysqlnd_global_stats->values[(statistic)] += v; \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic)] += v; \ +		} \ +	} \ + }  + +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ + { \ + 	if (MYSQLND_G(collect_statistics)) { \ +		my_uint64 v1 = (my_uint64) (value1); \ +		my_uint64 v2 = (my_uint64) (value2); \ +		my_uint64 v3 = (my_uint64) (value3); \ +								\ +		mysqlnd_global_stats->values[(statistic1)]+= v1; \ +		mysqlnd_global_stats->values[(statistic2)]+= v2; \ +		mysqlnd_global_stats->values[(statistic3)]+= v3; \ +		if ((conn_stats)) { \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \ +			((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \ +		} \ +	} \ + } + +#endif + +void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value +							 TSRMLS_DC ZEND_FILE_LINE_DC); + +#endif	/* MYSQLND_STATISTICS_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h new file mode 100644 index 0000000000..31367d53c9 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -0,0 +1,540 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_STRUCTS_H +#define MYSQLND_STRUCTS_H + +typedef struct st_mysqlnd_cmd_buffer +{ +	zend_uchar		*buffer; +	size_t			length; +} MYSQLND_CMD_BUFFER; + + +typedef struct st_mysqlnd_field +{ +	char *name;					/* Name of column */ +	char *org_name;				/* Original column name, if an alias */ +	char *table;				/* Table of column if column was a field */ +	char *org_table;			/* Org table name, if table was an alias */ +	char *db;					/* Database for table */ +	char *catalog;				/* Catalog for table */ +	char *def;					/* Default value (set by mysql_list_fields) */ +	unsigned long length;		/* Width of column (create length) */ +	unsigned long max_length;	/* Max width for selected set */ +	unsigned int name_length; +	unsigned int org_name_length; +	unsigned int table_length; +	unsigned int org_table_length; +	unsigned int db_length; +	unsigned int catalog_length; +	unsigned int def_length; +	unsigned int flags;			/* Diverse flags */ +	unsigned int decimals;		/* Number of decimals in field */ +	unsigned int charsetnr;		/* Character set */ +	enum mysqlnd_field_types type;	/* Type of field. See mysql_com.h for types */ +	char *root; +	size_t root_len; +} MYSQLND_FIELD; + + + +typedef struct st_mysqlnd_upsert_result +{ +	unsigned int		warning_count; +	unsigned int		server_status; +	mynd_ulonglong		affected_rows; +	mynd_ulonglong		last_insert_id; +} mysqlnd_upsert_status; + + +typedef struct st_mysqlnd_error_info +{ +	char error[MYSQLND_ERRMSG_SIZE+1]; +	char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; +	unsigned int error_no; +} mysqlnd_error_info; + + +typedef struct st_mysqlnd_zval_pcache			MYSQLND_ZVAL_PCACHE; +typedef struct st_mysqlnd_thread_zval_pcache	MYSQLND_THD_ZVAL_PCACHE; +typedef struct st_mysqlnd_qcache				MYSQLND_QCACHE; + + +typedef struct st_mysqlnd_infile_info +{ +	php_stream			*fd; +	int					error_no; +	char				error_msg[MYSQLND_ERRMSG_SIZE + 1]; +	const char			*filename; +} MYSQLND_INFILE_INFO; + + +/* character set information */ +typedef struct st_mysqlnd_charset +{ +	uint	nr; +	char	*name; +	char	*collation; +	uint	char_minlen; +	uint	char_maxlen; +	uint	dangerous_for_escape_backslash; +	uint 	(*mb_charlen)(uint c); +	uint 	(*mb_valid)(const char *start, const char *end); +} MYSQLND_CHARSET; + + +/* local infile handler */ +typedef struct st_mysqlnd_infile +{ +	int		(*local_infile_init)(void **ptr, char *filename, void **userdata TSRMLS_DC); +	int		(*local_infile_read)(void *ptr, char *buf, uint buf_len TSRMLS_DC); +	int		(*local_infile_error)(void *ptr, char *error_msg, uint error_msg_len TSRMLS_DC); +	void	(*local_infile_end)(void *ptr TSRMLS_DC); +	zval	*callback; +	void	*userdata; +} MYSQLND_INFILE; + +typedef struct st_mysqlnd_option +{ +	/* timeouts */ +	uint		timeout_connect; +	uint		timeout_read; +	uint		timeout_write; + +	ulong		flags; + +	/* init commands - we need to send them to server directly after connect */ +	uint		num_commands; +	char		**init_commands; + +	/* configuration file information */ +	char 		*cfg_file; +	char		*cfg_section; + +	/* SSL information */ +	char		*ssl_key; +	char		*ssl_cert; +	char		*ssl_ca; +	char		*ssl_capath; +	char		*ssl_cipher; +	zend_bool	use_ssl; + +	char 		*charset_name; +	/* maximum allowed packet size for communication */ +	ulong		max_allowed_packet; + +	zend_bool	numeric_and_datetime_as_unicode; +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +	zend_bool	int_and_year_as_int; +#endif +	unsigned int net_read_buffer_size; +} MYSQLND_OPTION; + + +typedef struct st_mysqlnd_connection MYSQLND; +typedef struct st_mysqlnd_res	MYSQLND_RES; +typedef char** 					MYSQLND_ROW;		/* return data as array of strings */ +typedef struct st_mysqlnd_stmt	MYSQLND_STMT; +typedef unsigned int			MYSQLND_FIELD_OFFSET; + +typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND; + +typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND; + +typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA; +typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED; +typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED; + +typedef struct st_mysqlnd_debug MYSQLND_DEBUG; + + +typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); +typedef enum_func_status  (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, +													void *param, +													unsigned int flags, +													zend_bool *fetched_anything +													TSRMLS_DC); + +typedef struct st_mysqlnd_stats +{ +	my_uint64	values[STAT_LAST]; +#ifdef ZTS +	MUTEX_T		LOCK_access; +#endif +} MYSQLND_STATS; + + +typedef struct st_mysqlnd_net +{ +	php_stream		*stream; +	/* sequence for simple checking of correct packets */ +	zend_uchar		packet_no; + +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND +	zend_uchar		last_command; +#endif + +	/* cmd buffer */ +	MYSQLND_CMD_BUFFER	cmd_buffer; +} MYSQLND_NET; + + +struct st_mysqlnd_conn_methods +{ +	ulong				(*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC); +	enum_func_status	(*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); +	enum_func_status	(*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); +	MYSQLND_RES *		(*use_result)(MYSQLND * const conn TSRMLS_DC); +	MYSQLND_RES *		(*store_result)(MYSQLND * const conn TSRMLS_DC); +	enum_func_status	(*next_result)(MYSQLND * const conn TSRMLS_DC); +	zend_bool			(*more_results)(const MYSQLND * const conn); + +	MYSQLND_STMT *		(*stmt_init)(MYSQLND * const conn TSRMLS_DC); + +	enum_func_status	(*shutdown_server)(MYSQLND * const conn, unsigned long level TSRMLS_DC); +	enum_func_status	(*refresh_server)(MYSQLND * const conn, unsigned long options TSRMLS_DC); + +	enum_func_status	(*ping)(MYSQLND * const conn TSRMLS_DC); +	enum_func_status	(*kill_connection)(MYSQLND *conn, unsigned int pid TSRMLS_DC); +	enum_func_status	(*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); +	enum_func_status	(*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); +	enum_func_status	(*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db TSRMLS_DC); + +	unsigned int		(*get_error_no)(const MYSQLND * const conn); +	const char *		(*get_error_str)(const MYSQLND * const conn); +	const char *		(*get_sqlstate)(const MYSQLND * const conn); +	mynd_ulonglong		(*get_thread_id)(const MYSQLND * const conn); +	void				(*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); + +	unsigned long		(*get_server_version)(const MYSQLND * const conn); +	const char *		(*get_server_information)(const MYSQLND * const conn); +	enum_func_status	(*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); +	const char *		(*get_host_information)(const MYSQLND * const conn); +	unsigned int		(*get_protocol_information)(const MYSQLND * const conn); +	const char *		(*get_last_message)(const MYSQLND * const conn); +	const char *		(*charset_name)(const MYSQLND * const conn); +	MYSQLND_RES *		(*list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC); +	MYSQLND_RES *		(*list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC); + +	mynd_ulonglong		(*get_last_insert_id)(const MYSQLND * const conn); +	mynd_ulonglong		(*get_affected_rows)(const MYSQLND * const conn); +	unsigned int		(*get_warning_count)(const MYSQLND * const conn); + +	unsigned int		(*get_field_count)(const MYSQLND * const conn); + +	enum_func_status	(*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); +	enum_func_status	(*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC); +	void				(*free_contents)(MYSQLND *conn TSRMLS_DC);	/* private */ +	enum_func_status	(*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); +	void				(*dtor)(MYSQLND *conn TSRMLS_DC);	/* private */ + +	MYSQLND *			(*get_reference)(MYSQLND * const conn); +	enum_func_status	(*free_reference)(MYSQLND * const conn TSRMLS_DC); +}; + + +struct st_mysqlnd_res_methods +{ +	mysqlnd_fetch_row_func	fetch_row; +	mysqlnd_fetch_row_func	fetch_row_normal_buffered; /* private */ +	mysqlnd_fetch_row_func	fetch_row_normal_unbuffered; /* private */ + +	MYSQLND_RES *		(*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); +	MYSQLND_RES *		(*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); +	void 				(*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); +	void 				(*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); +	void 				(*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); +	mynd_ulonglong		(*num_rows)(const MYSQLND_RES * const result); +	unsigned int		(*num_fields)(const MYSQLND_RES * const result); +	enum_func_status	(*skip_result)(MYSQLND_RES * const result TSRMLS_DC); +	enum_func_status	(*seek_data)(MYSQLND_RES * result, mynd_ulonglong row TSRMLS_DC); +	MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset); +	MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result); +	MYSQLND_FIELD *		(*fetch_field)(MYSQLND_RES * const result TSRMLS_DC); +	MYSQLND_FIELD *		(*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); + +	enum_func_status	(*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC); +	unsigned long *		(*fetch_lengths)(MYSQLND_RES * const result); +	void				(*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC);	/* private */ +	enum_func_status	(*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); +	void 				(*free_result_internal)(MYSQLND_RES *result TSRMLS_DC); +	void 				(*free_result_contents)(MYSQLND_RES *result TSRMLS_DC); +}; + + +struct st_mysqlnd_res_meta_methods +{ +	MYSQLND_FIELD *			(*fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); +	MYSQLND_FIELD *			(*fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); +	MYSQLND_FIELD_OFFSET	(*field_tell)(const MYSQLND_RES_METADATA * const meta); +	enum_func_status		(*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC); +	MYSQLND_RES_METADATA *	(*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC); +	void					(*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC); +}; + + +struct st_mysqlnd_stmt_methods +{ +	enum_func_status	(*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); +	enum_func_status	(*execute)(MYSQLND_STMT * const stmt TSRMLS_DC); +	MYSQLND_RES *		(*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +	MYSQLND_RES *		(*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +	MYSQLND_RES *		(*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +	enum_func_status	(*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +	enum_func_status	(*seek_data)(const MYSQLND_STMT * const stmt, mynd_ulonglong row TSRMLS_DC); +	enum_func_status	(*reset)(MYSQLND_STMT * const stmt TSRMLS_DC); +	enum_func_status	(*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ +	enum_func_status	(*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ + +	enum_func_status	(*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); + +	enum_func_status	(*bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC); +	enum_func_status	(*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC); +	enum_func_status	(*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, +										  const char * const data, unsigned long length TSRMLS_DC); +	MYSQLND_RES	*		(*get_parameter_metadata)(MYSQLND_STMT * const stmt); +	MYSQLND_RES	*		(*get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); + +	mynd_ulonglong		(*get_last_insert_id)(const MYSQLND_STMT * const stmt); +	mynd_ulonglong		(*get_affected_rows)(const MYSQLND_STMT * const stmt); +	mynd_ulonglong		(*get_num_rows)(const MYSQLND_STMT * const stmt); + +	unsigned int		(*get_param_count)(const MYSQLND_STMT * const stmt); +	unsigned int		(*get_field_count)(const MYSQLND_STMT * const stmt); +	unsigned int		(*get_warning_count)(const MYSQLND_STMT * const stmt); + +	unsigned int		(*get_error_no)(const MYSQLND_STMT * const stmt); +	const char *		(*get_error_str)(const MYSQLND_STMT * const stmt); +	const char *		(*get_sqlstate)(const MYSQLND_STMT * const stmt); + +	enum_func_status	(*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC); +	enum_func_status	(*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC); +}; + + +struct st_mysqlnd_connection +{ +/* Operation related */ +	MYSQLND_NET		net; + +/* Information related */ +	char			*host; +	char			*unix_socket; +	char			*user; +	char			*passwd; +	unsigned int	*passwd_len; +	char			*scheme; +	mynd_ulonglong	thread_id; +	char			*server_version; +	char			*host_info; +	unsigned char	*scramble; +	const MYSQLND_CHARSET *charset; +	const MYSQLND_CHARSET *greet_charset; +	MYSQLND_INFILE	infile; +	unsigned int	protocol_version; +	unsigned long	max_packet_size; +	unsigned int	port; +	unsigned long	client_flag; +	unsigned long	server_capabilities; + +	int				tmp_int; + + +	/* For UPSERT queries */ +	mysqlnd_upsert_status upsert_status; +	char			*last_message; +	unsigned int	last_message_len; + +	/* If error packet, we use these */ +	mysqlnd_error_info	error_info; + +	/* +	  To prevent queries during unbuffered fetches. Also to +	  mark the connection as destroyed for garbage collection. +	*/ +	enum mysqlnd_connection_state	state; +	enum_mysqlnd_query_type			last_query_type; +	/* Temporary storage between query and (use|store)_result() call */ +	MYSQLND_RES						*current_result; + +	/* +	  How many result sets reference this connection. +	  It won't be freed until this number reaches 0. +	  The last one, please close the door! :-) +	  The result set objects can determine by inspecting +	  'quit_sent' whether the connection is still valid. +	*/ +	unsigned int	refcount; + +	/* Temporal storage for mysql_query */ +	unsigned int	field_count; +	 +	/* persistent connection */ +	zend_bool		persistent; + +	/* options */ +	MYSQLND_OPTION	options; + +	/* zval cache */ +	MYSQLND_THD_ZVAL_PCACHE	*zval_cache; + +	/* qcache */ +	MYSQLND_QCACHE	*qcache; + +	/* stats */ +	MYSQLND_STATS	stats; + +	struct st_mysqlnd_conn_methods *m; +}; + +typedef struct st_php_mysql_packet_row php_mysql_packet_row; + + +struct mysqlnd_field_hash_key +{ +	zend_bool		is_numeric; +	unsigned long	key; +#if PHP_MAJOR_VERSION >= 6 +	zstr			ustr; +	unsigned int	ulen; +#endif	 +}; + + +struct st_mysqlnd_result_metadata +{ +	MYSQLND_FIELD					*fields; +	struct mysqlnd_field_hash_key	*zend_hash_keys; +	unsigned int					current_field; +	unsigned int					field_count; +	/* We need this to make fast allocs in rowp_read */ +	unsigned int					bit_fields_count; +	size_t							bit_fields_total_len; /* trailing \0 not counted */ + +	struct st_mysqlnd_res_meta_methods *m; +}; + + +struct st_mysqlnd_buffered_result +{ +	zval				***data; +	zval				***data_cursor; +	zend_uchar			**row_buffers; +	mynd_ulonglong		row_count; +	zend_bool			persistent; + +	MYSQLND_QCACHE		*qcache; +	unsigned int		references; + +	zend_bool			async_invalid; +	mysqlnd_error_info	error_info; +}; + + +struct st_mysqlnd_unbuffered_result +{ +	/* For unbuffered (both normal and PS) */ +	zval				**last_row_data; +	zend_uchar			*last_row_buffer; + +	mynd_ulonglong		row_count; +	zend_bool			eof_reached; +}; + + +struct st_mysqlnd_res +{ +	struct st_mysqlnd_res_methods m; + +	MYSQLND					*conn; +	enum_mysqlnd_res_type	type; +	unsigned int			field_count; + +	/* For metadata functions */ +	MYSQLND_RES_METADATA	*meta; + +	/* To be used with store_result() - both normal and PS */ +	MYSQLND_RES_BUFFERED	*data; + +	MYSQLND_RES_UNBUFFERED	*unbuf; + +	/* +	  Column lengths of current row - both buffered and unbuffered. +	  For buffered results it duplicates the data found in **data  +	*/ +	unsigned long			*lengths; + +	php_mysql_packet_row	*row_packet;	/* Unused for PS */ + +	/* zval cache */ +	MYSQLND_THD_ZVAL_PCACHE		*zval_cache; +}; + + +struct st_mysqlnd_param_bind +{ +	zval		*zv; +	zend_uchar	type; +	enum_param_bind_flags	flags; +}; + +struct st_mysqlnd_result_bind +{ +	zval		*zv; +	zend_uchar	original_type; +	zend_bool	bound; +}; + + +struct st_mysqlnd_stmt +{ +	MYSQLND						*conn; +	unsigned long				stmt_id; +	unsigned long				flags;/* cursor is set here */ +	enum_mysqlnd_stmt_state		state; +	unsigned int				warning_count; +	MYSQLND_RES					*result; +	unsigned int				field_count; +	unsigned int				param_count; +	unsigned char				send_types_to_server; +	MYSQLND_PARAM_BIND			*param_bind; +	MYSQLND_RESULT_BIND			*result_bind; +	zend_bool					result_zvals_separated_once; + +	mysqlnd_upsert_status		upsert_status; + +	mysqlnd_error_info			error_info; + +	zend_bool					update_max_length; +	unsigned long				prefetch_rows; + +	zend_bool					cursor_exists; +	mysqlnd_stmt_use_or_store_func default_rset_handler; + +	MYSQLND_CMD_BUFFER			cmd_buffer; +	unsigned int				execute_count;/* count how many times the stmt was executed */ + +	struct st_mysqlnd_stmt_methods	*m; +}; + +#endif /* MYSQLND_STRUCTS_H */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c new file mode 100644 index 0000000000..73179281fa --- /dev/null +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -0,0 +1,1956 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ +#include "php.h" +#include "php_globals.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_statistics.h" +#include "mysqlnd_palloc.h" +#include "mysqlnd_debug.h" +#include "ext/standard/sha1.h" +#include "php_network.h" +#include "zend_ini.h" + +#ifndef PHP_WIN32 +#include <netinet/tcp.h> +#else +#include <winsock.h> +#endif + + +#define USE_CORK 0 + +#define MYSQLND_SILENT 1 + +#define MYSQLND_DUMP_HEADER_N_BODY2 +#define MYSQLND_DUMP_HEADER_N_BODY_FULL2 + +#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1) + +#define	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type) \ +	{ \ +		if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\ +			conn->state = CONN_QUIT_SENT; \ +			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\ +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \ +			DBG_ERR_FMT("Can't read %s's header", (packet_type)); \ +			DBG_RETURN(FAIL);\ +		}\ +		if ((buf_size) < (packet)->header.size) { \ +			DBG_ERR_FMT("Packet buffer wasn't big enough %u bytes will be unread", \ +						(packet)->header.size - (buf_size)); \ +		}\ +		if (!mysqlnd_read_body((conn), (buf), \ +							   MIN((buf_size), (packet)->header.size) TSRMLS_CC)) { \ +			conn->state = CONN_QUIT_SENT; \ +			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\ +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \ +			DBG_ERR_FMT("Empty %s packet body", (packet_type)); \ +			DBG_RETURN(FAIL);\ +		} \ +	} + + +extern mysqlnd_packet_methods packet_methods[]; + +static const char *unknown_sqlstate= "HY000"; + +char * const mysqlnd_empty_string = ""; + +/* Used in mysqlnd_debug.c */ +char * mysqlnd_read_header_name	= "mysqlnd_read_header"; +char * mysqlnd_read_body_name		= "mysqlnd_read_body"; + + +/* {{{ mysqlnd_command_to_text  + */ +const char * const mysqlnd_command_to_text[COM_END] = +{ +  "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST", +  "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS", +  "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING", +  "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP", +  "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE", +  "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE", +  "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON" +}; +/* }}} */ + + +/* {{{ php_mysqlnd_net_field_length  +   Get next field's length */ +unsigned long php_mysqlnd_net_field_length(zend_uchar **packet) +{ +	register zend_uchar *p= (zend_uchar *)*packet; + +	if (*p < 251) { +		(*packet)++; +		return (unsigned long) *p; +	} + +	switch (*p) { +		case 251: +			(*packet)++; +			return MYSQLND_NULL_LENGTH; +		case 252: +			(*packet) += 3; +			return (unsigned long) uint2korr(p+1); +		case 253: +			(*packet) += 4; +			return (unsigned long) uint3korr(p+1); +		default: +			(*packet) += 9; +			return (unsigned long) uint4korr(p+1); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_net_field_length_ll  +   Get next field's length */ +mynd_ulonglong php_mysqlnd_net_field_length_ll(zend_uchar **packet) +{ +	register zend_uchar *p= (zend_uchar *)*packet; + +	if (*p < 251) { +		(*packet)++; +		return (mynd_ulonglong) *p; +	} + +	switch (*p) { +		case 251: +			(*packet)++; +			return (mynd_ulonglong) MYSQLND_NULL_LENGTH; +		case 252: +			(*packet) += 3; +			return (mynd_ulonglong) uint2korr(p + 1); +		case 253: +			(*packet) += 4; +			return (mynd_ulonglong) uint3korr(p + 1); +		default: +			(*packet) += 9; +			return (mynd_ulonglong) uint8korr(p + 1); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_net_store_length */ +zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length) +{ +	if (length < (mynd_ulonglong) L64(251)) { +		*packet = (zend_uchar) length; +		return packet + 1; +	} + +	if (length < (mynd_ulonglong) L64(65536)) { +		*packet++ = 252; +		int2store(packet,(uint) length); +		return packet + 2; +	} + +	if (length < (mynd_ulonglong) L64(16777216)) { +		*packet++ = 253; +		int3store(packet,(ulong) length); +		return packet + 3; +	} +	*packet++ = 254; +	int8store(packet, length); +	return packet + 8; +} +/* }}} */ + + +/* {{{ php_mysqlnd_consume_uneaten_data */ +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND +size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC) +{ + +	/* +	  Switch to non-blocking mode and try to consume something from +	  the line, if possible, then continue. This saves us from looking for +	  the actuall place where out-of-order packets have been sent. +	  If someone is completely sure that everything is fine, he can switch it +	  off. +	*/ +	char tmp_buf[256]; +	MYSQLND_NET *net = &conn->net; +	size_t skipped_bytes = 0; +	int opt = PHP_STREAM_OPTION_BLOCKING; +	int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC); + +	DBG_ENTER("php_mysqlnd_consume_uneaten_data"); + +	if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) { +		/* Do a read of 1 byte */ +		int bytes_consumed; + +		do { +			skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf))); +		} while (bytes_consumed == sizeof(tmp_buf)); + +		if (was_blocked) { +			net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC); +		} + +		if (bytes_consumed) { +			DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server", +						bytes_consumed, mysqlnd_command_to_text[net->last_command]); +			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't " +							 "consumed all the output from the server. PID=%d", +							 bytes_consumed, mysqlnd_command_to_text[net->last_command], getpid()); +		} +	} +	net->last_command = cmd; + +	DBG_RETURN(skipped_bytes); +} +#endif +/* }}} */ + + +/* {{{ php_mysqlnd_read_error_from_line */ +static +enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, +												  char *error, int error_buf_len, +												  unsigned int *error_no, char *sqlstate TSRMLS_DC) +{ +	zend_uchar *p = buf; +	int error_msg_len= 0; + +	DBG_ENTER("php_mysqlnd_read_error_from_line"); + +	if (buf_len > 2) { +		*error_no = uint2korr(p); +		p+= 2; +		/* sqlstate is following */ +		if (*p == '#') { +			memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH); +			p+= MYSQLND_SQLSTATE_LENGTH; +		} +		error_msg_len = buf_len - (p - buf); +		error_msg_len = MIN(error_msg_len, error_buf_len - 1); +		memcpy(error, p, error_msg_len); +	} else { +		*error_no = CR_UNKNOWN_ERROR; +		memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH); +	} +	sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0'; +	error[error_msg_len]= '\0'; +	 +	DBG_RETURN(FAIL); +}  +/* }}} */ + + +/* {{{ mysqlnd_set_sock_no_delay */ +int mysqlnd_set_sock_no_delay(php_stream *stream) +{ + +	int socketd = ((php_netstream_data_t*)stream->abstract)->socket; +	int ret = SUCCESS; +	int flag = 1; +	int result = setsockopt(socketd, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int)); +	TSRMLS_FETCH(); + +	DBG_ENTER("mysqlnd_set_sock_no_delay"); + +	if (result == -1) { +		ret = FAILURE; +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */ +#define STORE_HEADER_SIZE(safe_storage, buffer)  int4store((safe_storage), (*(uint32 *)(buffer))) +#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) + +/* {{{ mysqlnd_stream_write_w_header */ +/* +  IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!! +  			  This is done for performance reasons in the caller of this function. +			  Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. +			  Neither are quick, thus the clients of this function are obligated to do +			  what they are asked for. + +  `count` is actually the length of the payload data. Thus : +  count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer) +*/ +size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC) +{ +	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; +	zend_uchar *safe_storage = safe_buf; +	MYSQLND_NET *net = &conn->net; +	size_t old_chunk_size = net->stream->chunk_size; +	size_t ret, left = count, packets_sent = 1; +	zend_uchar *p = (zend_uchar *) buf; + +	DBG_ENTER("mysqlnd_stream_write_w_header"); +	DBG_INF_FMT("conn=%llu count=%lu", conn->thread_id, count); + +	net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; + +	while (left > MYSQLND_MAX_PACKET_SIZE) { +		STORE_HEADER_SIZE(safe_storage, p); +		int3store(p, MYSQLND_MAX_PACKET_SIZE); +		int1store(p + 3, net->packet_no);		 +		net->packet_no++; +		ret = php_stream_write(net->stream, (char *)p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE); +		RESTORE_HEADER_SIZE(p, safe_storage); + +		p += MYSQLND_MAX_PACKET_SIZE; +		left -= MYSQLND_MAX_PACKET_SIZE; + +		packets_sent++; +	} +	/* Even for zero size payload we have to send a packet */ +	STORE_HEADER_SIZE(safe_storage, p); +	int3store(p, left); +	int1store(p + 3, net->packet_no);		 +	net->packet_no++; +	ret = php_stream_write(net->stream, (char *)p, left + MYSQLND_HEADER_SIZE); +	RESTORE_HEADER_SIZE(p, safe_storage); + +	if (!ret) { +		DBG_ERR_FMT("Can't %u send bytes", count); +		conn->state = CONN_QUIT_SENT; +		SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); +	}	 + +	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, +			STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, +			STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, +			STAT_PACKETS_SENT, packets_sent); + +	net->stream->chunk_size = old_chunk_size; + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_stream_write_w_command */ +#if USE_CORK && defined(TCP_CORK) +static +size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum php_mysqlnd_server_command command, +									  const char * const buf, size_t count TSRMLS_DC) +{ +	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; +	zend_uchar *safe_storage = (char *) &safe_buf; +	MYSQLND_NET *net = &conn->net; +	size_t old_chunk_size = net->stream->chunk_size; +	size_t ret, left = count, header_len = MYSQLND_HEADER_SIZE + 1, packets_sent = 1; +	const zend_uchar *p = (zend_uchar *) buf; +	zend_bool command_sent = FALSE; +	int corked = 1; + +	DBG_ENTER("mysqlnd_stream_write_w_command"); + +	net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; + +	setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket, +				IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); + +	int1store(safe_storage + MYSQLND_HEADER_SIZE, command);		 +	while (left > MYSQLND_MAX_PACKET_SIZE) { +		size_t body_size = MYSQLND_MAX_PACKET_SIZE; + +		int3store(safe_storage, MYSQLND_MAX_PACKET_SIZE); +		int1store(safe_storage + 3, net->packet_no);		 +		net->packet_no++; +		 +		ret = php_stream_write(net->stream, (char *)safe_storage, header_len); +		if (command_sent == FALSE) { +			--header_len; +			/* Sent one byte less*/ +			--body_size; +			command_sent = TRUE; +		} + +		ret = php_stream_write(net->stream, (char *)p, body_size); + +		p += body_size; +		left -= body_size; + +		packets_sent++; +	} +	/* Even for zero size payload we have to send a packet */ +	int3store(safe_storage, header_len == MYSQLND_HEADER_SIZE? left:left+1); +	int1store(safe_storage + 3, net->packet_no); +	net->packet_no++; +	 +	ret = php_stream_write(net->stream, (char *)safe_storage, header_len); + +	if (left) { +		ret = php_stream_write(net->stream, (char *)p, left); +	} +	corked = 0; +	setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket, +				IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); + +	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, STAT_BYTES_SENT, +				count + packets_sent * MYSQLND_HEADER_SIZE); +				STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE); +				STAT_PACKETS_SENT, packets_sent); + +	net->stream->chunk_size = old_chunk_size; + +	DBG_RETURN(ret); +} +#endif +/* }}} */ + + +/* {{{ mysqlnd_read_header */ +static enum_func_status +mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC) +{ +	MYSQLND_NET *net = &conn->net; +	char buffer[MYSQLND_HEADER_SIZE]; +	char *p = buffer; +	int to_read = MYSQLND_HEADER_SIZE, ret; + +	DBG_ENTER(mysqlnd_read_header_name); + +	do { +		if (!(ret= php_stream_read(net->stream, p, to_read))) { +			DBG_ERR_FMT("Error while reading header from socket"); +			return FAIL; +		} +		p += ret; +		to_read -= ret; +	} while (to_read); + +	header->size = uint3korr(buffer); +	header->packet_no = uint1korr(buffer + 3); + +	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, +							STAT_BYTES_RECEIVED, MYSQLND_HEADER_SIZE, +							STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE, +							STAT_PACKETS_RECEIVED, 1); + +	if (net->packet_no == header->packet_no) { +		/* +		  Have to increase the number, so we can send correct number back. It will +		  round at 255 as this is unsigned char. The server needs this for simple +		  flow control checking. +		*/ +		net->packet_no++; +#ifdef MYSQLND_DUMP_HEADER_N_BODY +		DBG_ERR_FMT("HEADER: packet_no=%d size=%3d", header->packet_no, header->size); +#endif +		DBG_RETURN(PASS); +	} + +#if !MYSQLND_SILENT +	DBG_ERR_FMT("Packets out of order. Expected %d received %d. Packet size=%d", +				net->packet_no, header->packet_no, header->size); +#endif +	php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d. PID=%d", +			  net->packet_no, header->packet_no, header->size, getpid()); +	DBG_RETURN(FAIL); +} +/* }}} */ + + +/* {{{ mysqlnd_read_body */ +static +size_t mysqlnd_read_body(MYSQLND *conn, zend_uchar *buf, size_t size TSRMLS_DC) +{ +	size_t ret; +	char *p = (char *)buf; +	int iter = 0; +	MYSQLND_NET *net = &conn->net; +	size_t old_chunk_size = net->stream->chunk_size; + +	DBG_ENTER(mysqlnd_read_body_name); +	DBG_INF_FMT("chunk_size=%d", net->stream->chunk_size); + +	net->stream->chunk_size = MIN(size, conn->options.net_read_buffer_size); +	do { +		size -= (ret = php_stream_read(net->stream, p, size)); +		if (size || iter++) { +			DBG_INF_FMT("read=%d buf=%p p=%p chunk_size=%d left=%d", +						ret, buf, p , net->stream->chunk_size, size); +		} +		p += ret; +	} while (size > 0); + +	MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, p - (char*)buf); +	net->stream->chunk_size = old_chunk_size; + +#ifdef MYSQLND_DUMP_HEADER_N_BODY_FULL +	{ +		int i; +		DBG_INF_FMT("BODY: requested=%d last_read=%3d", p - (char*)buf, ret); +		for (i = 0 ; i < p - (char*)buf; i++) { +			if (i && (i % 30 == 0)) { +				printf("\n\t\t"); +			} +			printf("[%c] ", *(char *)(&(buf[i]))); +		} +		for (i = 0 ; i < p - (char*)buf; i++) { +			if (i && (i % 30 == 0)) { +				printf("\n\t\t"); +			} +			printf("%.2X ", (int)*((char*)&(buf[i]))); +		} +		php_printf("\n\t\t\t-=-=-=-=-\n"); +	} +#endif + +	DBG_RETURN(p - (char*)buf); +} +/* }}} */ + + +/* {{{ php_mysqlnd_greet_read */ +static enum_func_status +php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	zend_uchar buf[512]; +	zend_uchar *p= buf; +	zend_uchar *begin = buf; +	php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet; + +	DBG_ENTER("php_mysqlnd_greet_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting"); + +	packet->protocol_version = uint1korr(p); +	p++; + +	if (packet->protocol_version == 0xFF) { +		php_mysqlnd_read_error_from_line(p, packet->header.size - 1, +										 packet->error, sizeof(packet->error), +										 &packet->error_no, packet->sqlstate +										 TSRMLS_CC); +		/* +		  The server doesn't send sqlstate in the greet packet. +		  It's a bug#26426 , so we have to set it correctly ourselves. +		  It's probably "Too many connections, which has SQL state 08004". +		*/ +		if (packet->error_no == 1040) { +			memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH); +		}							  +		DBG_RETURN(PASS); +	} + +	packet->server_version = pestrdup((char *)p, conn->persistent); +	p+= strlen(packet->server_version) + 1; /* eat the '\0' */ + +	packet->thread_id = uint4korr(p); +	p+=4; + +	memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323); +	p+= 8; + +	/* pad1 */ +	p++; + +	packet->server_capabilities = uint2korr(p); +	p+= 2; + +	packet->charset_no = uint1korr(p); +	p++; + +	packet->server_status = uint2korr(p); +	p+= 2; + +	/* pad2 */ +	p+= 13; + +	if (p - buf < packet->header.size) { +		/* scramble_buf is split into two parts */ +		memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323, +		   		p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323); +	} else { +		packet->pre41 = TRUE; +	} +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_greet_free_mem */ +static +void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet; +	if (p->server_version) { +		mnd_efree(p->server_version); +		p->server_version = NULL; +	} +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + +#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \ +				CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \ +				CLIENT_MULTI_RESULTS) + + +/* {{{ php_mysqlnd_crypt */ +static +void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len) +{ +	const unsigned char *s1_end= s1 + len; +	while (s1 < s1_end) { +		*buffer++= *s1++ ^ *s2++; +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_scramble */ +void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password) +{ +	PHP_SHA1_CTX context; +	unsigned char sha1[SHA1_MAX_LENGTH]; +	unsigned char sha2[SHA1_MAX_LENGTH]; +	 + +	/* Phase 1: hash password */ +	PHP_SHA1Init(&context); +	PHP_SHA1Update(&context, password, strlen((char *)password)); +	PHP_SHA1Final(sha1, &context); + +	/* Phase 2: hash sha1 */ +	PHP_SHA1Init(&context); +	PHP_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH); +	PHP_SHA1Final(sha2, &context); + +	/* Phase 3: hash scramble + sha2 */ +	PHP_SHA1Init(&context); +	PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH); +	PHP_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH); +	PHP_SHA1Final(buffer, &context); + +	/* let's crypt buffer now */ +	php_mysqlnd_crypt(buffer, (const unsigned char *)buffer, (const unsigned  char *)sha1, SHA1_MAX_LENGTH); +} +/* }}} */ + + +/* {{{ php_mysqlnd_auth_write */ +static +size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	char buffer[1024]; +	register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */ +	int len; +	register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet; + +	DBG_ENTER("php_mysqlnd_auth_write"); + +	packet->client_flags |= MYSQLND_CAPABILITIES; + +	if (packet->db) { +		packet->client_flags |= CLIENT_CONNECT_WITH_DB; +	} + +	if (PG(open_basedir) && strlen(PG(open_basedir))) { +		packet->client_flags ^= CLIENT_LOCAL_FILES; +	} +	 +	/* don't allow multi_queries via connect parameter */ +	packet->client_flags ^= CLIENT_MULTI_STATEMENTS; +	int4store(p, packet->client_flags); +	p+= 4; + +	int4store(p, packet->max_packet_size); +	p+= 4; + +	int1store(p, packet->charset_no); +	p++; + +	memset(p, 0, 23); /* filler */ +	p+= 23;	 + +	len= strlen(packet->user); +	strncpy(p, packet->user, len); +	p+= len; +	*p++ = '\0'; + +	/* copy scrambled pass*/ +	if (packet->password && packet->password[0]) { +		/* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ +		int1store(p, 20); +		p++; +		php_mysqlnd_scramble((unsigned char*)p, packet->server_scramble_buf, +							 (unsigned char *)packet->password); +		p+= 20; +	} else { +		/* Zero length */ +		int1store(p, 0); +		p++; +	} + +	if (packet->db) { +		memcpy(p, packet->db, packet->db_len); +		p+= packet->db_len; +		*p++= '\0'; +	} +	/* Handle CLIENT_CONNECT_WITH_DB */ +	/* no \0 for no DB */ + +	DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC)); +} +/* }}} */ + +/* {{{ php_mysqlnd_auth_free_mem */ +static +void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	if (!alloca) { +		mnd_efree((php_mysql_packet_auth *) _packet); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_ok_read */ +static enum_func_status +php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	zend_uchar buf[1024]; +	zend_uchar *p = buf; +	zend_uchar *begin = buf; +	int i; +	register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet; + +	DBG_ENTER("php_mysqlnd_ok_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "OK"); + +	/* Should be always 0x0 or 0xFF for error */ +	packet->field_count= uint1korr(p); +	p++; + +	if (0xFF == packet->field_count) { +		php_mysqlnd_read_error_from_line(p, packet->header.size - 1, +										 packet->error, sizeof(packet->error), +										 &packet->error_no, packet->sqlstate +										 TSRMLS_CC); +		DBG_RETURN(PASS); +	} +	/* Everything was fine! */ +	packet->affected_rows  = php_mysqlnd_net_field_length_ll(&p); +	packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p); + +	packet->server_status = uint2korr(p); +	p+= 2; + +	packet->warning_count = uint2korr(p); +	p+= 2; + +	/* There is a message */ +	if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) { +		packet->message = pestrndup((char *)p, MIN(i, sizeof(buf) - (p - buf)), conn->persistent); +		packet->message_len = i; +	} else { +		packet->message = NULL; +	} + +	DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d", +					packet->affected_rows, packet->last_insert_id, packet->server_status, +					packet->warning_count); + +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_ok_free_mem */ +static +void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet; +	if (p->message) { +		mnd_efree(p->message); +		p->message = NULL; +	} +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_eof_read */ +static enum_func_status +php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	/* +	  EOF packet is since 4.1 five bytes long, +	  but we can get also an error, make it bigger. + +	  Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE +	*/ +	php_mysql_packet_eof *packet= (php_mysql_packet_eof *) _packet; +	zend_uchar buf[5 + 10 + sizeof(packet->sqlstate) + sizeof(packet->error)]; +	zend_uchar *p= buf; +	zend_uchar *begin = buf; + +	DBG_ENTER("php_mysqlnd_eof_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "EOF"); + +	/* Should be always 0xFE */ +	packet->field_count= uint1korr(p); +	p++; + +	if (0xFF == packet->field_count) { +		php_mysqlnd_read_error_from_line(p, packet->header.size - 1, +										 packet->error, sizeof(packet->error), +										 &packet->error_no, packet->sqlstate +										 TSRMLS_CC); +		DBG_RETURN(PASS); +	} + +	/* +		4.1 sends 1 byte EOF packet after metadata of +		PREPARE/EXECUTE but 5 bytes after the result. This is not +		according to the Docs@Forge!!! +	*/ +	if (packet->header.size > 1) { +		packet->warning_count = uint2korr(p); +		p+= 2; +		packet->server_status = uint2korr(p); +		p+= 2; +	} else { +		packet->warning_count = 0; +		packet->server_status = 0; +	} + +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} +	 +	DBG_INF_FMT("EOF packet: status=%d warnings=%d", packet->server_status, packet->warning_count); + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_eof_free_mem */ +static +void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	if (!alloca) { +		mnd_efree(_packet); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_cmd_write */ +size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	/* Let's have some space, which we can use, if not enough, we will allocate new buffer */ +	php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet; +	MYSQLND_NET *net = &conn->net; +	unsigned int error_reporting = EG(error_reporting); +	size_t written; + +	DBG_ENTER("php_mysqlnd_cmd_write"); +	/* +	  Reset packet_no, or we will get bad handshake! +	  Every command starts a new TX and packet numbers are reset to 0. +	*/ +	net->packet_no = 0; + +	if (error_reporting) { +		EG(error_reporting) = 0; +	} +	 +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND +	php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC); +#endif + +	if (!packet->argument || !packet->arg_len) { +		char buffer[MYSQLND_HEADER_SIZE + 1]; + +		int1store(buffer + MYSQLND_HEADER_SIZE, packet->command); +		written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC); +	} else { +#if USE_CORK && defined(TCP_CORK) +		written = mysqlnd_stream_write_w_command(conn, packet->command, packet->argument, +												 packet->arg_len TSRMLS_CC)); +#else +		size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret; +		zend_uchar *tmp, *p; +		tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer; +		p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */ + +		int1store(p, packet->command); +		p++; + +		memcpy(p, packet->argument, packet->arg_len); + +		ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC); +		if (tmp != net->cmd_buffer.buffer) { +			MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL); +			mnd_efree(tmp); +		} +		written = ret; +#endif +	} +	if (error_reporting) { +		/* restore error reporting */ +		EG(error_reporting) = error_reporting; +	} +	DBG_RETURN(written); +} +/* }}} */ + + +/* {{{ php_mysqlnd_cmd_free_mem */ +static +void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	if (!alloca) { +		mnd_efree((php_mysql_packet_command *) _packet); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_rset_header_read */ +static enum_func_status +php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	zend_uchar buf[1024]; +	zend_uchar *p = buf; +	zend_uchar *begin = buf; +	size_t len; +	php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet; + +	DBG_ENTER("php_mysqlnd_rset_header_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "resultset header"); + +	/* +	  Don't increment. First byte is 0xFF on error, but otherwise is starting byte +	  of encoded sequence for length. +	*/ +	if (*p == 0xFF) { +		/* Error */ +		p++; +		php_mysqlnd_read_error_from_line(p, packet->header.size - 1, +										 packet->error_info.error, sizeof(packet->error_info.error), +										 &packet->error_info.error_no, packet->error_info.sqlstate +										 TSRMLS_CC); +		DBG_RETURN(PASS); +	} + +	packet->field_count= php_mysqlnd_net_field_length(&p); +	switch (packet->field_count) { +		case MYSQLND_NULL_LENGTH: +			/* +			  First byte in the packet is the field count. +			  Thus, the name is size - 1. And we add 1 for a trailing \0. +			*/ +			len = packet->header.size - 1; +			packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent); +			memcpy(packet->info_or_local_file, p, len); +			packet->info_or_local_file[len] = '\0'; +			packet->info_or_local_file_len = len; +			break; +		case 0x00: +			packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); +			packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p); +			packet->server_status = uint2korr(p); +			p+=2; +			packet->warning_count = uint2korr(p); +			p+=2; +			/* Check for additional textual data */ +			if (packet->header.size  > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { +				packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent); +				memcpy(packet->info_or_local_file, p, len); +				packet->info_or_local_file[len] = '\0'; +				packet->info_or_local_file_len = len; +			} +			break; +		default: +			/* Result set */ +			break; +	} +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_rset_header_free_mem */ +static +void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet; +	if (p->info_or_local_file) { +		mnd_efree(p->info_or_local_file); +		p->info_or_local_file = NULL; +	} +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + +static size_t rset_field_offsets[] = +{ +	STRUCT_OFFSET(MYSQLND_FIELD, catalog), +	STRUCT_OFFSET(MYSQLND_FIELD, catalog_length), +	STRUCT_OFFSET(MYSQLND_FIELD, db), +	STRUCT_OFFSET(MYSQLND_FIELD, db_length), +	STRUCT_OFFSET(MYSQLND_FIELD, table), +	STRUCT_OFFSET(MYSQLND_FIELD, table_length), +	STRUCT_OFFSET(MYSQLND_FIELD, org_table), +	STRUCT_OFFSET(MYSQLND_FIELD, org_table_length), +	STRUCT_OFFSET(MYSQLND_FIELD, name), +	STRUCT_OFFSET(MYSQLND_FIELD, name_length), +	STRUCT_OFFSET(MYSQLND_FIELD, org_name), +	STRUCT_OFFSET(MYSQLND_FIELD, org_name_length) +}; + + +/* {{{ php_mysqlnd_rset_field_read */ +static enum_func_status +php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	/* Should be enough for the metadata of a single row */ +	php_mysql_packet_res_field *packet= (php_mysql_packet_res_field *) _packet; +	zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer; +	zend_uchar *p = buf; +	zend_uchar *begin = buf; +	char *root_ptr; +	size_t buf_len = conn->net.cmd_buffer.length, len, total_len = 0; +	MYSQLND_FIELD *meta; +	unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t); + +	DBG_ENTER("php_mysqlnd_rset_field_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field"); + +	if (packet->skip_parsing) { +		DBG_RETURN(PASS); +	} +	if (*p == 0xFE && packet->header.size < 8) { +		/* Premature EOF. That should be COM_FIELD_LIST */ +		DBG_INF("Premature EOF. That should be COM_FIELD_LIST"); +		packet->stupid_list_fields_eof = TRUE; +		DBG_RETURN(PASS); +	} + +	meta = packet->metadata; + +	for (i = 0; i < field_count; i += 2) { +		len = php_mysqlnd_net_field_length(&p); +		switch ((len)) { +			case 0: +				*(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string; +				*(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0; +				break; +			case MYSQLND_NULL_LENGTH: +				goto faulty_fake; +			default: +				*(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p; +				*(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len; +				p += len; +				total_len += len + 1; +				break; +		} +	} + +	/* 1 byte filler */ +	p++; + +	meta->charsetnr = uint2korr(p); +	p += 2; + +	meta->length = uint4korr(p); +	p += 4; + +	meta->type = uint1korr(p); +	p += 1; + +	meta->flags = uint2korr(p); +	p += 2; + +	meta->decimals = uint2korr(p); +	p += 1; + +	/* 2 byte filler */ +	p +=2; + +	/* Should we set NUM_FLAG (libmysql does it) ? */ +	if ( +		(meta->type <= MYSQL_TYPE_INT24 && +			(meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8) +		) || meta->type == MYSQL_TYPE_YEAR) +	{ +		meta->flags |= NUM_FLAG; +	} + + +	/* +	  def could be empty, thus don't allocate on the root. +	  NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL. +	  Otherwise the string is length encoded. +	*/ +	if (packet->header.size > (p - buf) && +		(len = php_mysqlnd_net_field_length(&p)) && +		len != MYSQLND_NULL_LENGTH) +	{ +		DBG_INF_FMT("Def found, length %lu", len); +		meta->def = mnd_emalloc(len + 1); +		memcpy(meta->def, p, len); +		meta->def[len] = '\0'; +		meta->def_length = len; +		p += len; +	} + +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("Result set field packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes " +						 "shorter than expected. PID=%d", p - begin - packet->header.size, getpid()); +	} + +	root_ptr = meta->root = mnd_emalloc(total_len); +	meta->root_len = total_len; +	/* Now do allocs */ +	if (meta->catalog && meta->catalog != mysqlnd_empty_string) { +		len = meta->catalog_length; +		meta->catalog = memcpy(root_ptr, meta->catalog, len); +		*(root_ptr +=len) = '\0'; +		root_ptr++; +	} + +	if (meta->db && meta->db != mysqlnd_empty_string) { +		len = meta->db_length; +		meta->db = memcpy(root_ptr, meta->db, len); +		*(root_ptr + len) = '\0'; +	} + +	if (meta->table && meta->table != mysqlnd_empty_string) { +		len = meta->table_length; +		meta->table = memcpy(root_ptr, meta->table, len); +		*(root_ptr +=len) = '\0'; +		root_ptr++; +	} + +	if (meta->org_table && meta->org_table != mysqlnd_empty_string) { +		len = meta->org_table_length; +		meta->org_table = memcpy(root_ptr, meta->org_table, len); +		*(root_ptr +=len) = '\0'; +		root_ptr++; +	} + +	if (meta->name && meta->name != mysqlnd_empty_string) { +		len = meta->name_length; +		meta->name = memcpy(root_ptr, meta->name, len); +		*(root_ptr +=len) = '\0'; +		root_ptr++; +	} + +	if (meta->org_name && meta->org_name != mysqlnd_empty_string) { +		len = meta->org_name_length; +		meta->org_name = memcpy(root_ptr, meta->org_name, len); +		*(root_ptr +=len) = '\0'; +		root_ptr++; +	} +/* +	DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*", +				meta->name? meta->name:"*NA*"); +*/ +	DBG_RETURN(PASS); + +faulty_fake: +	DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty"); +	php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH." +					 " The server is faulty"); +	DBG_RETURN(FAIL); +} +/* }}} */ + + +/* {{{ php_mysqlnd_rset_field_free_mem */ +static +void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet; + +	/* p->metadata was passed to us as temporal buffer */ +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + +static enum_func_status +php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, +						size_t *data_size, zend_bool persistent_alloc, +						unsigned int prealloc_more_bytes TSRMLS_DC) +{ +	enum_func_status ret = PASS; +	mysqlnd_packet_header header; +	zend_uchar *new_buf = NULL, *p = *buf; +	zend_bool first_iteration = TRUE; + +	DBG_ENTER("php_mysqlnd_read_row_ex"); + +	/* +	  To ease the process the server splits everything in packets up to 2^24 - 1. +	  Even in the case the payload is evenly divisible by this value, the last +	  packet will be empty, namely 0 bytes. Thus, we can read every packet and ask +	  for next one if they have 2^24 - 1 sizes. But just read the header of a +	  zero-length byte, don't read the body, there is no such. +	*/ + +	*data_size = prealloc_more_bytes; +	while (1) { +		if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) { +			ret = FAIL; +			break; +		} + +		*data_size += header.size; + +		if (first_iteration && header.size > buf_size) { +			first_iteration = FALSE; +			/* +			  We need a trailing \0 for the last string, in case of text-mode, +			  to be able to implement read-only variables. Thus, we add + 1. +			*/ +			p = new_buf = mnd_pemalloc(*data_size + 1, persistent_alloc); +		} else if (!first_iteration) { +			/* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */ +			if (!header.size) { +				break; +			} + +			/* +			  We have to realloc the buffer. + +			  We need a trailing \0 for the last string, in case of text-mode, +			  to be able to implement read-only variables. +			*/ +			new_buf = mnd_perealloc(new_buf, *data_size + 1, persistent_alloc); +			/* The position could have changed, recalculate */ +			p = new_buf + (*data_size - header.size); +		} + +		if (!mysqlnd_read_body(conn, p, header.size TSRMLS_CC)) { +			DBG_ERR("Empty row packet body"); +			php_error(E_WARNING, "Empty row packet body. PID=%d", getpid()); +			ret = FAIL; +			break; +		} + +		if (header.size < MYSQLND_MAX_PACKET_SIZE) { +			break; +		} +	} +	if (ret == PASS && new_buf) { +		*buf = new_buf; +	} +	*data_size -= prealloc_more_bytes; +	DBG_RETURN(ret); +} + + +/* {{{ php_mysqlnd_rowp_read_binary_protocol */ +static +void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND *conn, +										   zend_uchar *p, size_t data_size TSRMLS_DC) +{ +	int i; +	zend_uchar *null_ptr, bit; +	zval **current_field, **end_field, **start_field; +	zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode; +	zend_bool allocated; +	void *obj; + +	DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol"); + +	end_field = (current_field = start_field = packet->fields) + packet->field_count; + + +	/* skip the first byte, not 0xFE -> 0x0, status */ +	p++; +	null_ptr= p; +	p += (packet->field_count + 9)/8;		/* skip null bits */ +	bit	= 4;								/* first 2 bits are reserved */ + +	for (i = 0; current_field < end_field; current_field++, i++) { +#if 1 +		obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC); +		if (allocated) { +			*current_field = (zval *) obj; +		} else { +			/* It's from the cache, so we can upcast here */ +			*current_field = &((mysqlnd_zval *) obj)->zv;			 +			((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER; +		} +#else +		MAKE_STD_ZVAL(*current_field); +#endif +		if (*null_ptr & bit) { +			ZVAL_NULL(*current_field); +		} else { +			enum_mysqlnd_field_types type = packet->fields_metadata[i].type; +			mysqlnd_ps_fetch_functions[type].func(*current_field, &packet->fields_metadata[i], +												  0, &p, as_unicode TSRMLS_CC); +		} +		if (!((bit<<=1) & 255)) { +			bit= 1;					/* To next byte */ +			null_ptr++; +		} +	} +	/* Normal queries: The buffer has one more byte at the end, because we need it */ +	packet->row_buffer[data_size] = '\0'; + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ php_mysqlnd_rowp_read_text_protocol */ +static +void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *conn, +										 zend_uchar *p, size_t data_size TSRMLS_DC) +{ +	int i; +	zend_bool last_field_was_string; +	zval **current_field, **end_field, **start_field; +	zend_uchar *bit_area = packet->row_buffer + data_size + 1; /* we allocate from here */ +	zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode; +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +	zend_bool as_int = conn->options.int_and_year_as_int; +#endif + +	DBG_ENTER("php_mysqlnd_rowp_read_text_protocol"); + +	end_field = (current_field = start_field = packet->fields) + packet->field_count; +	for (i = 0; current_field < end_field; current_field++, i++) { +		/* Don't reverse the order. It is significant!*/ +		void *obj; +		zend_bool allocated; +		zend_uchar *this_field_len_pos = p; +		/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */ +		unsigned long len = php_mysqlnd_net_field_length(&p); + +		obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC); +		if (allocated) { +			*current_field = (zval *) obj; +		} else { +			/* It's from the cache, so we can upcast here */ +			*current_field = &((mysqlnd_zval *) obj)->zv;	 +			((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_FREE; +		} + +		if (current_field > start_field && last_field_was_string) { +			/* +			  Normal queries:  +			  We have to put \0 now to the end of the previous field, if it was +			  a string. IS_NULL doesn't matter. Because we have already read our +			  length, then we can overwrite it in the row buffer. +			  This statement terminates the previous field, not the current one. + +			  NULL_LENGTH is encoded in one byte, so we can stick a \0 there. +			  Any string's length is encoded in at least one byte, so we can stick +			  a \0 there. +			*/ + +			*this_field_len_pos = '\0'; +		} + +		/* NULL or NOT NULL, this is the question! */ +		if (len == MYSQLND_NULL_LENGTH) { +			ZVAL_NULL(*current_field); +			last_field_was_string = FALSE; +		} else { +#if PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION) +			struct st_mysqlnd_perm_bind perm_bind = +					mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type]; +#endif + +#ifdef MYSQLND_STRING_TO_INT_CONVERSION +			if (as_int && perm_bind.php_type == IS_LONG && +				perm_bind.pack_len <= SIZEOF_LONG) +			{ +				zend_uchar save = *(p + len); +				/* We have to make it ASCIIZ temporarily */ +				*(p + len) = '\0'; +				if (perm_bind.pack_len < SIZEOF_LONG) +				{ +					/* direct conversion */ +					my_int64 v = atoll((char *) p); +					ZVAL_LONG(*current_field, v); +				} else { +					my_uint64 v = (my_uint64) atoll((char *) p); +					zend_bool uns = packet->fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE; +					/* We have to make it ASCIIZ temporarily */ +#if SIZEOF_LONG==8 +					if (uns == TRUE && v > 9223372036854775807L) +#elif SIZEOF_LONG==4 +					if ((uns == TRUE && v > L64(2147483647)) ||  +						(uns == FALSE && (( L64(2147483647) < (my_int64) v) || +						(L64(-2147483648) > (my_int64) v)))) +#endif /* SIZEOF */ +					{ +						ZVAL_STRINGL(*current_field, (char *)p, len, 0); +					} else { +						ZVAL_LONG(*current_field, (my_int64)v); +					} +				} +				*(p + len) = save; +			} else +#endif +			if (packet->fields_metadata[i].type == MYSQL_TYPE_BIT) { +				/* +				  BIT fields are specially handled. As they come as bit mask, we have +				  to convert it to human-readable representation. As the bits take +				  less space in the protocol than the numbers they represent, we don't +				  have enough space in the packet buffer to overwrite inside. +				  Thus, a bit more space is pre-allocated at the end of the buffer, +				  see php_mysqlnd_rowp_read(). And we add the strings at the end. +				  Definitely not nice, _hackish_ :(, but works. +				*/ +				zend_uchar *start = bit_area; +				ps_fetch_from_1_to_8_bytes(*current_field, &(packet->fields_metadata[i]), +							   			   0, &p, as_unicode, len TSRMLS_CC); +				/* +				  We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because +				  later in this function there will be an advancement. +				*/ +				p -= len; +				if (Z_TYPE_PP(current_field) == IS_LONG) { +					bit_area += 1 + sprintf((char *)start, MYSQLND_LLU_SPEC, +											(my_int64) Z_LVAL_PP(current_field)); +#if PHP_MAJOR_VERSION >= 6 +					if (as_unicode) { +						ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0); +					} else 			 +#endif +					{ +						ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); +					} +					if (allocated == FALSE) { +						((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; +					} +				} else if (Z_TYPE_PP(current_field) == IS_STRING){ +					memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field)); +					bit_area += Z_STRLEN_PP(current_field); +					*bit_area++ = '\0'; +					zval_dtor(*current_field); +#if PHP_MAJOR_VERSION >= 6 +					if (as_unicode) { +						ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0); +					} else 			 +#endif +					{ +						ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); +					} +					if (allocated == FALSE) { +						((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; +					} +				} +				/* +				  IS_UNICODE should not be specially handled. In unicode mode +				  the buffers are not referenced - everything is copied. +				*/ +			} else  +#if PHP_MAJOR_VERSION < 6 +			{ +				ZVAL_STRINGL(*current_field, (char *)p, len, 0); +				if (allocated == FALSE) { +					((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; +				} +			} +#else +			/* +			  Here we have to convert to UTF16, which means not reusing the buffer. +			  Which in turn means that we can free the buffers once we have +			  stored the result set, if we use store_result(). + +			  Also the destruction of the zvals should not call zval_copy_ctor() +			  because then we will leak. + +			  I suppose we can use UG(unicode) in mysqlnd.c/mysqlnd_palloc.c when +			  freeing a result set +			  to check if we need to call copy_ctor(). + +			  XXX: Keep in mind that up there there is an open `else` in +				   #ifdef MYSQLND_STRING_TO_INT_CONVERSION +				   which will make with this `if` an `else if`. +			*/ +			if ((perm_bind.is_possibly_blob == TRUE && +				 packet->fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) || +				(!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE)) +			{ +				/* BLOB - no conversion please */ +				ZVAL_STRINGL(*current_field, (char *)p, len, 0); +			} else { +				ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0); +			} +			if (allocated == FALSE) { +				/* +				  The zval cache will check and see that the type is IS_STRING. +				  In this case it will call copy_ctor(). This is valid when +				  allocated == TRUE . In this case we can't upcast. Thus for non-PS +				  point_type doesn't matter much, as the valuable information is +				  in the type of result set. Still good to set it. +				*/ +				if (Z_TYPE_P(*current_field) == IS_STRING) { +					((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; +				} else { +					((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;					 +				} +			} +#endif +			p += len; +			last_field_was_string = TRUE; +		} +	} +	if (last_field_was_string) { +		/* Normal queries: The buffer has one more byte at the end, because we need it */ +		packet->row_buffer[data_size] = '\0'; +	} + +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ php_mysqlnd_rowp_read */ +/* +  if normal statements => packet->fields is created by this function,  +  if PS => packet->fields is passed from outside +*/ +static enum_func_status +php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	MYSQLND_NET *net = &conn->net; +	zend_uchar *p; +	enum_func_status ret = PASS; +	size_t data_size = 0; +	size_t old_chunk_size = net->stream->chunk_size; +	php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet; +	size_t post_alloc_for_bit_fields = 0; + +	DBG_ENTER("php_mysqlnd_rowp_read"); + +	if (!packet->binary_protocol && packet->bit_fields_count) { +		/* For every field we need terminating \0 */ +		post_alloc_for_bit_fields = +			packet->bit_fields_total_len + packet->bit_fields_count; +	} + +	ret = php_mysqlnd_read_row_ex(conn, &packet->row_buffer, 0, &data_size, +								  packet->persistent_alloc, post_alloc_for_bit_fields +								  TSRMLS_CC); +	if (FAIL == ret) { +		goto end; +	} + +	/* packet->row_buffer is of size 'data_size + 1' */ +	packet->header.size = data_size; + +	if ((*(p = packet->row_buffer)) == 0xFF) { +		/* +		   Error message as part of the result set, +		   not good but we should not hang. See: +		   Bug #27876 : SF with cyrillic variable name fails during execution +		*/ +		ret = FAIL; +		php_mysqlnd_read_error_from_line(p + 1, data_size - 1, +										 packet->error_info.error, +										 sizeof(packet->error_info.error), +										 &packet->error_info.error_no, +										 packet->error_info.sqlstate +										 TSRMLS_CC); +	} else if (*p == 0xFE && data_size < 8) { /* EOF */ +		packet->eof = TRUE; +		p++; +		if (data_size > 1) { +			packet->warning_count = uint2korr(p); +			p += 2; +			packet->server_status = uint2korr(p); +			/* Seems we have 3 bytes reserved for future use */ +		} +	} else { +		MYSQLND_INC_CONN_STATISTIC(&conn->stats, +									packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS: +															 STAT_ROWS_FETCHED_FROM_SERVER_NORMAL); + +		packet->eof = FALSE; +		/* packet->field_count is set by the user of the packet */ + +		if (!packet->skip_extraction) { +			if (!packet->fields) { +				DBG_INF("Allocating packet->fields"); +				/* +				  old-API will probably set packet->fields to NULL every time, though for +				  unbuffered sets it makes not much sense as the zvals in this buffer matter, +				  not the buffer. Constantly allocating and deallocating brings nothing. + +				  For PS - if stmt_store() is performed, thus we don't have a cursor, it will +				  behave just like old-API buffered. Cursors will behave like a bit different, +				  but mostly like old-API unbuffered and thus will populate this array with +				  value. +				*/ +				packet->fields = (zval **) mnd_pemalloc(packet->field_count * sizeof(zval *), +													packet->persistent_alloc); +			} + +			if (packet->binary_protocol) { +				php_mysqlnd_rowp_read_binary_protocol(packet, conn, p, data_size TSRMLS_CC); +			} else { +				php_mysqlnd_rowp_read_text_protocol(packet, conn, p, data_size TSRMLS_CC); +			} +		} else { +			MYSQLND_INC_CONN_STATISTIC(&conn->stats, +										packet->binary_protocol? STAT_ROWS_SKIPPED_PS: +																 STAT_ROWS_SKIPPED_NORMAL); +		} +	} + +end: +	net->stream->chunk_size = old_chunk_size; +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ php_mysqlnd_rowp_free_mem */ +static +void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_row *p= (php_mysql_packet_row *) _packet; +	if (p->row_buffer) { +		mnd_pefree(p->row_buffer, p->persistent_alloc); +		p->row_buffer = NULL; +	} +	/* +	  Don't free packet->fields : +	  - normal queries -> store_result() | fetch_row_unbuffered() will transfer +		the ownership and NULL it. +	  - PS will pass in it the bound variables, we have to use them! and of course +		not free the array. As it is passed to us, we should not clean it ourselves. +	*/ +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + + +/* {{{ php_mysqlnd_stats_read */ +static enum_func_status +php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	zend_uchar buf[1024]; +	php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet; + +	DBG_ENTER("php_mysqlnd_stats_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "statistics"); + +	packet->message = mnd_pemalloc(packet->header.size + 1, conn->persistent); +	memcpy(packet->message, buf, packet->header.size); +	packet->message[packet->header.size] = '\0'; +	packet->message_len = packet->header.size; + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_stats_free_mem */ +static +void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet; +	if (p->message) { +		mnd_efree(p->message); +		p->message = NULL; +	} +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + +/* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */ +#define PREPARE_RESPONSE_SIZE_41 9 +#define PREPARE_RESPONSE_SIZE_50 12 + +/* {{{ php_mysqlnd_prepare_read */ +static enum_func_status +php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	/* In case of an error, we should have place to put it */ +	zend_uchar buf[1024]; +	zend_uchar *p = buf; +	zend_uchar *begin = buf; +	unsigned int data_size; +	php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet; + +	DBG_ENTER("php_mysqlnd_prepare_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "prepare"); +	 +	data_size = packet->header.size; +	packet->error_code = uint1korr(p); +	p++; + +	if (0xFF == packet->error_code) { +		php_mysqlnd_read_error_from_line(p, data_size - 1, +										 packet->error_info.error, +										 sizeof(packet->error_info.error), +										 &packet->error_info.error_no, +										 packet->error_info.sqlstate +										 TSRMLS_CC); +		DBG_RETURN(PASS); +	} + +	if (data_size != PREPARE_RESPONSE_SIZE_41 && +		data_size != PREPARE_RESPONSE_SIZE_50 && +		!(data_size > PREPARE_RESPONSE_SIZE_50)) { +		DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %d", data_size); +		php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d. PID=%d", data_size, getpid()); +		DBG_RETURN(FAIL); +	} + +	packet->stmt_id = uint4korr(p); +	p += 4; + +	/* Number of columns in result set */ +	packet->field_count = uint2korr(p); +	p += 2; + +	packet->param_count = uint2korr(p); +	p += 2; + +	if (data_size > 9) { +		/* 0x0 filler sent by the server for 5.0+ clients */ +		p++; + +		packet->warning_count = uint2korr(p); +	} + +	DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d", +				packet->stmt_id, packet->field_count, packet->param_count); + +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_prepare_free_mem */ +static +void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet; +	if (!alloca) { +		mnd_efree(p); +	} +} +/* }}} */ + + +/* {{{ php_mysqlnd_chg_user_read */ +static enum_func_status +php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) +{ +	/* There could be an error message */ +	zend_uchar buf[1024]; +	zend_uchar *p = buf; +	zend_uchar *begin = buf; +	php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet; + +	DBG_ENTER("php_mysqlnd_chg_user_read"); + +	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "change user response "); + +	/* +	  Don't increment. First byte is 0xFF on error, but otherwise is starting byte +	  of encoded sequence for length. +	*/ + +	/* Should be always 0x0 or 0xFF for error */ +	packet->field_count= uint1korr(p); +	p++; + +	if (packet->header.size == 1 && buf[0] == 0xFE && +		packet->server_capabilities & CLIENT_SECURE_CONNECTION) { +		/* We don't handle 3.23 authentication */ +		DBG_RETURN(FAIL); +	} + +	if (0xFF == packet->field_count) { +		php_mysqlnd_read_error_from_line(p, packet->header.size - 1, +										 packet->error_info.error, +										 sizeof(packet->error_info.error), +										 &packet->error_info.error_no, +										 packet->error_info.sqlstate +										 TSRMLS_CC); +	} +	if (p - begin > packet->header.size) { +		DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet %d bytes shorter than expected. PID=%d", +						 p - begin - packet->header.size, getpid()); +	} + +	DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ php_mysqlnd_chg_user_free_mem */ +static +void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +{ +	if (!alloca) { +		mnd_efree(_packet); +	} +} +/* }}} */ + + +/* {{{ packet_methods  + */ +mysqlnd_packet_methods packet_methods[PROT_LAST] = +{ +	{ +		sizeof(php_mysql_packet_greet), +		php_mysqlnd_greet_read, +		NULL, /* write */ +		php_mysqlnd_greet_free_mem, +	}, /* PROT_GREET_PACKET */ +	{ +		sizeof(php_mysql_packet_auth), +		NULL, /* read */ +		php_mysqlnd_auth_write, +		php_mysqlnd_auth_free_mem, +	}, /* PROT_AUTH_PACKET */ +	{ +		sizeof(php_mysql_packet_ok), +		php_mysqlnd_ok_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_ok_free_mem, +	}, /* PROT_OK_PACKET */ +	{ +		sizeof(php_mysql_packet_eof), +		php_mysqlnd_eof_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_eof_free_mem, +	}, /* PROT_EOF_PACKET */ +	{ +		sizeof(php_mysql_packet_command), +		NULL, /* read */ +		php_mysqlnd_cmd_write, /* write */ +		php_mysqlnd_cmd_free_mem, +	}, /* PROT_CMD_PACKET */ +	{ +		sizeof(php_mysql_packet_rset_header), +		php_mysqlnd_rset_header_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_rset_header_free_mem, +	}, /* PROT_RSET_HEADER_PACKET */ +	{ +		sizeof(php_mysql_packet_res_field), +		php_mysqlnd_rset_field_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_rset_field_free_mem, +	}, /* PROT_RSET_FLD_PACKET */ +	{ +		sizeof(php_mysql_packet_row), +		php_mysqlnd_rowp_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_rowp_free_mem, +	}, /* PROT_ROW_PACKET */ +	{ +		sizeof(php_mysql_packet_stats), +		php_mysqlnd_stats_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_stats_free_mem, +	}, /* PROT_STATS_PACKET */ +	{ +		sizeof(php_mysql_packet_prepare_response), +		php_mysqlnd_prepare_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_prepare_free_mem, +	}, /* PROT_PREPARE_RESP_PACKET */ +	{ +		sizeof(php_mysql_packet_chg_user_resp), +		php_mysqlnd_chg_user_read, /* read */ +		NULL, /* write */ +		php_mysqlnd_chg_user_free_mem, +	} /* PROT_CHG_USER_PACKET */ +}; +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h new file mode 100644 index 0000000000..96da8d16a8 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -0,0 +1,335 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_WIREPROTOCOL_H +#define MYSQLND_WIREPROTOCOL_H + +#define MYSQLND_HEADER_SIZE 4 + +#define MYSQLND_NULL_LENGTH	(unsigned long) ~0 + +typedef zend_uchar	mysqlnd_1b; +typedef zend_ushort mysqlnd_2b; +typedef zend_uint	mysqlnd_4b; + +/* Used in mysqlnd_debug.c */ +extern char * mysqlnd_read_header_name; +extern char * mysqlnd_read_body_name; + + +/* Packet handling */ +#define PACKET_INIT(packet, enum_type, c_type)  \ +	{ \ +		packet = (c_type) ecalloc(1, packet_methods[enum_type].struct_size); \ +		((c_type) (packet))->header.m = &packet_methods[enum_type]; \ +	} +#define PACKET_WRITE(packet, conn)	((packet)->header.m->write_to_net((packet), (conn) TSRMLS_CC)) +#define PACKET_READ(packet, conn)	((packet)->header.m->read_from_net((packet), (conn) TSRMLS_CC)) +#define PACKET_FREE(packet) \ +	do { \ +		((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \ +	} while (0); + +#define PACKET_INIT_ALLOCA(packet, enum_type)  \ +	{ \ +		memset(&(packet), 0, packet_methods[enum_type].struct_size); \ +		(packet).header.m = &packet_methods[enum_type]; \ +	} +#define PACKET_WRITE_ALLOCA(packet, conn)	PACKET_WRITE(&(packet), (conn)) +#define PACKET_READ_ALLOCA(packet, conn)	PACKET_READ(&(packet), (conn)) +#define PACKET_FREE_ALLOCA(packet)			(packet.header.m->free_mem(&(packet), TRUE TSRMLS_CC)) + +/* Enums */ +enum php_mysql_packet_type +{ +	PROT_GREET_PACKET= 0, +	PROT_AUTH_PACKET, +	PROT_OK_PACKET, +	PROT_EOF_PACKET, +	PROT_CMD_PACKET, +	PROT_RSET_HEADER_PACKET, +	PROT_RSET_FLD_PACKET, +	PROT_ROW_PACKET, +	PROT_STATS_PACKET, +	PROT_PREPARE_RESP_PACKET, +	PROT_CHG_USER_PACKET, +	PROT_LAST, /* should always be last */ +}; + + +enum php_mysqlnd_server_command +{ +	COM_SLEEP = 0, +	COM_QUIT, +	COM_INIT_DB, +	COM_QUERY, +	COM_FIELD_LIST, +	COM_CREATE_DB, +	COM_DROP_DB, +	COM_REFRESH, +	COM_SHUTDOWN, +	COM_STATISTICS, +	COM_PROCESS_INFO, +	COM_CONNECT, +	COM_PROCESS_KILL, +	COM_DEBUG, +	COM_PING, +	COM_TIME = 15, +	COM_DELAYED_INSERT, +	COM_CHANGE_USER, +	COM_BINLOG_DUMP, +	COM_TABLE_DUMP, +	COM_CONNECT_OUT = 20, +	COM_REGISTER_SLAVE, +	COM_STMT_PREPARE = 22, +	COM_STMT_EXECUTE = 23, +	COM_STMT_SEND_LONG_DATA = 24, +	COM_STMT_CLOSE = 25, +	COM_STMT_RESET = 26, +	COM_SET_OPTION = 27, +	COM_STMT_FETCH = 28, +	COM_DAEMON, +	COM_END +}; + +extern const char * const mysqlnd_command_to_text[COM_END]; + +/* Low-level extraction functionality */ +typedef struct st_mysqlnd_packet_methods { +	size_t				struct_size; +	enum_func_status	(*read_from_net)(void *packet, MYSQLND *conn TSRMLS_DC); +	size_t				(*write_to_net)(void *packet, MYSQLND *conn TSRMLS_DC); +	void				(*free_mem)(void *packet, zend_bool alloca TSRMLS_DC); +} mysqlnd_packet_methods; + +extern mysqlnd_packet_methods packet_methods[]; + + +typedef struct st_mysqlnd_packet_header { +	size_t		size; +	zend_uchar	packet_no; +	mysqlnd_packet_methods *m; +} mysqlnd_packet_header; + +/* Server greets the client */ +typedef struct st_php_mysql_packet_greet { +	mysqlnd_packet_header		header; +	mysqlnd_1b		protocol_version; +	char			*server_version; +	mysqlnd_4b		thread_id; +	zend_uchar		scramble_buf[SCRAMBLE_LENGTH]; +	/* 1 byte pad */ +	mysqlnd_2b		server_capabilities; +	mysqlnd_1b		charset_no; +	mysqlnd_2b		server_status; +	/* 13 byte pad*/ +	zend_bool		pre41; +	/* If error packet, we use these */ +	char 			error[MYSQLND_ERRMSG_SIZE+1]; +	char 			sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; +	unsigned int 	error_no; +} php_mysql_packet_greet; + + +/* Client authenticates */ +typedef struct st_php_mysql_packet_auth { +	mysqlnd_packet_header		header; +	mysqlnd_4b	client_flags; +	uint32		max_packet_size; +	mysqlnd_1b	charset_no; +	/* 23 byte pad */ +	char		*user; +	/* 8 byte scramble */ +	char		*db; +	/* 12 byte scramble */ + +	/* Here the packet ends. This is user supplied data */ +	char		*password; +	/* +1 for \0 because of scramble() */ +	unsigned char	*server_scramble_buf; +	size_t			db_len; +} php_mysql_packet_auth; + +/* OK packet */ +typedef struct st_php_mysql_packet_ok { +	mysqlnd_packet_header		header; +	mysqlnd_1b		field_count; /* always 0x0 */ +	mynd_ulonglong	affected_rows; +	mynd_ulonglong	last_insert_id; +	mysqlnd_2b		server_status; +	mysqlnd_2b		warning_count; +	char			*message; +	size_t			message_len; +	/* If error packet, we use these */ +	char 			error[MYSQLND_ERRMSG_SIZE+1]; +	char 			sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; +	unsigned int 	error_no; +} php_mysql_packet_ok; + + +/* Command packet */ +typedef struct st_php_mysql_packet_command { +	mysqlnd_packet_header			header; +	enum php_mysqlnd_server_command	command; +	const char						*argument; +	size_t							arg_len; +} php_mysql_packet_command; + + +/* EOF packet */ +typedef struct st_php_mysql_packet_eof { +	mysqlnd_packet_header		header; +	mysqlnd_1b		field_count; /* 0xFE */ +	mysqlnd_2b		warning_count; +	mysqlnd_2b		server_status; +	/* If error packet, we use these */ +	char 			error[MYSQLND_ERRMSG_SIZE+1]; +	char 			sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; +	unsigned int 	error_no; +} php_mysql_packet_eof; +/* EOF packet */ + + +/* Result Set header*/ +typedef struct st_php_mysql_packet_rset_header { +	mysqlnd_packet_header		header; +	/* +	  0x00 => ok +	  ~0   => LOAD DATA LOCAL +	  error_no != 0 => error +	  others => result set -> Read res_field packets up to field_count +	*/ +	unsigned long		field_count; +	/* +	  These are filled if no SELECT query. For SELECT warning_count +	  and server status are in the last row packet, the EOF packet. +	*/ +	mysqlnd_2b			warning_count; +	mysqlnd_2b			server_status; +	mynd_ulonglong		affected_rows; +	mynd_ulonglong		last_insert_id; +	/* This is for both LOAD DATA or info, when no result set */ +	char				*info_or_local_file; +	size_t				info_or_local_file_len; +	/* If error packet, we use these */ +	mysqlnd_error_info	error_info; +} php_mysql_packet_rset_header; + + +/* Result set field packet */ +typedef struct st_php_mysql_packet_res_field { +	mysqlnd_packet_header	header; +	MYSQLND_FIELD			*metadata; +	/* For table definitions, empty for result sets */ +	zend_bool				skip_parsing; +	zend_bool				stupid_list_fields_eof; +} php_mysql_packet_res_field; + + +/* Row packet */ +struct st_php_mysql_packet_row { +	mysqlnd_packet_header	header; +	zval			**fields; +	mysqlnd_4b		field_count; +	zend_bool		eof; +	/* +	  These are, of course, only for SELECT in the EOF packet, +	  which is detected by this packet +	*/ +	mysqlnd_2b		warning_count; +	mysqlnd_2b		server_status; + +	zend_uchar		*row_buffer; + +	zend_bool		skip_extraction; +	zend_bool		binary_protocol; +	zend_bool		persistent_alloc; +	MYSQLND_FIELD	*fields_metadata; +	/* We need this to alloc bigger bufs in non-PS mode */ +	unsigned int	bit_fields_count; +	size_t			bit_fields_total_len; /* trailing \0 not counted */ + +	/* If error packet, we use these */ +	mysqlnd_error_info	error_info; +}; + + +/* Statistics packet */ +typedef struct st_php_mysql_packet_stats { +	mysqlnd_packet_header	header; +	char *message; +	/* message_len is not part of the packet*/ +	size_t message_len; +} php_mysql_packet_stats; + + +/* COM_PREPARE response packet */ +typedef struct st_php_mysql_packet_prepare_response { +	mysqlnd_packet_header	header; +	/* also known as field_count 0x00=OK , 0xFF=error */ +	unsigned char	error_code; +	unsigned long	stmt_id; +	unsigned int	field_count; +	unsigned int	param_count; +	unsigned int	warning_count; + +	/* present in case of error */ +	mysqlnd_error_info	error_info; +} php_mysql_packet_prepare_response; + + +/* Statistics packet */ +typedef struct st_php_mysql_packet_chg_user_resp { +	mysqlnd_packet_header	header; +	mysqlnd_4b			field_count; +	 +	/* message_len is not part of the packet*/ +	mysqlnd_2b			server_capabilities; +	/* If error packet, we use these */ +	mysqlnd_error_info	error_info; +} php_mysql_packet_chg_user_resp; + + +size_t mysqlnd_stream_write(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); +size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); + +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND +size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC); +#endif + +void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass); + +unsigned long	php_mysqlnd_net_field_length(zend_uchar **packet); +zend_uchar *	php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length); + +extern char * const mysqlnd_empty_string; + +#endif /* MYSQLND_WIREPROTOCOL_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/php_mysqlnd.h b/ext/mysqlnd/php_mysqlnd.h new file mode 100644 index 0000000000..0958fcae1f --- /dev/null +++ b/ext/mysqlnd/php_mysqlnd.h @@ -0,0 +1,29 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 1997-2007 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@php.net>                               | +  |          Andrey Hristov <andrey@php.net>                             | +  |          Ulf Wendel <uw@php.net>                                     | +  +----------------------------------------------------------------------+ + +  $Id$  +*/ + +#ifndef PHP_MYSQLND_H +#define PHP_MYSQLND_H + +#define phpext_mysqlnd_ptr &mysqlnd_module_entry +extern zend_module_entry mysqlnd_module_entry; + +#endif	/* PHP_MYSQLND_H */  | 
