cvs: php-src / acinclude.m4 /ext/mysql .cvsignore config.m4 config.w32 mysql_mysqlnd.h package.xml php_mysql.h /ext/mysqli .cvsignore config.m4
config.w32 config9.m4 mysqli_mysqlnd.h php_mysqli.h php_mysqli_structs.h /ext/mysqli/mysqlnd config-win.h mysqlnd.c mysqlnd.h mysqlnd_charset.c
mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_priv.h mysqlnd_ps.c
mysqlnd_ps_codec.c mysqlnd_qcache.c mysqlnd_result.c mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c
mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h php_mysqlnd.h portability.h /ext/mysqlnd .cvsignore CREDITS config-win.h
config.w32 config9.m4 mysqlnd.c mysqlnd.h mysqlnd_charset.c mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c
mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_portability.h mysqlnd_priv.h mysqlnd_ps.c mysqlnd_ps_codec.c mysqlnd_qcache.c mys! qlnd_result.c
mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h
php_mysqlnd.h

From: Date: Wed, 25 Jul 2007 23:47:33 +0000
Subject: cvs: php-src / acinclude.m4 /ext/mysql .cvsignore config.m4 config.w32 mysql_mysqlnd.h package.xml php_mysql.h /ext/mysqli .cvsignore config.m4
config.w32 config9.m4 mysqli_mysqlnd.h php_mysqli.h php_mysqli_structs.h /ext/mysqli/mysqlnd config-win.h mysqlnd.c mysqlnd.h mysqlnd_charset.c
mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_priv.h mysqlnd_ps.c
mysqlnd_ps_codec.c mysqlnd_qcache.c mysqlnd_result.c mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c
mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h php_mysqlnd.h portability.h /ext/mysqlnd .cvsignore CREDITS config-win.h
config.w32 config9.m4 mysqlnd.c mysqlnd.h mysqlnd_charset.c mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c
mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_portability.h mysqlnd_priv.h mysqlnd_ps.c mysqlnd_ps_codec.c mysqlnd_qcache.c mys! qlnd_result.c
mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h
php_mysqlnd.h
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-45605@lists.php.net to get a copy of this message
jani Wed Jul 25 23:47:33 2007 UTC Added files: /php-src/ext/mysqlnd .cvsignore CREDITS config-win.h config.w32 config9.m4 mysqlnd.c mysqlnd.h mysqlnd_charset.c mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_portability.h mysqlnd_priv.h mysqlnd_ps.c mysqlnd_ps_codec.c mysqlnd_qcache.c mysqlnd_result.c mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h php_mysqlnd.h Removed files: /php-src/ext/mysqli config9.m4 /php-src/ext/mysqli/mysqlnd config-win.h mysqlnd.c mysqlnd.h mysqlnd_charset.c mysqlnd_charset.h mysqlnd_enum_n_def.h mysqlnd_libmysql_compat.h mysqlnd_loaddata.c mysqlnd_palloc.c mysqlnd_palloc.h mysqlnd_priv.h mysqlnd_ps.c mysqlnd_ps_codec.c mysqlnd_qcache.c mysqlnd_result.c mysqlnd_result.h mysqlnd_result_meta.c mysqlnd_result_meta.h mysqlnd_statistics.c mysqlnd_statistics.h mysqlnd_wireprotocol.c mysqlnd_wireprotocol.h php_mysqlnd.h portability.h Modified files: /php-src acinclude.m4 /php-src/ext/mysql .cvsignore config.m4 config.w32 mysql_mysqlnd.h package.xml php_mysql.h /php-src/ext/mysqli config.m4 .cvsignore config.w32 mysqli_mysqlnd.h php_mysqli.h php_mysqli_structs.h Log: - Moved mysqnd to it's own directory: It is sort of an extension itself. (Similar to what ext/libxml is..)

http://cvs.php.net/viewvc.cgi/php-src/acinclude.m4?r1=1.380&r2=1.381&diff_format=u Index: php-src/acinclude.m4 diff -u php-src/acinclude.m4:1.380 php-src/acinclude.m4:1.381 --- php-src/acinclude.m4:1.380 Wed Jul 25 21:19:43 2007 +++ php-src/acinclude.m4 Wed Jul 25 23:47:32 2007 @@ -1,5 +1,5 @@ dnl -dnl $Id: acinclude.m4,v 1.380 2007/07/25 21:19:43 jani Exp $ +dnl $Id: acinclude.m4,v 1.381 2007/07/25 23:47:32 jani Exp $ dnl dnl This file contains local autoconf functions. dnl @@ -1026,7 +1026,14 @@ dnl Checks for structures, typedefs, broken functions, etc. dnl ------------------------------------------------------------------------- -dnl Internal helper macro +dnl Internal helper macros +dnl +dnl _PHP_DEF_HAVE_FILE(what, filename) +AC_DEFUN([_PHP_DEF_HAVE_FILE], [ + php_def_have_what=HAVE_[]`echo $1 | tr 'abcdefghijklmnopqrstuvwxyz-' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' ` + echo "#define $php_def_have_what 1" >> $2 +]) +dnl dnl _PHP_CHECK_SIZEOF(type, cross-value, extra-headers [, found-action [, not-found-action]]) dnl AC_DEFUN([_PHP_CHECK_SIZEOF], [ @@ -1085,6 +1092,21 @@ ]) dnl +dnl PHP_CHECK_TYPES(type-list, include-file [, extra-headers]) +dnl +AC_DEFUN([PHP_CHECK_TYPES], [ + for php_typename in $1; do + AC_MSG_CHECKING([whether $php_typename exists]) + _PHP_CHECK_SIZEOF($php_typename, 0, $3, [ + _PHP_DEF_HAVE_FILE($php_typename, $2) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) + done +]) + +dnl dnl PHP_CHECK_IN_ADDR_T dnl AC_DEFUN([PHP_CHECK_IN_ADDR_T], [ http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/.cvsignore?r1=1.12&r2=1.13&diff_format=u Index: php-src/ext/mysql/.cvsignore diff -u php-src/ext/mysql/.cvsignore:1.12 php-src/ext/mysql/.cvsignore:1.13 --- php-src/ext/mysql/.cvsignore:1.12 Tue Jul 24 19:23:35 2007 +++ php-src/ext/mysql/.cvsignore Wed Jul 25 23:47:32 2007 @@ -48,4 +48,3 @@ weztest.sqlite *.gcda *.gcno -php_have_mysqlnd.h \ No newline at end of file http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/config.m4?r1=1.69&r2=1.70&diff_format=u Index: php-src/ext/mysql/config.m4 diff -u php-src/ext/mysql/config.m4:1.69 php-src/ext/mysql/config.m4:1.70 --- php-src/ext/mysql/config.m4:1.69 Tue Jul 24 16:13:25 2007 +++ php-src/ext/mysql/config.m4 Wed Jul 25 23:47:32 2007 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4,v 1.69 2007/07/24 16:13:25 andrey Exp $ +dnl $Id: config.m4,v 1.70 2007/07/25 23:47:32 jani Exp $ dnl AC_DEFUN([MYSQL_LIB_CHK], [ @@ -140,7 +140,6 @@ PHP_ADD_LIBRARY_WITH_PATH($MYSQL_LIBNAME, $MYSQL_LIB_DIR, MYSQL_SHARED_LIBADD) PHP_ADD_INCLUDE($MYSQL_INC_DIR) - MYSQL_MODULE_TYPE=external MYSQL_LIBS="-L$MYSQL_LIB_DIR -l$MYSQL_LIBNAME $MYSQL_LIBS" MYSQL_INCLUDE=-I$MYSQL_INC_DIR @@ -156,9 +155,7 @@ PHP_NEW_EXTENSION(mysql, php_mysql.c, $ext_shared) PHP_SUBST(MYSQL_SHARED_LIBADD) - dnl These 3 lines are neeeded to be able to build ext/mysql and/or ext/mysqli with/without mysqlnd. - dnl Need to do this here for the file to be always available. - $php_shtool mkdir -p ext/mysql/ - echo > ext/mysql/php_have_mysqlnd.h - test "$PHP_MYSQL" = "mysqlnd" && PHP_DEFINE(HAVE_MYSQLND, 1, [ext/mysql]) + if test "$PHP_MYSQLI" = "mysqlnd"; then + PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) + fi fi http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/config.w32?r1=1.2&r2=1.3&diff_format=u Index: php-src/ext/mysql/config.w32 diff -u php-src/ext/mysql/config.w32:1.2 php-src/ext/mysql/config.w32:1.3 --- php-src/ext/mysql/config.w32:1.2 Sun Jun 5 19:25:00 2005 +++ php-src/ext/mysql/config.w32 Wed Jul 25 23:47:32 2007 @@ -1,15 +1,19 @@ -// $Id: config.w32,v 1.2 2005/06/05 19:25:00 wez Exp $ +// $Id: config.w32,v 1.3 2007/07/25 23:47:32 jani Exp $ // vim:ft=javascript ARG_WITH("mysql", "MySQL support", "no"); if (PHP_MYSQL != "no") { - 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'); - } else { - WARNING("mysql not enabled; libraries and headers not found"); - } + 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)) { + } else { + WARNING("mysql not enabled; libraries and headers not found"); + } + } else { + ADD_EXTENSION_DEP('mysql', 'mysqlnd', true); + } + EXTENSION("mysql", "php_mysql.c"); + AC_DEFINE('HAVE_MYSQL', 1, 'Have MySQL library'); } http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/mysql_mysqlnd.h?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/mysql/mysql_mysqlnd.h diff -u php-src/ext/mysql/mysql_mysqlnd.h:1.1 php-src/ext/mysql/mysql_mysqlnd.h:1.2 --- php-src/ext/mysql/mysql_mysqlnd.h:1.1 Tue Jul 24 16:50:20 2007 +++ php-src/ext/mysql/mysql_mysqlnd.h Wed Jul 25 23:47:32 2007 @@ -22,7 +22,7 @@ #ifndef MYSQL_MYSQLND_H #define MYSQL_MYSQLND_H -#include "ext/mysqli/mysqlnd/mysqlnd_libmysql_compat.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 http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/package.xml?r1=1.2&r2=1.3&diff_format=u Index: php-src/ext/mysql/package.xml diff -u php-src/ext/mysql/package.xml:1.2 php-src/ext/mysql/package.xml:1.3 --- php-src/ext/mysql/package.xml:1.2 Wed Mar 14 09:49:57 2007 +++ php-src/ext/mysql/package.xml Wed Jul 25 23:47:32 2007 @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE package SYSTEM "../pear/package.dtd"> <package> - <name>mysqli</name> + <name>mysql</name> <summary>MySQL database access functions</summary> <maintainers> <maintainer> http://cvs.php.net/viewvc.cgi/php-src/ext/mysql/php_mysql.h?r1=1.41&r2=1.42&diff_format=u Index: php-src/ext/mysql/php_mysql.h diff -u php-src/ext/mysql/php_mysql.h:1.41 php-src/ext/mysql/php_mysql.h:1.42 --- php-src/ext/mysql/php_mysql.h:1.41 Tue Jul 24 16:13:25 2007 +++ php-src/ext/mysql/php_mysql.h Wed Jul 25 23:47:32 2007 @@ -16,8 +16,7 @@ +----------------------------------------------------------------------+ */ - -/* $Id: php_mysql.h,v 1.41 2007/07/24 16:13:25 andrey Exp $ */ +/* $Id: php_mysql.h,v 1.42 2007/07/25 23:47:32 jani Exp $ */ #ifndef PHP_MYSQL_H #define PHP_MYSQL_H @@ -42,12 +41,8 @@ #define FALSE 0 #endif -#ifdef PHP_ATOM_INC -#include "ext/mysql/php_have_mysqlnd.h" /* HAVE_MYSQLND is defined here if enabled */ -#endif - #if defined(HAVE_MYSQLND) -#include "ext/mysqli/mysqlnd/mysqlnd.h" +#include "ext/mysqlnd/mysqlnd.h" #include "ext/mysql/mysql_mysqlnd.h" #else #include <mysql.h> @@ -152,8 +147,4 @@ #define phpext_mysql_ptr mysql_module_ptr - -#include "ext/mysqli/mysqlnd/php_mysqlnd.h" - - #endif /* PHP_MYSQL_H */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/config.m4?r1=1.27&r2=1.28&diff_format=u Index: php-src/ext/mysqli/config.m4 diff -u /dev/null php-src/ext/mysqli/config.m4:1.28 --- /dev/null Wed Jul 25 23:47:33 2007 +++ php-src/ext/mysqli/config.m4 Wed Jul 25 23:47:32 2007 @@ -0,0 +1,77 @@ +dnl +dnl $Id: config.m4,v 1.28 2007/07/25 23:47:32 jani Exp $ +dnl config.m4 for extension mysqli + +PHP_ARG_WITH(mysqli, for MySQLi support, +[ --with-mysqli[=FILE] Include MySQLi support. FILE is the optional pathname to mysql_config [mysql_config]. + If mysqlnd is passed as FILE, the MySQL native driver will be used]) + +PHP_ARG_ENABLE(embedded_mysqli, whether to enable embedded MySQLi support, +[ --enable-embedded-mysqli MYSQLi: Enable embedded support], no, no) + +if test "$PHP_MYSQLI" = "mysqlnd"; then + dnl This needs to be set in any extension which wishes to use mysqlnd + PHP_MYSQLND_ENABLED=yes + +elif test "$PHP_MYSQLI" != "no"; then + + if test "$PHP_MYSQLI" = "yes"; then + MYSQL_CONFIG=`$php_shtool path mysql_config` + else + MYSQL_CONFIG=$PHP_MYSQLI + fi + + MYSQL_LIB_NAME='mysqlclient' + if test "$PHP_EMBEDDED_MYSQLI" = "yes"; then + AC_DEFINE(HAVE_EMBEDDED_MYSQLI, 1, [embedded MySQL support enabled]) + MYSQL_LIB_CFG='--libmysqld-libs' + dnl mysqlnd doesn't support embedded, so we have to add some extra stuff + mysqli_extra_sources="mysqli_embedded.c" + elif test "$enable_maintainer_zts" = "yes"; then + MYSQL_LIB_CFG='--libs_r' + MYSQL_LIB_NAME='mysqlclient_r' + else + MYSQL_LIB_CFG='--libs' + fi + + if test -x "$MYSQL_CONFIG" && $MYSQL_CONFIG $MYSQL_LIB_CFG > /dev/null 2>&1; then + MYSQLI_INCLINE=`$MYSQL_CONFIG --cflags | $SED -e "s/'//g"` + MYSQLI_LIBLINE=`$MYSQL_CONFIG $MYSQL_LIB_CFG | $SED -e "s/'//g"` + else + AC_MSG_RESULT([mysql_config not found]) + AC_MSG_ERROR([Please reinstall the mysql distribution]) + fi + + dnl + dnl Check the library + dnl + PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_server_option, + [ + PHP_EVAL_INCLINE($MYSQLI_INCLINE) + PHP_EVAL_LIBLINE($MYSQLI_LIBLINE, MYSQLI_SHARED_LIBADD) + AC_DEFINE(HAVE_MYSQLILIB, 1, [ ]) + PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_character_set, + [ ],[ + AC_MSG_ERROR([MySQLI doesn't support versions < 4.1.13 (for MySQL 4.1.x) and < 5.0.7 for (MySQL 5.0.x) anymore. Please update your libraries.]) + ],[$MYSQLI_LIBLINE]) + ],[ + AC_MSG_ERROR([wrong mysql library version or lib not found. Check config.log for more information.]) + ],[ + $MYSQLI_LIBLINE + ]) + + 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 http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/.cvsignore?r1=1.4&r2=1.5&diff_format=u Index: php-src/ext/mysqli/.cvsignore diff -u php-src/ext/mysqli/.cvsignore:1.4 php-src/ext/mysqli/.cvsignore:1.5 --- php-src/ext/mysqli/.cvsignore:1.4 Tue Jul 24 19:23:35 2007 +++ php-src/ext/mysqli/.cvsignore Wed Jul 25 23:47:32 2007 @@ -1,4 +1,3 @@ - #*# *.dsw *.la @@ -48,4 +47,3 @@ scan_makefile_in.awk *.gcda *.gcno -php_have_mysqlnd.h \ No newline at end of file http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/config.w32?r1=1.8&r2=1.9&diff_format=u Index: php-src/ext/mysqli/config.w32 diff -u php-src/ext/mysqli/config.w32:1.8 php-src/ext/mysqli/config.w32:1.9 --- php-src/ext/mysqli/config.w32:1.8 Tue Jul 24 16:13:25 2007 +++ php-src/ext/mysqli/config.w32 Wed Jul 25 23:47:32 2007 @@ -1,13 +1,12 @@ -// $Id: config.w32,v 1.8 2007/07/24 16:13:25 andrey Exp $ +// $Id: config.w32,v 1.9 2007/07/25 23:47:32 jani Exp $ // vim:ft=javascript // Note: The extension name is "mysqli", you enable it with "--with-mysqli". -// The "--enable-mysqlnd" tells to use "mysqli", if enabled, with the bundled +// 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"); -ARG_ENABLE("mysqlnd", "MySQLi with mysqlnd support", "no"); if (PHP_MYSQLI != "no") { @@ -23,7 +22,7 @@ "mysqli_report.c " + "mysqli_warning.c"; - if (PHP_MYSQLND == "no") { + 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 + @@ -38,27 +37,8 @@ } else { - mysqlnd_source = - "mysqlnd.c " + -// "mysqlnd_alloc.c " + - "mysqlnd_charset.c " + - "mysqlnd_loaddata.c " + - "mysqlnd_palloc.c " + - "mysqlnd_ps.c " + - "mysqlnd_ps_codec.c " + - "mysqlnd_qcache.c " + - "mysqlnd_result.c " + - "mysqlnd_result_meta.c " + - "mysqlnd_statistics.c " + - "mysqlnd_wireprotocol.c"; - EXTENSION("mysqli", mysqli_source); - // Specify that add "mysqlnd" sources, but use same object file - // directory as the "mysqli" sources - // FIXME the hard coded "ext/mysqli/mysqlnd" prevents pointing - // out sources in another directory? Like above: PHP_MYSQLI + "\\include;" - ADD_SOURCES("ext/mysqli/mysqlnd", mysqlnd_source, "mysqli"); AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); - AC_DEFINE('HAVE_MYSQLND' , 1, 'MySQL native driver support enabled'); + ADD_EXTENSION_DEP('mysqli', 'mysqlnd', true); } } http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/mysqli_mysqlnd.h?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/mysqli/mysqli_mysqlnd.h diff -u php-src/ext/mysqli/mysqli_mysqlnd.h:1.1 php-src/ext/mysqli/mysqli_mysqlnd.h:1.2 --- php-src/ext/mysqli/mysqli_mysqlnd.h:1.1 Tue Jul 24 16:13:26 2007 +++ php-src/ext/mysqli/mysqli_mysqlnd.h Wed Jul 25 23:47:32 2007 @@ -22,7 +22,7 @@ #ifndef MYSQL_MYSQLND_H #define MYSQL_MYSQLND_H -#include "ext/mysqli/mysqlnd/mysqlnd_libmysql_compat.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 http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/php_mysqli.h?r1=1.65&r2=1.66&diff_format=u Index: php-src/ext/mysqli/php_mysqli.h diff -u php-src/ext/mysqli/php_mysqli.h:1.65 php-src/ext/mysqli/php_mysqli.h:1.66 --- php-src/ext/mysqli/php_mysqli.h:1.65 Tue Jul 24 16:13:26 2007 +++ php-src/ext/mysqli/php_mysqli.h Wed Jul 25 23:47:32 2007 @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: php_mysqli.h,v 1.65 2007/07/24 16:13:26 andrey Exp $ + $Id: php_mysqli.h,v 1.66 2007/07/25 23:47:32 jani Exp $ */ #ifndef PHP_MYSQLI_H @@ -144,14 +144,11 @@ ZEND_FUNCTION(mysqli_driver_construct); ZEND_METHOD(mysqli_warning,__construct); - #define phpext_mysqli_ptr &mysqli_module_entry extern zend_module_entry mysqli_module_entry; #endif /* PHP_MYSQLI.H */ -#include "ext/mysqli/mysqlnd/php_mysqlnd.h" - /* * Local variables: * tab-width: 4 http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/php_mysqli_structs.h?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/mysqli/php_mysqli_structs.h diff -u php-src/ext/mysqli/php_mysqli_structs.h:1.1 php-src/ext/mysqli/php_mysqli_structs.h:1.2 --- php-src/ext/mysqli/php_mysqli_structs.h:1.1 Tue Jul 24 16:13:26 2007 +++ php-src/ext/mysqli/php_mysqli_structs.h Wed Jul 25 23:47:32 2007 @@ -15,7 +15,7 @@ | Author: Georg Richter <georg@php.net> | +----------------------------------------------------------------------+ - $Id: php_mysqli_structs.h,v 1.1 2007/07/24 16:13:26 andrey Exp $ + $Id: php_mysqli_structs.h,v 1.2 2007/07/25 23:47:32 jani Exp $ */ #ifndef PHP_MYSQLI_STRUCTS_H @@ -36,12 +36,8 @@ #define FALSE 0 #endif -#ifdef PHP_ATOM_INC -#include "ext/mysqli/php_have_mysqlnd.h" /* HAVE_MYSQLND is defined here if enabled */ -#endif - -#if defined(HAVE_MYSQLND) -#include "ext/mysqli/mysqlnd/mysqlnd.h" +#ifdef HAVE_MYSQLND +#include "ext/mysqlnd/mysqlnd.h" #include "ext/mysqli/mysqli_mysqlnd.h" #else #include <mysql.h> http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/.cvsignore?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/.cvsignore +++ php-src/ext/mysqlnd/.cvsignore #*# *.dsw *.la *.lo *.ncb *.opt *.plg *.tgz *~..#*..deps..libs Debug Debug_TS Makefile Makefile.fragments Makefile.global Makefile.objects Release Release_TS Release_TSDbg Release_TS_inline Release_inline acinclude.m4 aclocal.m4 autom4te.cache build config.cache config.guess config.h config.h.in config.log config.nice config.status config.sub configure configure.in conftest conftest.c include install-sh libtool ltmain.sh missing mkinstalldirs modules scan_makefile_in.awk *.gcda *.gcno http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/config-win.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/config-win.h +++ php-src/ext/mysqlnd/config-win.h /* 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 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/config.w32?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/config.w32 +++ php-src/ext/mysqlnd/config.w32 // $Id: config.w32,v 1.1 2007/07/25 23:47:32 jani Exp $ // vim:ft=javascript // Note: The extension name is "mysqli", you enable it with "--with-mysqli". // The "--enable-mysqlnd" tells to use "mysqli", if enabled, with the bundled // client library to connect to the MySQL server, i.e. no external MySQL // client library is needed to perform the build. ARG_WITH("mysqli", "MySQLi support", "no"); ARG_ENABLE("mysqlnd", "MySQLi with mysqlnd support", "no"); if (PHP_MYSQLI != "no") { mysqli_source = "mysqli.c " + "mysqli_api.c " + "mysqli_driver.c " + "mysqli_embedded.c " + "mysqli_exception.c " + "mysqli_fe.c " + "mysqli_nonapi.c " + "mysqli_prop.c " + "mysqli_report.c " + "mysqli_warning.c"; if (PHP_MYSQLND == "no") { if (CHECK_LIB("libmysql.lib", "mysqli", PHP_MYSQLI) && CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQLI", PHP_MYSQLI + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQLI)) { // No "mysqli_repl.c" when using "mysqlnd" mysqli_extra_sources = "mysqli_repl.c"; EXTENSION("mysqli", mysqli_source + " " + mysqli_extra_sources); AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); } else { WARNING("mysqli not enabled; libraries and headers not found"); } } else { mysqlnd_source = "mysqlnd.c " + // "mysqlnd_alloc.c " + "mysqlnd_charset.c " + "mysqlnd_loaddata.c " + "mysqlnd_palloc.c " + "mysqlnd_ps.c " + "mysqlnd_ps_codec.c " + "mysqlnd_qcache.c " + "mysqlnd_result.c " + "mysqlnd_result_meta.c " + "mysqlnd_statistics.c " + "mysqlnd_wireprotocol.c"; EXTENSION("mysqli", mysqli_source); // Specify that add "mysqlnd" sources, but use same object file // directory as the "mysqli" sources // FIXME the hard coded "ext/mysqli/mysqlnd" prevents pointing // out sources in another directory? Like above: PHP_MYSQLI + "\\include;" ADD_SOURCES("ext/mysqli/mysqlnd", mysqlnd_source, "mysqli"); AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); AC_DEFINE('HAVE_MYSQLND' , 1, 'MySQL native driver support enabled'); } } http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/config9.m4?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/config9.m4 +++ php-src/ext/mysqlnd/config9.m4 dnl dnl $Id: config9.m4,v 1.1 2007/07/25 23:47:32 jani Exp $ 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" 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 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 http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd.c +++ php-src/ext/mysqlnd/mysqlnd.c /* +----------------------------------------------------------------------+ | 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: mysqlnd.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" #include "mysqlnd_result.h" #include "mysqlnd_statistics.h" #include "mysqlnd_charset.h" #include "ext/standard/basic_functions.h" #include "ext/standard/php_lcg.h" #include "ext/standard/info.h" #define MYSQLND_SILENT /* the server doesn't support 4byte utf8, but let's make it forward compatible */ #define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte */ #define MYSQLND_MAX_ALLOWED_DB_LEN 256 /* 64 char * 4byte */ /* TODO : - Don't bind so tightly the metadata with the result set. This means that the metadata reading should not expect a MYSQLND_RES pointer, it does not need it, but return a pointer to the metadata (MYSQLND_FIELD *). For normal statements we will then just assign it to a member of MYSQLND_RES. For PS statements, it will stay as part of the statement (MYSQLND_STMT) between prepare and execute. At execute the new metadata will be sent by the server, so we will discard the old one and then finally attach it to the result set. This will make the code more clean, as a prepared statement won't have anymore stmt->result != NULL, as it is now, just to have where to store the metadata. - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING terminated by a string with ptr being NULL. Thus, multi-part messages can be sent to the network like writev() and this can save at least for mysqlnd_stmt_send_long_data() new malloc. This change will probably make the code in few other places cleaner. */ extern MYSQLND_CHARSET *mysqlnd_charsets; static const char * mysqlnd_server_gone = "MySQL server has gone away"; const char * mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now"; MYSQLND_STATS *mysqlnd_global_stats = NULL; static zend_bool mysqlnd_library_initted = FALSE; /* {{{ mysqlnd_library_init */ PHPAPI void mysqlnd_library_init() { if (mysqlnd_library_initted == FALSE) { mysqlnd_library_initted = TRUE; _mysqlnd_init_ps_subsystem(); mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS)); #ifdef ZTS mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc(); #endif } } /* }}} */ /* {{{ mysqlnd_library_end */ PHPAPI void mysqlnd_library_end() { if (mysqlnd_library_initted == TRUE) { #ifdef ZTS tsrm_mutex_free(mysqlnd_global_stats->LOCK_access); #endif free(mysqlnd_global_stats); mysqlnd_global_stats = NULL; mysqlnd_library_initted = FALSE; } } /* }}} */ /* {{{ mysqlnd_conn::free_contents */ static void MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) { zend_bool pers = conn->persistent; mysqlnd_local_infile_default(conn, TRUE); if (conn->current_result) { conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC); efree(conn->current_result); conn->current_result = NULL; } if (conn->net.stream) { php_stream_free(conn->net.stream, (pers) ? PHP_STREAM_FREE_RSRC_DTOR : PHP_STREAM_FREE_CLOSE); conn->net.stream = NULL; } if (conn->host) { pefree(conn->host, pers); conn->host = NULL; } if (conn->user) { pefree(conn->user, pers); conn->user = NULL; } if (conn->passwd) { pefree(conn->passwd, pers); conn->passwd = NULL; } if (conn->unix_socket) { pefree(conn->unix_socket, pers); conn->unix_socket = NULL; } if (conn->scheme) { pefree(conn->scheme, pers); conn->scheme = NULL; } if (conn->server_version) { pefree(conn->server_version, pers); conn->server_version = NULL; } if (conn->host_info) { pefree(conn->host_info, pers); conn->host_info = NULL; } if (conn->scramble) { pefree(conn->scramble, pers); conn->scramble = NULL; } if (conn->last_message) { pefree(conn->last_message, pers); conn->last_message = NULL; } if (conn->options.charset_name) { pefree(conn->options.charset_name, pers); conn->options.charset_name = NULL; } if (conn->options.num_commands) { unsigned int i; for (i=0; i < conn->options.num_commands; i++) { pefree(conn->options.init_commands[i], pers); } pefree(conn->options.init_commands, pers); conn->options.init_commands = NULL; } if (conn->options.cfg_file) { pefree(conn->options.cfg_file, pers); conn->options.cfg_file = NULL; } if (conn->options.cfg_section) { pefree(conn->options.cfg_section, pers); conn->options.cfg_section = NULL; } if (conn->options.ssl_key) { pefree(conn->options.ssl_key, pers); conn->options.ssl_key = NULL; } if (conn->options.ssl_cert) { pefree(conn->options.ssl_cert, pers); conn->options.ssl_cert = NULL; } if (conn->options.ssl_ca) { pefree(conn->options.ssl_ca, pers); conn->options.ssl_ca = NULL; } if (conn->options.ssl_capath) { pefree(conn->options.ssl_capath, pers); conn->options.ssl_capath = NULL; } if (conn->options.ssl_cipher) { pefree(conn->options.ssl_cipher, pers); conn->options.ssl_cipher = NULL; } if (conn->zval_cache) { mysqlnd_palloc_free_thd_cache_reference(&conn->zval_cache); conn->zval_cache = NULL; } if (conn->qcache) { mysqlnd_qcache_free_cache_reference(&conn->qcache); conn->qcache = NULL; } if (conn->net.cmd_buffer.buffer) { pefree(conn->net.cmd_buffer.buffer, pers); conn->net.cmd_buffer.buffer = NULL; } } /* }}} */ /* {{{ mysqlnd_conn::dtor */ static void MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) { conn->m->free_contents(conn TSRMLS_CC); pefree(conn, conn->persistent); } /* }}} */ /* {{{ mysqlnd_simple_command_handle_response */ enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command TSRMLS_DC) { enum_func_status ret; switch (ok_packet) { case PROT_OK_PACKET:{ php_mysql_packet_ok ok_response; PACKET_INIT_ALLOCA(ok_response, PROT_OK_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(ok_response, conn))) { if (!silent) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet", mysqlnd_command_to_text[command]); } } else { #ifndef MYSQLND_SILENT php_printf("\tOK from server\n"); #endif if (0xFF == ok_response.field_count) { /* The server signalled error. Set the error */ SET_CLIENT_ERROR(conn->error_info, ok_response.error_no, ok_response.sqlstate, ok_response.error); ret = FAIL; /* Cover a protocol design error: error packet does not contain the server status. Therefore, the client has no way to find out whether there are more result sets of a multiple-result-set statement pending. Luckily, in 5.0 an error always aborts execution of a statement, wherever it is a multi-statement or a stored procedure, so it should be safe to unconditionally turn off the flag here. */ conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS; SET_ERROR_AFF_ROWS(conn); } else { SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, ok_response.message, ok_response.message_len); conn->upsert_status.warning_count = ok_response.warning_count; conn->upsert_status.server_status = ok_response.server_status; conn->upsert_status.affected_rows = ok_response.affected_rows; conn->upsert_status.last_insert_id = ok_response.last_insert_id; } } PACKET_FREE_ALLOCA(ok_response); break; } case PROT_EOF_PACKET:{ php_mysql_packet_eof ok_response; PACKET_INIT_ALLOCA(ok_response, PROT_EOF_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(ok_response, conn))) { SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet", mysqlnd_command_to_text[command]); } } else if (0xFF == ok_response.field_count) { /* The server signalled error. Set the error */ SET_CLIENT_ERROR(conn->error_info, ok_response.error_no, ok_response.sqlstate, ok_response.error); SET_ERROR_AFF_ROWS(conn); } else if (0xFE != ok_response.field_count) { SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response.field_count); } } else { #ifndef MYSQLND_SILENT php_printf("\tOK from server\n"); #endif } PACKET_FREE_ALLOCA(ok_response); break; } default: ret = FAIL; SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %d passed to the function", ok_packet); break; } return ret; } /* }}} */ /* {{{ mysqlnd_simple_command */ enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum php_mysql_packet_type ok_packet, zend_bool silent TSRMLS_DC) { enum_func_status ret = PASS; php_mysql_packet_command cmd_packet; switch (conn->state) { case CONN_READY: break; case CONN_QUIT_SENT: SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); return FAIL; default: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } /* clean UPSERT info */ memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); SET_ERROR_AFF_ROWS(conn); SET_EMPTY_ERROR(conn->error_info); PACKET_INIT_ALLOCA(cmd_packet, PROT_CMD_PACKET); cmd_packet.command = command; if (arg && arg_len) { cmd_packet.argument = arg; cmd_packet.arg_len = arg_len; } if (! PACKET_WRITE_ALLOCA(cmd_packet, conn)) { if (!silent) { php_error(E_WARNING, "Error while sending %s packet", mysqlnd_command_to_text[command]); } SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); ret = FAIL; } else if (ok_packet != PROT_LAST) { ret = mysqlnd_simple_command_handle_response(conn, ok_packet, silent, command TSRMLS_CC); } /* There is no need to call FREE_ALLOCA on cmd_packet as the only allocated string is cmd_packet.argument and it was passed to us. We should not free it. */ return ret; } /* }}} */ /* {{{ mysqlnd_conn::set_server_option */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC) { char buffer[2]; int2store(buffer, (uint) option); return mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_start_psession */ PHPAPI void mysqlnd_restart_psession(MYSQLND *conn) { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_REUSED); } /* }}} */ /* {{{ mysqlnd_end_psession */ PHPAPI void mysqlnd_end_psession(MYSQLND *conn) { } /* }}} */ /* {{{ mysqlnd_connect */ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, char *host, char *user, char *passwd, unsigned int passwd_len, char *db, unsigned int db_len, unsigned int port, char *socket, unsigned int mysql_flags, MYSQLND_THD_ZVAL_PCACHE *zval_cache TSRMLS_DC) { char *transport = NULL, *errstr = NULL; char *hashed_details = NULL; int transport_len, errcode = 0; unsigned int streams_options = ENFORCE_SAFE_MODE; unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; zend_bool self_alloced = FALSE; struct timeval tv; zend_bool unix_socket = FALSE; const MYSQLND_CHARSET * charset; php_mysql_packet_greet greet_packet; php_mysql_packet_auth *auth_packet; php_mysql_packet_ok ok_packet; if (conn && conn->state != CONN_ALLOCED) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } if (!host || !host[0]) { host = "localhost"; } if (!user || !user[0]) { user = php_get_current_user(); } if (!passwd) { passwd = ""; passwd_len = 0; } if (!db) { db = ""; db_len = 0; } if (!port && !socket) { port = 3306; } #ifndef PHP_WIN32 if (!strncasecmp(host, "localhost", sizeof("localhost") - 1)) { if (!socket) { socket = "/tmp/mysql.sock"; } transport_len = spprintf(&transport, 0, "unix://%s", socket); unix_socket = TRUE; } else #endif { transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port); } if (conn->persistent) { struct timeval tv; gettimeofday(&tv, NULL); /* We should generate something unique */ spprintf(&hashed_details, 0, "%s@%s@%s@%ld@%ld@%0.8F", transport, user, db, tv.tv_sec, (long int)tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10); } PACKET_INIT_ALLOCA(greet_packet, PROT_GREET_PACKET); PACKET_INIT(auth_packet, PROT_AUTH_PACKET, php_mysql_packet_auth *); PACKET_INIT_ALLOCA(ok_packet, PROT_OK_PACKET); if (!conn) { conn = mysqlnd_init(FALSE); self_alloced = TRUE; } conn->state = CONN_ALLOCED; conn->net.packet_no = 0; if (conn->options.timeout_connect) { tv.tv_sec = conn->options.timeout_connect; tv.tv_usec = 0; } if (conn->persistent) { conn->scheme = pestrndup(transport, transport_len, 1); efree(transport); } else { conn->scheme = transport; } conn->net.stream = php_stream_xport_create(conn->scheme, transport_len, streams_options, streams_flags, hashed_details, (conn->options.timeout_connect) ? &tv : NULL, NULL /*ctx*/, &errstr, &errcode); if (hashed_details) { efree(hashed_details); } if (errstr || !conn->net.stream) { goto err; } if (conn->options.timeout_read) { tv.tv_sec = conn->options.timeout_read; tv.tv_usec = 0; php_stream_set_option(conn->net.stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); } if (!unix_socket) { /* Set TCP_NODELAY */ mysqlnd_set_sock_no_delay(conn->net.stream); } if (FAIL == PACKET_READ_ALLOCA(greet_packet, conn)) { #ifndef MYSQLND_SILENT php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet"); #endif goto err; } else if (greet_packet.error_no) { SET_CLIENT_ERROR(conn->error_info, greet_packet.error_no, greet_packet.sqlstate, greet_packet.error); goto err; } else if (greet_packet.pre41) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 " " is not supported. Server is %-.32s", greet_packet.server_version); SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Connecting to 3.22, 3.23 & 4.0 servers is not supported"); goto err; } conn->thread_id = greet_packet.thread_id; conn->protocol_version = greet_packet.protocol_version; conn->server_version = greet_packet.server_version; greet_packet.server_version = NULL; /* The string will be freed otherwise */ /* we allow load data local infile by default */ mysql_flags |= CLIENT_LOCAL_FILES; auth_packet->user = user; auth_packet->password = passwd; if (conn->options.charset_name && (charset = mysqlnd_find_charset_name(conn->options.charset_name))) { auth_packet->charset_no = charset->nr; #if PHP_MAJOR_VERSION >= 6 } else if (UG(unicode)) { auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ #endif } else { auth_packet->charset_no = greet_packet.charset_no; } auth_packet->db = db; auth_packet->db_len = db_len; auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL; auth_packet->client_flags= mysql_flags; conn->scramble = auth_packet->server_scramble_buf = pemalloc(SCRAMBLE_LENGTH, conn->persistent); memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH); PACKET_WRITE(auth_packet, conn); if (FAIL == PACKET_READ_ALLOCA(ok_packet, conn) || ok_packet.field_count >= 0xFE) { if (ok_packet.field_count == 0xFE) { /* old authentication with new server !*/ php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd cannot connect to MySQL 4.1+ using old authentication"); } else if (ok_packet.field_count == 0xFF) { if (ok_packet.sqlstate[0]) { if (!self_alloced) { strncpy(conn->error_info.sqlstate, ok_packet.sqlstate, sizeof(conn->error_info.sqlstate)); } #ifndef MYSQLND_SILENT php_error_docref(NULL TSRMLS_CC, E_WARNING, "ERROR:%d [SQLSTATE:%s] %s", ok_packet.error_no, ok_packet.sqlstate, ok_packet.error); #endif } if (!self_alloced) { conn->error_info.error_no = ok_packet.error_no; strncpy(conn->error_info.error, ok_packet.error, sizeof(conn->error_info.error)); } } } else { conn->state = CONN_READY; conn->user = pestrdup(user, conn->persistent); conn->passwd = pestrndup(passwd, passwd_len, conn->persistent); conn->port = port; if (host && !socket) { char *p; conn->host = pestrdup(host, conn->persistent); spprintf(&p, 0, "MySQL host info: %s via TCP/IP", conn->host); if (conn->persistent) { conn->host_info = pestrdup(p, 1); efree(p); } else { conn->host_info = p; } } else { conn->unix_socket = pestrdup(socket, conn->persistent); conn->host_info = pestrdup("MySQL host info: Localhost via UNIX socket", conn->persistent); } conn->client_flag = auth_packet->client_flags; conn->max_packet_size = auth_packet->max_packet_size; /* todo: check if charset is available */ conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no); conn->server_capabilities = greet_packet.server_capabilities; conn->upsert_status.warning_count = 0; conn->upsert_status.server_status = greet_packet.server_status; conn->upsert_status.affected_rows = 0; SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, ok_packet.message, ok_packet.message_len); SET_EMPTY_ERROR(conn->error_info); PACKET_FREE_ALLOCA(greet_packet); PACKET_FREE(auth_packet); PACKET_FREE_ALLOCA(ok_packet); conn->zval_cache = mysqlnd_palloc_get_thd_cache_reference(zval_cache); conn->net.cmd_buffer.length = 128L*1024L; conn->net.cmd_buffer.buffer = pemalloc(conn->net.cmd_buffer.length, conn->persistent); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_SUCCESS); { uint as_unicode = 1; conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode); } return conn; } err: PACKET_FREE_ALLOCA(greet_packet); PACKET_FREE(auth_packet); PACKET_FREE_ALLOCA(ok_packet); if (errstr) { SET_CLIENT_ERROR(conn->error_info, errcode, UNKNOWN_SQLSTATE, errstr); php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); efree(errstr); } if (conn->scheme) { pefree(conn->scheme, conn->persistent); conn->scheme = NULL; } /* This will also close conn->net.stream if it has been opened */ conn->m->free_contents(conn TSRMLS_CC); if (self_alloced) { /* We have alloced, thus there are no references to this object - we are free to kill it! */ conn->m->dtor(conn TSRMLS_CC); } else { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_FAILURE); } return NULL; } /* }}} */ /* {{{ mysqlnd_conn::query */ /* If conn->error_info.error_no is not zero, then we had an error. Still the result from the query is PASS */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; if (PASS != mysqlnd_simple_command(conn, COM_QUERY, query, query_len, PROT_LAST /* we will handle the OK packet*/, FALSE TSRMLS_CC)) { return FAIL; } /* Here read the result set. We don't do it in simple_command because it need information from the ok packet. We will fetch it ourselves. */ ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC); return ret; } /* }}} */ /* {{{ mysqlnd_conn::list_method */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, char *achtung_wild, char *par1 TSRMLS_DC) { char *show_query = NULL; size_t show_query_len; MYSQLND_RES *result = NULL; if (par1) { if (achtung_wild) { show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild); } else { show_query_len = spprintf(&show_query, 0, query, par1); } } else { if (achtung_wild) { show_query_len = spprintf(&show_query, 0, query, achtung_wild); } else { show_query_len = strlen(show_query = (char *)query); } } if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) { result = conn->m->store_result(conn TSRMLS_CC); } if (show_query != query) { efree(show_query); } return result; } /* }}} */ /* {{{ mysqlnd_conn::errno */ static unsigned int MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn) { return conn->error_info.error_no; } /* }}} */ /* {{{ mysqlnd_conn::error */ static const char * MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn) { return conn->error_info.error; } /* }}} */ /* {{{ mysqlnd_conn::sqlstate */ static const char * MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn) { return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; } /* }}} */ /* {{{ mysqlnd_old_escape_string */ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len) { return mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len); } /* }}} */ /* {{{ mysqlnd_conn::escape_string */ static ulong MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len) { if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) { return mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len); } return mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len); } /* }}} */ /* {{{ mysqlnd_conn::dump_debug_info */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC) { return mysqlnd_simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_conn::select_db */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC) { enum_func_status ret; ret = mysqlnd_simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE TSRMLS_CC); /* The server sends 0 but libmysql doesn't read it and has established a protocol of giving back -1. Thus we have to follow it :( */ SET_ERROR_AFF_ROWS(conn); return ret; } /* }}} */ /* {{{ mysqlnd_conn::ping */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret; ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, FALSE TSRMLS_CC); /* The server sends 0 but libmysql doesn't read it and has established a protocol of giving back -1. Thus we have to follow it :( */ SET_ERROR_AFF_ROWS(conn); return ret; } /* }}} */ /* {{{ mysqlnd_conn::stat */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC) { enum_func_status ret; php_mysql_packet_stats stats_header; ret = mysqlnd_simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE TSRMLS_CC); if (FAIL == ret) { return FAIL; } PACKET_INIT_ALLOCA(stats_header, PROT_STATS_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(stats_header, conn))) { return FAIL; } *message = stats_header.message; *message_len = stats_header.message_len; /* Ownership transfer */ stats_header.message = NULL; PACKET_FREE_ALLOCA(stats_header); return PASS; } /* }}} */ /* {{{ mysqlnd_conn::kill */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned long pid TSRMLS_DC) { enum_func_status ret; char buff[4]; int4store(buff, pid); /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */ if (pid != conn->thread_id) { ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE TSRMLS_CC); /* The server sends 0 but libmysql doesn't read it and has established a protocol of giving back -1. Thus we have to follow it :( */ SET_ERROR_AFF_ROWS(conn); } else if (PASS == (ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE TSRMLS_CC))) { conn->state = CONN_QUIT_SENT; } return ret; } /* }}} */ /* {{{ _mysqlnd_conn::set_charset */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC) { enum_func_status ret = PASS; char *query; size_t query_len; const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname); if (!charset) { SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Invalid characterset or character set not supported"); return FAIL; } query_len = spprintf(&query, 0, "SET NAMES %s", csname); if (FAIL == conn->m->query(conn, query, query_len TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query"); } else if (conn->error_info.error_no) { ret = FAIL; } else { conn->charset = charset; } efree(query); return ret; } /* }}} */ /* {{{ mysqlnd_conn::refresh */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, unsigned long options TSRMLS_DC) { zend_uchar bits[1]; int1store(bits, options); return mysqlnd_simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_conn::shutdown */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, unsigned long level TSRMLS_DC) { zend_uchar bits[1]; int1store(bits, level); return mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_send_close */ static enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) { enum_func_status ret = PASS; switch (conn->state) { case CONN_READY: ret = mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, conn->tmp_int? TRUE : FALSE TSRMLS_CC); /* Do nothing */ break; case CONN_SENDING_LOAD_DATA: /* Don't send COM_QUIT if we are in a middle of a LOAD DATA or we will crash (assert) a debug server. */ case CONN_NEXT_RESULT_PENDING: case CONN_QUERY_SENT: case CONN_FETCHING_DATA: MYSQLND_INC_CONN_STATISTIC(NULL, STAT_CLOSE_IN_MIDDLE); #ifndef MYSQLND_SILENT php_printf("Brutally closing connection [%p][%s]\n", conn, conn->scheme); #endif /* Do nothing, the connection will be brutally closed and the server will catch it and free close from its side. */ case CONN_ALLOCED: /* Allocated but not connected or there was failure when trying to connect with pre-allocated connect. Fall-through */ case CONN_QUIT_SENT: /* The user has killed its own connection */ break; } /* We hold one reference, and every other object which needs the connection does increase it by 1. */ conn->state = CONN_QUIT_SENT; return ret; } /* }}} */ /* {{{ mysqlnd_conn::close */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC) { enum_func_status ret = PASS; static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = { STAT_CLOSE_EXPLICIT, STAT_CLOSE_IMPLICIT, STAT_CLOSE_DISCONNECT }; enum_mysqlnd_collected_stats stat = close_type_to_stat_map[close_type]; MYSQLND_INC_CONN_STATISTIC(NULL, stat); mysqlnd_send_close(conn TSRMLS_CC); conn->m->free_reference(conn TSRMLS_CC); return ret; } /* }}} */ /* {{{ mysqlnd_conn::get_reference */ static MYSQLND * MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn) { ++conn->refcount; return conn; } /* }}} */ /* {{{ mysqlnd_conn::free_reference */ static void MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC) { if (!(--conn->refcount)) { /* No multithreading issues as we don't share the connection :) This will free the object too, of course because references has reached zero. */ mysqlnd_send_close(conn TSRMLS_CC); conn->m->dtor(conn TSRMLS_CC); } } /* }}} */ /* {{{ mysqlnd_conn::field_count */ static unsigned int MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn) { return conn->field_count; } /* }}} */ /* {{{ mysqlnd_conn::insert_id */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn) { return conn->upsert_status.last_insert_id; } /* }}} */ /* {{{ mysqlnd_conn::affected_rows */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn) { return conn->upsert_status.affected_rows; } /* }}} */ /* {{{ mysqlnd_conn::warning_count */ static unsigned int MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn) { return conn->upsert_status.warning_count; } /* }}} */ /* {{{ mysqlnd_conn::info */ static const char * MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn) { return conn->last_message; } /* }}} */ /* {{{ mysqlnd_get_client_info */ PHPAPI const char * mysqlnd_get_client_info() { return MYSQLND_VERSION; } /* }}} */ /* {{{ mysqlnd_get_client_version */ PHPAPI unsigned int mysqlnd_get_client_version() { return MYSQLND_VERSION_ID; } /* }}} */ /* {{{ mysqlnd_conn::get_server_info */ static const char * MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn) { return conn->server_version; } /* }}} */ /* {{{ mysqlnd_conn::get_host_info */ static const char * MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn) { return conn->host_info; } /* }}} */ /* {{{ mysqlnd_conn::get_proto_info */ static unsigned int MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn) { return conn->protocol_version; } /* }}} */ /* {{{ mysqlnd_conn::charset_name */ static const char * MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn) { return conn->charset->name; } /* }}} */ /* {{{ mysqlnd_conn::thread_id */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn) { return conn->thread_id; } /* }}} */ /* {{{ mysqlnd_conn::get_server_version */ static unsigned long MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn) { long major, minor, patch; char *p; if (!(p = conn->server_version)) { return 0; } major = strtol(p, &p, 10); p += 1; /* consume the dot */ minor = strtol(p, &p, 10); p += 1; /* consume the dot */ patch = strtol(p, &p, 10); return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch)); } /* }}} */ /* {{{ mysqlnd_conn::more_results */ static zend_bool MYSQLND_METHOD(mysqlnd_conn,more_results)(const MYSQLND * const conn) { /* (conn->state == CONN_NEXT_RESULT_PENDING) too */ return conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE; } /* }}} */ /* {{{ mysqlnd_conn::next_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret; if (conn->state != CONN_NEXT_RESULT_PENDING) { return FAIL; } SET_EMPTY_ERROR(conn->error_info); conn->upsert_status.affected_rows= ~(mynd_ulonglong) 0; /* We are sure that there is a result set, since conn->state is set accordingly in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered() */ if (FAIL == (ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC))) { #ifndef MYSQLND_SILENT php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error"); #endif conn->state = CONN_QUIT_SENT; } return ret; } /* }}} */ /* {{{ mysqlnd_field_type_name */ PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type) { switch(field_type) { case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: return "string"; case FIELD_TYPE_TINY: case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: case FIELD_TYPE_LONGLONG: case FIELD_TYPE_INT24: return "int"; case FIELD_TYPE_FLOAT: case FIELD_TYPE_DOUBLE: case FIELD_TYPE_DECIMAL: case FIELD_TYPE_NEWDECIMAL: return "real"; case FIELD_TYPE_TIMESTAMP: return "timestamp"; case FIELD_TYPE_YEAR: return "year"; case FIELD_TYPE_DATE: case FIELD_TYPE_NEWDATE: return "date"; case FIELD_TYPE_TIME: return "time"; case FIELD_TYPE_SET: return "set"; case FIELD_TYPE_ENUM: return "enum"; case FIELD_TYPE_GEOMETRY: return "geometry"; case FIELD_TYPE_DATETIME: return "datetime"; case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_LONG_BLOB: case FIELD_TYPE_BLOB: return "blob"; case FIELD_TYPE_NULL: return "null"; case FIELD_TYPE_BIT: return "bit"; default: return "unknown"; } } /* }}} */ /* {{{ mysqlnd_conn::change_user */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, const char *user, const char *passwd, const char *db TSRMLS_DC) { /* User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3 Stack space is not that expensive, so use a bit more to be protected against stack overrungs. */ size_t user_len; enum_func_status ret; php_mysql_packet_chg_user_resp chg_user_resp; char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1]; char *p = buffer; if (!user) { user = ""; } if (!passwd) { passwd = ""; } if (!db) { db = ""; } /* 1. user ASCIIZ */ user_len = strlen(user); memcpy(p, user, MIN(user_len, MYSQLND_MAX_ALLOWED_DB_LEN)); p += user_len; *p++ = '\0'; /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */ if (passwd[0]) { *p++ = SCRAMBLE_LENGTH; php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd); p += SCRAMBLE_LENGTH; } else { *p++ = '\0'; } /* 3. db ASCIIZ */ if (db[0]) { size_t db_len = strlen(db); memcpy(p, db, MIN(db_len, MYSQLND_MAX_ALLOWED_DB_LEN)); p += db_len; } *p++ = '\0'; if (PASS != mysqlnd_simple_command(conn, COM_CHANGE_USER, buffer, p - buffer, PROT_LAST /* we will handle the OK packet*/, FALSE TSRMLS_CC)) { return FAIL; } PACKET_INIT_ALLOCA(chg_user_resp, PROT_CHG_USER_PACKET); ret = PACKET_READ_ALLOCA(chg_user_resp, conn); conn->error_info = chg_user_resp.error_info; PACKET_FREE_ALLOCA(chg_user_resp); if (conn->error_info.error_no) { ret = FAIL; /* COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune. bug#25371 mysql_change_user() triggers "packets out of sync" When it gets fixed, there should be one more check here */ if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) { php_mysql_packet_ok redundant_error_packet; PACKET_INIT_ALLOCA(redundant_error_packet, PROT_OK_PACKET); PACKET_READ_ALLOCA(redundant_error_packet, conn); PACKET_FREE_ALLOCA(redundant_error_packet); } } /* Here we should close all statements. Unbuffered queries should not be a problem as we won't allow sending COM_CHANGE_USER. */ return ret; } /* }}} */ /* {{{ mysqlnd_conn::set_client_option */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, enum mysqlnd_option option, const char * const value) { switch (option) { case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE: conn->options.numeric_and_datetime_as_unicode = *(uint*) value; break; #ifdef MYSQLND_STRING_TO_INT_CONVERSION case MYSQLND_OPT_INT_AND_YEAR_AS_INT: conn->options.int_and_year_as_int = *(uint*) value; break; #endif case MYSQL_OPT_CONNECT_TIMEOUT: conn->options.timeout_connect = *(uint*) value; break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_READ_TIMEOUT: conn->options.timeout_read = *(uint*) value; break; case MYSQL_OPT_WRITE_TIMEOUT: conn->options.timeout_write = *(uint*) value; break; #endif case MYSQL_OPT_LOCAL_INFILE: if (!value || (*(uint*) value) ? 1 : 0) { conn->options.flags |= CLIENT_LOCAL_FILES; } else { conn->options.flags &= ~CLIENT_LOCAL_FILES; } break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_COMPRESS: #endif case MYSQL_INIT_COMMAND: case MYSQL_READ_DEFAULT_FILE: case MYSQL_READ_DEFAULT_GROUP: #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CLIENT_IP: case MYSQL_REPORT_DATA_TRUNCATION: case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: #endif /* currently not supported. Todo!! */ break; case MYSQL_SET_CHARSET_NAME: conn->options.charset_name = pestrdup(value, conn->persistent); break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CHARSET_DIR: case MYSQL_OPT_RECONNECT: case MYSQL_OPT_PROTOCOL: /* we don't need external character sets, all character sets are compiled in. For compatibility we just ignore this setting. Same for protocol, we don't support old protocol */ case MYSQL_OPT_USE_REMOTE_CONNECTION: case MYSQL_OPT_USE_EMBEDDED_CONNECTION: case MYSQL_OPT_GUESS_CONNECTION: /* todo: throw an error, we don't support embedded */ break; #endif #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_NAMED_PIPE: case MYSQL_SHARED_MEMORY_BASE_NAME: case MYSQL_OPT_USE_RESULT: case MYSQL_SECURE_AUTH: /* not sure, todo ? */ #endif default: return FAIL; } return PASS; } /* }}} */ /* {{{ _mysqlnd_conn::use_result */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) { MYSQLND_RES *result; if (!conn->current_result) { return NULL; } /* Nothing to store for UPSERT/LOAD DATA */ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS); result = conn->current_result; conn->current_result = NULL; result->conn = conn->m->get_reference(conn); return result->m.use_result(result, FALSE TSRMLS_CC); } /* }}} */ /* {{{ _mysqlnd_conn::store_result */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) { MYSQLND_RES *result; if (!conn->current_result) { return NULL; } /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); result = conn->current_result; conn->current_result = NULL; return result->m.store_result(result, conn, FALSE TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_conn::get_connection_stats */ void MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { mysqlnd_fill_stats_hash(&(conn->stats), return_value TSRMLS_CC ZEND_FILE_LINE_CC); } /* }}} */ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn); MYSQLND_CLASS_METHODS_START(mysqlnd_conn) MYSQLND_METHOD(mysqlnd_conn, escape_string), MYSQLND_METHOD(mysqlnd_conn, set_charset), MYSQLND_METHOD(mysqlnd_conn, query), MYSQLND_METHOD(mysqlnd_conn, use_result), MYSQLND_METHOD(mysqlnd_conn, store_result), MYSQLND_METHOD(mysqlnd_conn, next_result), MYSQLND_METHOD(mysqlnd_conn, more_results), _mysqlnd_stmt_init, MYSQLND_METHOD(mysqlnd_conn, shutdown), MYSQLND_METHOD(mysqlnd_conn, refresh), MYSQLND_METHOD(mysqlnd_conn, ping), MYSQLND_METHOD(mysqlnd_conn, kill), MYSQLND_METHOD(mysqlnd_conn, select_db), MYSQLND_METHOD(mysqlnd_conn, dump_debug_info), MYSQLND_METHOD(mysqlnd_conn, change_user), MYSQLND_METHOD(mysqlnd_conn, errno), MYSQLND_METHOD(mysqlnd_conn, error), MYSQLND_METHOD(mysqlnd_conn, sqlstate), MYSQLND_METHOD(mysqlnd_conn, thread_id), MYSQLND_METHOD(mysqlnd_conn, get_connection_stats), MYSQLND_METHOD(mysqlnd_conn, get_server_version), MYSQLND_METHOD(mysqlnd_conn, get_server_info), MYSQLND_METHOD(mysqlnd_conn, stat), MYSQLND_METHOD(mysqlnd_conn, get_host_info), MYSQLND_METHOD(mysqlnd_conn, get_proto_info), MYSQLND_METHOD(mysqlnd_conn, info), MYSQLND_METHOD(mysqlnd_conn, charset_name), MYSQLND_METHOD(mysqlnd_conn, list_method), MYSQLND_METHOD(mysqlnd_conn, insert_id), MYSQLND_METHOD(mysqlnd_conn, affected_rows), MYSQLND_METHOD(mysqlnd_conn, warning_count), MYSQLND_METHOD(mysqlnd_conn, field_count), MYSQLND_METHOD(mysqlnd_conn, set_server_option), MYSQLND_METHOD(mysqlnd_conn, set_client_option), MYSQLND_METHOD(mysqlnd_conn, free_contents), MYSQLND_METHOD(mysqlnd_conn, close), MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor), MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference), MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference), MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_init */ PHPAPI MYSQLND *mysqlnd_init(zend_bool persistent) { MYSQLND *ret = pecalloc(1, sizeof(MYSQLND), persistent); SET_ERROR_AFF_ROWS(ret); ret->persistent = persistent; ret->m = & mysqlnd_mysqlnd_conn_methods; ret->m->get_reference(ret); return ret; } /* }}} */ /* {{{ mysqlnd_functions[] * * Every user visible function must have an entry in mysqlnd_functions[]. */ static zend_function_entry mysqlnd_functions[] = { {NULL, NULL, NULL} /* Must be the last line in mysqlnd_functions[] */ }; /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(mysqlnd) { mysqlnd_library_init(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(mysqlnd) { mysqlnd_library_end(); return SUCCESS; } /* }}} */ /* {{{ mysqlnd_minfo_print_hash */ #if PHP_MAJOR_VERSION >= 6 PHPAPI void mysqlnd_minfo_print_hash(zval *values) { zval **values_entry; HashPosition pos_values; zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&values_entry, &pos_values) == SUCCESS) { TSRMLS_FETCH(); zstr string_key; uint string_key_len; ulong num_key; char *s = NULL; zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values); convert_to_string(*values_entry); if (UG(unicode)) { int s_len; if (zend_unicode_to_string(ZEND_U_CONVERTER(UG(runtime_encoding_conv)), &s, &s_len, string_key.u, string_key_len TSRMLS_CC) == SUCCESS) { php_info_print_table_row(2, s, Z_STRVAL_PP(values_entry)); } if (s) { efree(s); } } else { php_info_print_table_row(2, string_key.s, Z_STRVAL_PP(values_entry)); } zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values); } } #else void mysqlnd_minfo_print_hash(zval *values) { zval **values_entry; HashPosition pos_values; zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&values_entry, &pos_values) == SUCCESS) { char *string_key; uint string_key_len; ulong num_key; zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values); convert_to_string(*values_entry); php_info_print_table_row(2, string_key, Z_STRVAL_PP(values_entry)); zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values); } } #endif /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(mysqlnd) { zval values; php_info_print_table_start(); php_info_print_table_header(2, "mysqlnd", "enabled"); php_info_print_table_row(2, "Version", mysqlnd_get_client_info()); /* Print client stats */ php_info_print_table_header(2, "Client statistics", ""); mysqlnd_get_client_stats(&values); mysqlnd_minfo_print_hash(&values); zval_dtor(&values); php_info_print_table_end(); } /* }}} */ /* {{{ mysqlnd_module_entry */ zend_module_entry mysqlnd_module_entry = { STANDARD_MODULE_HEADER, "mysqlnd", mysqlnd_functions, PHP_MINIT(mysqlnd), PHP_MSHUTDOWN(mysqlnd), NULL, NULL, PHP_MINFO(mysqlnd), MYSQLND_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd.h +++ php-src/ext/mysqlnd/mysqlnd.h /* +----------------------------------------------------------------------+ | 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: mysqlnd.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_H #define MYSQLND_H #define MYSQLND_VERSION "mysqlnd 5.0.2-dev - 070702 - $Revision: 1.1 $" #define MYSQLND_VERSION_ID 50002 #define phpext_mysqlnd_ptr &mysqlnd_module_entry extern zend_module_entry mysqlnd_module_entry; /* This forces inlining of some accessor functions */ #define MYSQLND_USE_OPTIMISATIONS 0 /* #define MYSQLND_STRING_TO_INT_CONVERSION */ /* This force mysqlnd to do a single (or more depending on ammount of data) non-blocking read() calls before sending a command to the server. Useful for debugging, if previous function hasn't consumed all the output sent to it - like stmt_send_long_data() error because the data was larger that max_allowed_packet_size, and COM_STMT_SEND_LONG_DATA by protocol doesn't use response packets, thus letting the next command to fail miserably, if the connector implementor is not aware of this deficiency. Should be off on production systems, if of course measured performance degradation is not minimal. */ #define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1 #include "mysqlnd_portability.h" #ifdef ZTS #include "TSRM.h" #endif #include "mysqlnd_enum_n_def.h" /* /-----> CONN_CLOSE <---------------\ | ^ \ | | \ CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA ^ | \-------------------------------------/ */ typedef enum mysqlnd_connection_state { CONN_ALLOCED = 0, CONN_READY, CONN_QUERY_SENT, CONN_SENDING_LOAD_DATA, CONN_FETCHING_DATA, CONN_NEXT_RESULT_PENDING, CONN_QUIT_SENT, /* object is "destroyed" at this stage */ } enum_mysqlnd_connection_state; typedef enum mysqlnd_stmt_state { MYSQLND_STMT_INITTED = 0, MYSQLND_STMT_PREPARED, MYSQLND_STMT_EXECUTED, MYSQLND_STMT_WAITING_USE_OR_STORE, MYSQLND_STMT_USE_OR_STORE_CALLED, MYSQLND_STMT_USER_FETCHING, /* fetch_row_buff or fetch_row_unbuf */ } enum_mysqlnd_stmt_state; typedef enum param_bind_flags { MYSQLND_PARAM_BIND_BLOB_USED = 1 } enum_param_bind_flags; /* PS */ enum mysqlnd_stmt_attr { STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS }; enum myslqnd_cursor_type { CURSOR_TYPE_NO_CURSOR= 0, CURSOR_TYPE_READ_ONLY= 1, CURSOR_TYPE_FOR_UPDATE= 2, CURSOR_TYPE_SCROLLABLE= 4 }; typedef enum mysqlnd_connection_close_type { MYSQLND_CLOSE_EXPLICIT = 0, MYSQLND_CLOSE_IMPLICIT, MYSQLND_CLOSE_DISCONNECTED, MYSQLND_CLOSE_LAST /* for checking, should always be last */ } enum_connection_close_type; typedef enum mysqlnd_collected_stats { STAT_BYTES_SENT, STAT_BYTES_RECEIVED, STAT_PACKETS_SENT, STAT_PACKETS_RECEIVED, STAT_PROTOCOL_OVERHEAD_IN, STAT_PROTOCOL_OVERHEAD_OUT, STAT_RSET_QUERY, STAT_NON_RSET_QUERY, STAT_NO_INDEX_USED, STAT_BAD_INDEX_USED, STAT_BUFFERED_SETS, STAT_UNBUFFERED_SETS, STAT_PS_BUFFERED_SETS, STAT_PS_UNBUFFERED_SETS, STAT_FLUSHED_NORMAL_SETS, STAT_FLUSHED_PS_SETS, STAT_ROWS_FETCHED_FROM_SERVER, STAT_ROWS_FETCHED_FROM_CLIENT, STAT_ROWS_SKIPPED, STAT_COPY_ON_WRITE_SAVED, STAT_COPY_ON_WRITE_PERFORMED, STAT_CMD_BUFFER_TOO_SMALL, STAT_CONNECT_SUCCESS, STAT_CONNECT_FAILURE, STAT_CONNECT_REUSED, STAT_CLOSE_EXPLICIT, STAT_CLOSE_IMPLICIT, STAT_CLOSE_DISCONNECT, STAT_CLOSE_IN_MIDDLE, STAT_FREE_RESULT_EXPLICIT, STAT_FREE_RESULT_IMPLICIT, STAT_STMT_CLOSE_EXPLICIT, STAT_STMT_CLOSE_IMPLICIT, STAT_LAST /* Should be always the last */ } enum_mysqlnd_collected_stats; typedef struct st_mysqlnd_cmd_buffer { zend_uchar *buffer; size_t length; } MYSQLND_CMD_BUFFER; typedef struct st_mysqlnd_field { char *name; /* Name of column */ char *org_name; /* Original column name, if an alias */ char *table; /* Table of column if column was a field */ char *org_table; /* Org table name, if table was an alias */ char *db; /* Database for table */ char *catalog; /* Catalog for table */ char *def; /* Default value (set by mysql_list_fields) */ unsigned long length; /* Width of column (create length) */ unsigned long max_length; /* Max width for selected set */ unsigned int name_length; unsigned int org_name_length; unsigned int table_length; unsigned int org_table_length; unsigned int db_length; unsigned int catalog_length; unsigned int def_length; unsigned int flags; /* Diverse flags */ unsigned int decimals; /* Number of decimals in field */ unsigned int charsetnr; /* Character set */ enum mysqlnd_field_types type; /* Type of field. See mysql_com.h for types */ char *root; size_t root_len; } MYSQLND_FIELD; typedef struct st_mysqlnd_upsert_result { unsigned int warning_count; unsigned int server_status; unsigned long long affected_rows; unsigned long long last_insert_id; } mysqlnd_upsert_status; typedef struct st_mysqlnd_error_info { char error[MYSQLND_ERRMSG_SIZE+1]; char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; unsigned int error_no; } mysqlnd_error_info; typedef struct st_mysqlnd_zval_pcache MYSQLND_ZVAL_PCACHE; typedef struct st_mysqlnd_thread_zval_pcache MYSQLND_THD_ZVAL_PCACHE; typedef struct st_mysqlnd_qcache MYSQLND_QCACHE; typedef struct st_mysqlnd_infile_info { php_stream *fd; int error_no; char error_msg[MYSQLND_ERRMSG_SIZE + 1]; const char *filename; zval *callback; } MYSQLND_INFILE_INFO; /* character set information */ typedef struct st_mysqlnd_charset { uint nr; char *name; char *collation; uint char_minlen; uint char_maxlen; uint dangerous_for_escape_backslash; uint (*mb_charlen)(uint c); uint (*mb_valid)(const char *start, const char *end); } MYSQLND_CHARSET; /* local infile handler */ typedef struct st_mysqlnd_infile { int (*local_infile_init)(void **ptr, char *filename, void **userdata TSRMLS_DC); int (*local_infile_read)(void *ptr, char *buf, uint buf_len TSRMLS_DC); int (*local_infile_error)(void *ptr, char *error_msg, uint error_msg_len TSRMLS_DC); void (*local_infile_end)(void *ptr TSRMLS_DC); zval *callback; void *userdata; } MYSQLND_INFILE; typedef struct st_mysqlnd_option { /* timeouts */ uint timeout_connect; uint timeout_read; uint timeout_write; ulong flags; /* init commands - we need to send them to server directly after connect */ uint num_commands; char **init_commands; /* configuration file information */ char *cfg_file; char *cfg_section; /* SSL information */ char *ssl_key; char *ssl_cert; char *ssl_ca; char *ssl_capath; char *ssl_cipher; zend_bool use_ssl; char *charset_name; /* maximum allowed packet size for communication */ ulong max_allowed_packet; zend_bool numeric_and_datetime_as_unicode; #ifdef MYSQLND_STRING_TO_INT_CONVERSION zend_bool int_and_year_as_int; #endif } MYSQLND_OPTION; typedef struct st_mysqlnd_connection MYSQLND; typedef struct st_mysqlnd_res MYSQLND_RES; typedef char** MYSQLND_ROW; /* return data as array of strings */ typedef struct st_mysqlnd_stmt MYSQLND_STMT; typedef unsigned int MYSQLND_FIELD_OFFSET; typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND; typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND; typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA; typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED; typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED; typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC); typedef struct st_mysqlnd_stats { my_uint64 values[STAT_LAST]; #ifdef ZTS MUTEX_T LOCK_access; #endif } MYSQLND_STATS; typedef struct st_mysqlnd_net { php_stream *stream; /* sequence for simple checking of correct packets */ zend_uchar packet_no; #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND zend_uchar last_command; #endif /* cmd buffer */ MYSQLND_CMD_BUFFER cmd_buffer; } MYSQLND_NET; struct st_mysqlnd_conn_methods { ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len); enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC); MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC); enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC); zend_bool (*more_results)(const MYSQLND * const conn); MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn); enum_func_status (*shutdown_server)(MYSQLND * const conn, unsigned long level TSRMLS_DC); enum_func_status (*refresh_server)(MYSQLND * const conn, unsigned long options TSRMLS_DC); enum_func_status (*ping)(MYSQLND * const conn TSRMLS_DC); enum_func_status (*kill_connection)(MYSQLND *conn, unsigned long pid TSRMLS_DC); enum_func_status (*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); enum_func_status (*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); enum_func_status (*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db TSRMLS_DC); unsigned int (*get_error_no)(const MYSQLND * const conn); const char * (*get_error_str)(const MYSQLND * const conn); const char * (*get_sqlstate)(const MYSQLND * const conn); mynd_ulonglong (*get_thread_id)(const MYSQLND * const conn); void (*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); unsigned long (*get_server_version)(const MYSQLND * const conn); const char * (*get_server_information)(const MYSQLND * const conn); enum_func_status (*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); const char * (*get_host_information)(const MYSQLND * const conn); unsigned int (*get_protocol_information)(const MYSQLND * const conn); const char * (*get_last_message)(const MYSQLND * const conn); const char * (*charset_name)(const MYSQLND * const conn); MYSQLND_RES * (*list_method)(MYSQLND *conn, const char *query, char *achtung_wild, char *par1 TSRMLS_DC); mynd_ulonglong (*get_last_insert_id)(const MYSQLND * const conn); mynd_ulonglong (*get_affected_rows)(const MYSQLND * const conn); unsigned int (*get_warning_count)(const MYSQLND * const conn); unsigned int (*get_field_count)(const MYSQLND * const conn); enum_func_status (*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); enum_func_status (*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value); void (*free_contents)(MYSQLND *conn TSRMLS_DC); /* private */ enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */ MYSQLND * (*get_reference)(MYSQLND * const conn); void (*free_reference)(MYSQLND * const conn TSRMLS_DC); }; struct st_mysqlnd_res_methods { mysqlnd_fetch_row_func fetch_row; mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); mynd_ulonglong (*num_rows)(const MYSQLND_RES * const result); unsigned int (*num_fields)(const MYSQLND_RES * const result); enum_func_status (*skip_result)(MYSQLND_RES * const result TSRMLS_DC); enum_func_status (*seek_data)(MYSQLND_RES * result, mynd_ulonglong row); MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset); MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result); MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES * const result); MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr); enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC); unsigned long * (*fetch_lengths)(MYSQLND_RES * const result); void (*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC); void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC); }; struct st_mysqlnd_res_meta_methods { MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES_METADATA * const meta); MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES_METADATA * const meta); enum_func_status (*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC); MYSQLND_RES_METADATA * (*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent); void (*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC); }; struct st_mysqlnd_stmt_methods { enum_func_status (*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC); MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, mynd_ulonglong row); enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC); enum_func_status (*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); enum_func_status (*bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind); enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind); enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, const char * const data, unsigned long length TSRMLS_DC); MYSQLND_RES * (*get_parameter_metadata)(MYSQLND_STMT * const stmt); MYSQLND_RES * (*get_result_metadata)(MYSQLND_STMT * const stmt); mynd_ulonglong (*get_last_insert_id)(const MYSQLND_STMT * const stmt); mynd_ulonglong (*get_affected_rows)(const MYSQLND_STMT * const stmt); mynd_ulonglong (*get_num_rows)(const MYSQLND_STMT * const stmt); unsigned int (*get_param_count)(const MYSQLND_STMT * const stmt); unsigned int (*get_field_count)(const MYSQLND_STMT * const stmt); unsigned int (*get_warning_count)(const MYSQLND_STMT * const stmt); unsigned int (*get_error_no)(const MYSQLND_STMT * const stmt); const char * (*get_error_str)(const MYSQLND_STMT * const stmt); const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt); enum_func_status (*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value); enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value); }; struct st_mysqlnd_connection { /* Operation related */ MYSQLND_NET net; /* Information related */ char *host; char *unix_socket; char *user; char *passwd; unsigned int *passwd_len; char *scheme; unsigned long long thread_id; char *server_version; char *host_info; unsigned char *scramble; const MYSQLND_CHARSET *charset; MYSQLND_INFILE infile; unsigned int protocol_version; unsigned long max_packet_size; unsigned int port; unsigned long client_flag; unsigned long server_capabilities; int tmp_int; /* For UPSERT queries */ mysqlnd_upsert_status upsert_status; char *last_message; unsigned int last_message_len; /* If error packet, we use these */ mysqlnd_error_info error_info; /* To prevent queries during unbuffered fetches. Also to mark the connection as destroyed for garbage collection. */ enum mysqlnd_connection_state state; enum_mysqlnd_query_type last_query_type; /* Temporary storage between query and (use|store)_result() call */ MYSQLND_RES *current_result; /* How many result sets reference this connection. It won't be freed until this number reaches 0. The last one, please close the door! :-) The result set objects can determine by inspecting 'quit_sent' whether the connection is still valid. */ unsigned int refcount; /* Temporal storage for mysql_query */ unsigned int field_count; /* persistent connection */ zend_bool persistent; /* options */ MYSQLND_OPTION options; /* zval cache */ MYSQLND_THD_ZVAL_PCACHE *zval_cache; /* qcache */ MYSQLND_QCACHE *qcache; /* stats */ MYSQLND_STATS stats; struct st_mysqlnd_conn_methods *m; }; typedef struct st_php_mysql_packet_row php_mysql_packet_row; struct mysqlnd_field_hash_key { zend_bool is_numeric; unsigned long key; #if PHP_MAJOR_VERSION >= 6 zstr ustr; unsigned int ulen; #endif }; struct st_mysqlnd_result_metadata { MYSQLND_FIELD *fields; struct mysqlnd_field_hash_key *zend_hash_keys; unsigned int current_field; unsigned int field_count; /* We need this to make fast allocs in rowp_read */ unsigned int bit_fields_count; size_t bit_fields_total_len; /* trailing \0 not counted */ struct st_mysqlnd_res_meta_methods *m; }; struct st_mysqlnd_buffered_result { zval ***data; zval ***data_cursor; zend_uchar **row_buffers; mynd_ulonglong row_count; zend_bool persistent; MYSQLND_QCACHE *qcache; unsigned int references; zend_bool async_invalid; mysqlnd_error_info error_info; }; struct st_mysqlnd_unbuffered_result { /* For unbuffered (both normal and PS) */ zval **last_row_data; zend_uchar *last_row_buffer; mynd_ulonglong row_count; zend_bool eof_reached; }; struct st_mysqlnd_res { struct st_mysqlnd_res_methods m; MYSQLND *conn; enum_mysqlnd_res_type type; unsigned int field_count; /* For metadata functions */ MYSQLND_RES_METADATA *meta; /* To be used with store_result() - both normal and PS */ MYSQLND_RES_BUFFERED *data; MYSQLND_RES_UNBUFFERED *unbuf; /* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ unsigned long *lengths; php_mysql_packet_row *row_packet; /* Unused for PS */ /* zval cache */ MYSQLND_THD_ZVAL_PCACHE *zval_cache; }; #define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1 struct st_mysqlnd_param_bind { zval *zv; zend_uchar type; enum_param_bind_flags flags; }; struct st_mysqlnd_result_bind { zval *zv; zend_uchar original_type; zend_bool bound; }; struct st_mysqlnd_stmt { MYSQLND *conn; unsigned long stmt_id; unsigned long flags;/* cursor is set here */ enum_mysqlnd_stmt_state state; unsigned int warning_count; MYSQLND_RES *result; unsigned int field_count; unsigned int param_count; unsigned char send_types_to_server; MYSQLND_PARAM_BIND *param_bind; MYSQLND_RESULT_BIND *result_bind; zend_bool result_zvals_separated_once; mysqlnd_upsert_status upsert_status; mysqlnd_error_info error_info; zend_bool update_max_length; unsigned long prefetch_rows; zend_bool cursor_exists; mysqlnd_stmt_use_or_store_func default_rset_handler; MYSQLND_CMD_BUFFER cmd_buffer; struct st_mysqlnd_stmt_methods *m; }; /* Library related */ PHPAPI void mysqlnd_library_init(); PHPAPI void mysqlnd_library_end(); PHPAPI void mysqlnd_restart_psession(MYSQLND *conn); PHPAPI void mysqlnd_end_psession(MYSQLND *conn); PHPAPI void mysqlnd_minfo_print_hash(zval *values); #define mysqlnd_thread_safe() TRUE PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint charsetno); PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const charsetname); /* Connect */ PHPAPI MYSQLND * mysqlnd_init(zend_bool persistent); PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, char *host, char *user, char *passwd, unsigned int passwd_len, char *db, unsigned int db_len, unsigned int port, char *socket, unsigned int mysql_flags, MYSQLND_THD_ZVAL_PCACHE *zval_cache TSRMLS_DC); #define mysqlnd_change_user(conn, user, passwd, db) (conn)->m->change_user((conn), (user), (passwd), (db) TSRMLS_CC) #define mysqlnd_debug(x) /* Query */ #define mysqlnd_fetch_into(result, flags, ret_val, ext) (result)->m.fetch_into((result), (flags), (ret_val), (ext) TSRMLS_CC ZEND_FILE_LINE_CC) #define mysqlnd_fetch_all(result, flags, return_value) (result)->m.fetch_all((result), (flags), (return_value) TSRMLS_CC ZEND_FILE_LINE_CC) #define mysqlnd_result_fetch_field_data(res,offset,ret) (res)->m.fetch_field_data((res), (offset), (ret) TSRMLS_CC) #define mysqlnd_get_connection_stats(conn, values) (conn)->m->get_statistics((conn), (values) TSRMLS_CC ZEND_FILE_LINE_CC) #define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats((values) TSRMLS_CC ZEND_FILE_LINE_CC) #define mysqlnd_close(conn,is_forced) (conn)->m->close((conn), (is_forced) TSRMLS_CC) #define mysqlnd_query(conn, query_str, query_len) (conn)->m->query((conn), (query_str), (query_len) TSRMLS_CC) #define mysqlnd_unbuffered_skip_result(result) (result)->m.skip_result((result) TSRMLS_CC) #define mysqlnd_use_result(conn) (conn)->m->use_result((conn) TSRMLS_CC) #define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC) #define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC) #define mysqlnd_more_results(conn) (conn)->m->more_results((conn)) #define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC) #define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row)) /*****************************************************************************************************/ #if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1 /* Errors */ #define mysqlnd_errno(conn) (conn)->error_info.error_no #define mysqlnd_error(conn) (conn)->error_info.error #define mysqlnd_sqlstate(conn) ((conn)->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) /* Charset */ #define mysqlnd_character_set_name(conn) (conn)->charset->name /* Simple metadata */ #define mysqlnd_field_count(conn) (conn)->field_count #define mysqlnd_insert_id(conn) (conn)->upsert_status.last_insert_id #define mysqlnd_affected_rows(conn) (conn)->upsert_status.affected_rows #define mysqlnd_warning_count(conn) (conn)->upsert_status.warning_count #define mysqlnd_info(conn) (conn)->last_message #define mysqlnd_get_server_info(conn) (conn)->server_version #define mysqlnd_get_host_info(conn) (conn)->host_info #define mysqlnd_get_proto_info(conn) (conn)->protocol_version #define mysqlnd_thread_id(conn) (conn)->thread_id #define mysqlnd_num_rows(result) ((result)->data? (result)->data->row_count:0) #define mysqlnd_num_fields(result) (result)->field_count #define mysqlnd_fetch_lengths(result) ((result)->m.fetch_lengths? (result)->m.fetch_lengths((result)):NULL) #define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs)) #define mysqlnd_field_tell(result) (result)->meta? (result)->meta->current_field:0) #define mysqlnd_fetch_field(result) (result)->m.fetch_field((result)) #define mysqlnd_fetch_field_direct(result,fnr) ((result)->meta? &((result)->meta->fields[(fnr)]):NULL) /* mysqlnd metadata */ #define mysqlnd_get_client_info() MYSQLND_VERSION #define mysqlnd_get_client_version() MYSQLND_VERSION_ID /* PS */ #define mysqlnd_stmt_insert_id(stmt) (stmt)->upsert_status.last_insert_id #define mysqlnd_stmt_affected_rows(stmt) (stmt)->upsert_status.affected_rows #define mysqlnd_stmt_num_rows(stmt) (stmt)->result? mysqlnd_num_rows((stmt)->result):0 #define mysqlnd_stmt_param_count(stmt) (stmt)->param_count #define mysqlnd_stmt_field_count(stmt) (stmt)->field_count #define mysqlnd_stmt_warning_count(stmt) (stmt)->upsert_status.warning_count #define mysqlnd_stmt_errno(stmt) (stmt)->error_info.error_no #define mysqlnd_stmt_error(stmt) (stmt)->error_info.error #define mysqlnd_stmt_sqlstate(stmt) ((stmt)->error_info.sqlstate[0] ? (stmt)->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) /*****************************************************************************************************/ #else /* Using plain functions */ /*****************************************************************************************************/ /* Errors */ #define mysqlnd_errno(conn) (conn)->m->get_error_no((conn)) #define mysqlnd_error(conn) (conn)->m->get_error_str((conn)) #define mysqlnd_sqlstate(conn) (conn)->m->get_sqlstate((conn)) /* Charset */ #define mysqlnd_character_set_name(conn) (conn)->m->charset_name((conn)) /* Simple metadata */ #define mysqlnd_field_count(conn) (conn)->m->get_field_count((conn)) #define mysqlnd_insert_id(conn) (conn)->m->get_last_insert_id((conn)) #define mysqlnd_affected_rows(conn) (conn)->m->get_affected_rows((conn)) #define mysqlnd_warning_count(conn) (conn)->m->get_warning_count((conn)) #define mysqlnd_info(conn) (conn)->m->get_last_message((conn)) #define mysqlnd_get_server_info(conn) (conn)->m->get_server_information((conn)) #define mysqlnd_get_host_info(conn) (conn)->m->get_host_information((conn)) #define mysqlnd_get_proto_info(conn) (conn)->m->get_protocol_information((conn)) #define mysqlnd_thread_id(conn) (conn)->m->get_thread_id((conn)) #define mysqlnd_num_rows(result) (result)->m.num_rows((result)) #define mysqlnd_num_fields(result) (result)->m.num_fields((result)) PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result); #define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs)) #define mysqlnd_field_tell(result) (result)->m.field_tell((result)) #define mysqlnd_fetch_field(result) (result)->m.fetch_field((result)) #define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr)) /* mysqlnd metadata */ PHPAPI const char * mysqlnd_get_client_info(); PHPAPI unsigned int mysqlnd_get_client_version(); /* PS */ #define mysqlnd_stmt_insert_id(stmt) (stmt)->m->get_last_insert_id((stmt)) #define mysqlnd_stmt_affected_rows(stmt) (stmt)->m->get_affected_rows((stmt)) #define mysqlnd_stmt_num_rows(stmt) (stmt)->m->get_num_rows((stmt)) #define mysqlnd_stmt_param_count(stmt) (stmt)->m->get_param_count((stmt)) #define mysqlnd_stmt_field_count(stmt) (stmt)->m->get_field_count((stmt)) #define mysqlnd_stmt_warning_count(stmt) (stmt)->m->get_warning_count((stmt)) #define mysqlnd_stmt_errno(stmt) (stmt)->m->get_error_no((stmt)) #define mysqlnd_stmt_error(stmt) (stmt)->m->get_error_str((stmt)) #define mysqlnd_stmt_sqlstate(stmt) (stmt)->m->get_sqlstate((stmt)) #endif /* MYSQLND_USE_OPTIMISATIONS */ /*****************************************************************************************************/ PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type); /* LOAD DATA LOCAL */ PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback); PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname); /* Simple commands */ #define mysqlnd_autocommit(conn, mode) (conn)->m->query((conn),(mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", 16 TSRMLS_CC) #define mysqlnd_commit(conn) (conn)->m->query((conn), "COMMIT", sizeof("COMMIT")-1 TSRMLS_CC) #define mysqlnd_rollback(conn) (conn)->m->query((conn), "ROLLBACK", sizeof("ROLLBACK")-1 TSRMLS_CC) #define mysqlnd_list_dbs(conn, wild) (conn)->m->list_method((conn), wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL TSRMLS_CC) #define mysqlnd_list_fields(conn, tab,wild) (conn)->m->list_method((conn), wild? "SHOW FIELDS FROM %s LIKE %s":"SHOW FIELDS FROM %s", wild, tab TSRMLS_CC) #define mysqlnd_list_processes(conn) (conn)->m->list_method((conn), "SHOW PROCESSLIST", NULL, NULL TSRMLS_CC) #define mysqlnd_list_tables(conn, wild) (conn)->m->list_method((conn), wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL TSRMLS_CC) #define mysqlnd_dump_debug_info(conn) (conn)->m->server_dump_debug_information((conn) TSRMLS_CC) #define mysqlnd_select_db(conn, db, db_len) (conn)->m->select_db((conn), (db), (db_len) TSRMLS_CC) #define mysqlnd_ping(conn) (conn)->m->ping((conn) TSRMLS_CC) #define mysqlnd_kill(conn, pid) (conn)->m->kill_connection((conn), (pid) TSRMLS_CC) #define mysqlnd_refresh(conn, options) (conn)->m->refresh_server((conn), (options) TSRMLS_CC) #define mysqlnd_shutdown(conn, level) (conn)->m->shutdown_server((conn), (level) TSRMLS_CC) #define mysqlnd_get_server_version(conn) (conn)->m->get_server_version((conn)) #define mysqlnd_set_character_set(conn, cs) (conn)->m->set_charset((conn), (cs) TSRMLS_CC) #define mysqlnd_stat(conn, msg, msg_len) (conn)->m->get_server_statistics((conn), (msg), (msg_len) TSRMLS_CC) #define mysqlnd_options(conn, opt, value) (conn)->m->set_client_option((conn), (opt), (value)) #define mysqlnd_set_server_option(conn, op) (conn)->m->set_server_option((conn), (op) TSRMLS_CC) /* Escaping */ #define mysqlnd_real_escape_string(conn, newstr, escapestr, escapestr_len) \ (conn)->m->escape_string((conn), (newstr), (escapestr), (escapestr_len)) #define mysqlnd_escape_string(newstr, escapestr, escapestr_len) \ mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len)) PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len); /* PS */ #define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn)) #define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL)) #define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC) #define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row)) #define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC) #define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt) TSRMLS_CC) #define mysqlnd_stmt_send_long_data(s,p,d,l) (s)->m->send_long_data((s), (p), (d), (l) TSRMLS_CC) #define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_param((stmt), (bind)) #define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind)) #define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt)) #define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt)) #define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt) TSRMLS_CC) #define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit) TSRMLS_CC) #define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt) TSRMLS_CC) #define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value)) #define mysqlnd_stmt_attr_set(stmt, attr, value) (stmt)->m->set_attribute((stmt), (attr), (value)) #define mysqlnd_stmt_fetch(stmt, fetched) (stmt)->m->fetch((stmt), (fetched) TSRMLS_CC) /* Performance statistics */ PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); /* Persistent caching zval allocator */ PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size); PHPAPI void mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache); PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value); PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache); PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache); PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache); MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache); PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache); /* There two should not be used from outside */ void * mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated); void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const cache, enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC); /* ---------------------- QUERY CACHE ---------------*/ struct st_mysqlnd_qcache { HashTable *ht; unsigned int references; #ifdef ZTS MUTEX_T LOCK_access; #endif }; typedef struct st_mysqlnd_qcache_element { MYSQLND_RES_BUFFERED *data; MYSQLND_RES_METADATA *meta; const char * query; size_t query_len; } MYSQLND_QCACHE_ELEMENT; PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_init_cache(); PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_get_cache_reference(MYSQLND_QCACHE * const cache); PHPAPI void mysqlnd_qcache_free_cache_reference(MYSQLND_QCACHE **cache); PHPAPI void mysqlnd_qcache_stats(const MYSQLND_QCACHE * const cache, zval *return_value); MYSQLND_RES * mysqlnd_qcache_get(MYSQLND_QCACHE * const cache, const char * query, size_t query_len); void mysqlnd_qcache_put(MYSQLND_QCACHE * const cache, char * query, size_t query_len, MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta); #endif /* MYSQLND_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_charset.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_charset.c +++ php-src/ext/mysqlnd/mysqlnd_charset.c /* +----------------------------------------------------------------------+ | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 2006-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Georg Richter <georg@mysql.com> | | Andrey Hristov <andrey@mysql.com> | | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_globals.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" /* {{{ utf8 functions */ static uint check_mb_utf8(const char *start, const char *end) { zend_uchar c; if (start >= end) { return 0; } c = (zend_uchar) start[0]; if (c < 0x80) { return 1; /* single byte character */ } if (c < 0xC2) { return 0; /* invalid mb character */ } if (c < 0xE0) { if (start + 2 > end) { return 0; /* too small */ } if (!(((zend_uchar)start[1] ^ 0x80) < 0x40)) { return 0; } return 2; } if (c < 0xF0) { if (start + 3 > end) { return 0; /* too small */ } if (!(((zend_uchar)start[1] ^ 0x80) < 0x40 && ((zend_uchar)start[2] ^ 0x80) < 0x40 && (c >= 0xE1 || (zend_uchar)start[1] >= 0xA0))) { return 0; /* invalid utf8 character */ } return 3; } return 0; } static uint mysqlnd_mbcharlen_utf8(uint utf8) { if (utf8 < 0x80) { return 1; /* single byte character */ } if (utf8 < 0xC2) { return 0; /* invalid multibyte header */ } if (utf8 < 0xE0) { return 2; /* double byte character */ } if (utf8 < 0xF0) { return 3; /* triple byte character */ } /* We still don't support characters out of the BMP */ return 0; } /* }}} */ /* {{{ big5 functions */ #define valid_big5head(c) (0xA1 <= (uint)(c) && (uint)(c) <= 0xF9) #define valid_big5tail(c) ((0x40 <= (uint)(c) && (uint)(c) <= 0x7E) || \ (0xA1 <= (uint)(c) && (uint)(c) <= 0xFE)) #define isbig5code(c,d) (isbig5head(c) && isbig5tail(d)) static uint check_mb_big5(const char *start, const char *end) { return ((end - start) > 1 && valid_big5head(*(start)) && valid_big5tail(*(start + 1)) ? 2 : 0); } static uint mysqlnd_mbcharlen_big5(uint big5) { return (valid_big5head(big5)) ? 2 : 1; } /* }}} */ /* {{{ cp932 functions */ #define valid_cp932head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && c <= 0xFC)) #define valid_cp932tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && c <= 0xFC)) static uint check_mb_cp932(const char *start, const char *end) { return (((end > start) + 1) && valid_cp932head((uint)start[0]) && valid_cp932tail((uint)start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_cp932(uint cp932) { return (valid_cp932head(cp932)) ? 2 : 1; } /* }}} */ /* {{{ euckr functions */ #define valid_euckr(c) ((0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE)) static uint check_mb_euckr(const char *start, const char *end) { if (end - start <= 1) { return 0; /* invalid length */ } if (*(zend_uchar *)start < 0x80) { return 0; /* invalid euckr character */ } if (valid_euckr(start[1])) { return 2; } return 0; } static uint mysqlnd_mbcharlen_euckr(uint kr) { return (valid_euckr(kr)) ? 2 : 1; } /* }}} */ /* {{{ eucjpms functions */ #define valid_eucjpms(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xFE) #define valid_eucjpms_kata(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xDF) #define valid_eucjpms_ss2(c) (((c) & 0xFF) == 0x8E) #define valid_eucjpms_ss3(c) (((c) & 0xFF) == 0x8F) static uint check_mb_eucjpms(const char *start, const char *end) { if (*((uint *)start) < 0x80) { return 0; /* invalid eucjpms character */ } if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) { return 2; } if (valid_eucjpms_ss2(start[0]) && (end - start) > 1 && valid_eucjpms_kata(start[1])) { return 2; } if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) && valid_eucjpms(start[2])) { return 2; } return 0; } static uint mysqlnd_mbcharlen_eucjpms(uint jpms) { if (valid_eucjpms(jpms) || valid_eucjpms_ss2(jpms)) { return 2; } if (valid_eucjpms_ss3(jpms)) { return 3; } return 1; } /* }}} */ /* {{{ gb2312 functions */ #define valid_gb2312_head(c) (0xA1 <= (c) && (c) <= 0xF7) #define valid_gb2312_tail(c) (0xA1 <= (c) && (c) <= 0xFE) static uint check_mb_gb2312(const char *start, const char *end) { return (end - start > 1 || valid_gb2312_head((uint)start[0]) || valid_gb2312_tail((uint)start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_gb2312(uint gb) { return (valid_gb2312_head(gb)) ? 2 : 1; } /* }}} */ /* {{{ gbk functions */ #define valid_gbk_head(c) ((uint)(c) >> 8) #define valid_gbk_tail(c) ((uint)(c) & 0xFF) static uint check_mb_gbk(const char *start, const char *end) { if (end - start <= 1) { return 0; /* invalid length */ } return (valid_gbk_head(start[0]) && valid_gbk_tail(start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_gbk(uint gbk) { return (valid_gbk_head(gbk) ? 2 : 1); } /* }}} */ /* {{{ sjis functions */ #define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) && \ (0xE0 <= (c) && (c) <= 0xFC)) #define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) && \ (0x80 <= (c) && (c) <= 0x7C)) static uint check_mb_sjis(const char *start, const char *end) { if (end - start <= 1) { return 0; } return (valid_sjis_head((uint)start[0]) && valid_sjis_tail((uint)start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_sjis(uint sjis) { return (valid_sjis_head((uint)sjis)) ? 2 : 1; } /* }}} */ /* {{{ ucs2 functions */ static uint check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused))) { return 2; /* always 2 */ } static uint mysqlnd_mbcharlen_ucs2(uint ucs2 __attribute((unused))) { return 2; /* always 2 */ } /* }}} */ /* {{{ ujis functions */ #define valid_ujis(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xFE)) #define valid_ujis_kata(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xDF)) #define valid_ujis_ss2(c) (((c)&0xFF) == 0x8E) #define valid_ujis_ss3(c) (((c)&0xFF) == 0x8F) static uint check_mb_ujis(const char *start, const char *end) { if ((uint)start[0] < 0x80) { return 0; /* invalid ujis character */ } if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) { return 2; } if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) { return 2; } if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) { return 3; } return 0; } static uint mysqlnd_mbcharlen_ujis(uint ujis) { if (((ujis & 0xFF) >= 0xA1 && (ujis & 0xFF) <= 0xFE) || ((ujis & 0xFF) == 0x8E)) { return 2; } if ((ujis & 0xFF) == 0x8F) { return 3; } return 1; } /* }}} */ /* {{{ mysqlnd_charsets */ const MYSQLND_CHARSET mysqlnd_charsets[] = { { 1, "big5","big5_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5}, { 3, "dec8", "dec8_swedisch_ci", 1, 1, 0, NULL, NULL}, { 4, "cp850", "cp850_general_ci", 1, 1, 0, NULL, NULL}, { 6, "hp8", "hp8_english_ci", 1, 1, 0, NULL, NULL}, { 7, "koi8r", "koi8r_general_ci", 1, 1, 0, NULL, NULL}, { 8, "latin1", "latin1_swedish_ci", 1, 1, 0, NULL, NULL}, { 9, "latin2", "latin2_general_ci", 1, 1, 0, NULL, NULL}, { 10, "swe7", "swe7_swedish_ci", 1, 1, 0, NULL, NULL}, { 11, "ascii", "ascii_general_ci", 1, 1, 0, NULL, NULL}, { 12, "ujis", "ujis_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis}, { 13, "sjis", "sjis_japanese_ci", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis}, { 16, "hebrew", "hebrew_general_ci", 1, 1, 0, NULL, NULL}, { 18, "tis620", "tis620_thai_ci", 1, 1, 0, NULL, NULL}, { 19, "euckr", "euckr_korean_ci", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_eucjpms}, { 22, "koi8u", "koi8u_general_ci", 1, 1, 0, NULL, NULL}, { 24, "gb2312", "gb2312_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, { 25, "greek", "greek_general_ci", 1, 1, 0, NULL, NULL}, { 26, "cp1250", "cp1250_general_ci", 1, 1, 0, NULL, NULL}, { 28, "gbk", "gbk_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk}, { 30, "latin5", "latin5_turkish_ci", 1, 1, 0, NULL, NULL}, { 32, "armscii8", "armscii8_general_ci", 1, 1, 0, NULL, NULL}, { 33, "utf8", "utf8_general_ci", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 35, "ucs2", "ucs2_general_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 36, "cp866", "cp866_general_ci", 1, 1, 0, NULL, NULL}, { 37, "keybcs2", "keybcs2_general_ci", 1, 1, 0, NULL, NULL}, { 38, "macce", "macce_general_ci", 1, 1, 0, NULL, NULL}, { 39, "macroman", "macroman_general_ci", 1, 1, 0, NULL, NULL}, { 40, "cp852", "cp852_general_ci", 1, 1, 0, NULL, NULL}, { 41, "latin7", "latin7_general_ci", 1, 1, 0, NULL, NULL}, { 51, "cp1251", "cp1251_general_ci", 1, 1, 0, NULL, NULL}, { 57, "cp1256", "cp1256_general_ci", 1, 1, 0, NULL, NULL}, { 59, "cp1257", "cp1257_general_ci", 1, 1, 0, NULL, NULL}, { 63, "binary", "binary", 1, 1, 0, NULL, NULL}, { 92, "geostd8", "geostd8_general_ci", 1, 1, 0, NULL, NULL}, { 95, "cp932", "cp932_japanese_ci", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932}, { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, }, { 2, "latin2", "latin2_czech_cs", 1, 1, 0, NULL, NULL}, { 5, "latin1", "latin1_german_ci", 1, 1, 0, NULL, NULL}, { 14, "cp1251", "cp1251_bulgarian_ci", 1, 1, 0, NULL, NULL}, { 15, "latin1", "latin1_danish_ci", 1, 1, 0, NULL, NULL}, { 17, "filename", "filename", 1, 5, 1, NULL, NULL}, { 20, "latin7", "latin7_estonian_cs", 1, 1, 0, NULL, NULL}, { 21, "latin2", "latin2_hungarian_ci", 1, 1, 0, NULL, NULL}, { 23, "cp1251", "cp1251_ukrainian_ci", 1, 1, 0, NULL, NULL}, { 27, "latin2", "latin2_croatian_ci", 1, 1, 0, NULL, NULL}, { 29, "cp1257", "cp1257_lithunian_ci", 1, 1, 0, NULL, NULL}, { 31, "latin1", "latin1_german2_ci", 1, 1, 0, NULL, NULL}, { 34, "cp1250", "cp1250_czech_cs", 1, 1, 0, NULL, NULL}, { 42, "latin7", "latin7_general_cs", 1, 1, 0, NULL, NULL}, { 43, "macce", "macce_bin", 1, 1, 0, NULL, NULL}, { 44, "cp1250", "cp1250_croatian_ci", 1, 1, 0, NULL, NULL}, { 47, "latin1", "latin1_bin", 1, 1, 0, NULL, NULL}, { 48, "latin1", "latin1_general_ci", 1, 1, 0, NULL, NULL}, { 49, "latin1", "latin1_general_cs", 1, 1, 0, NULL, NULL}, { 50, "cp1251", "cp1251_bin", 1, 1, 0, NULL, NULL}, { 52, "cp1251", "cp1251_general_cs", 1, 1, 0, NULL, NULL}, { 53, "macroman", "macroman_bin", 1, 1, 0, NULL, NULL}, { 58, "cp1257", "cp1257_bin", 1, 1, 0, NULL, NULL}, { 60, "armascii8", "armascii8_bin", 1, 1, 0, NULL, NULL}, { 65, "ascii", "ascii_bin", 1, 1, 0, NULL, NULL}, { 66, "cp1250", "cp1250_bin", 1, 1, 0, NULL, NULL}, { 67, "cp1256", "cp1256_bin", 1, 1, 0, NULL, NULL}, { 68, "cp866", "cp866_bin", 1, 1, 0, NULL, NULL}, { 69, "dec8", "dec8_bin", 1, 1, 0, NULL, NULL}, { 70, "greek", "greek_bin", 1, 1, 0, NULL, NULL}, { 71, "hebew", "hebrew_bin", 1, 1, 0, NULL, NULL}, { 72, "hp8", "hp8_bin", 1, 1, 0, NULL, NULL}, { 73, "keybcs2", "keybcs2_bin", 1, 1, 0, NULL, NULL}, { 74, "koi8r", "koi8r_bin", 1, 1, 0, NULL, NULL}, { 75, "koi8u", "koi8u_bin", 1, 1, 0, NULL, NULL}, { 77, "latin2", "latin2_bin", 1, 1, 0, NULL, NULL}, { 78, "latin5", "latin5_bin", 1, 1, 0, NULL, NULL}, { 79, "latin7", "latin7_bin", 1, 1, 0, NULL, NULL}, { 80, "cp850", "cp850_bin", 1, 1, 0, NULL, NULL}, { 81, "cp852", "cp852_bin", 1, 1, 0, NULL, NULL}, { 82, "swe7", "swe7_bin", 1, 1, 0, NULL, NULL}, { 93, "geostd8", "geostd8_bin", 1, 1, 0, NULL, NULL}, { 83, "utf8", "utf8_bin", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 84, "big5", "big5_bin", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5}, { 85, "euckr", "euckr_bin", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr}, { 86, "gb2312", "gb2312_bin", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, { 87, "gbk", "gbk_bin", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk}, { 88, "sjis", "sjis_bin", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis}, { 89, "tis620", "tis620_bin", 1, 1, 0, NULL, NULL}, { 90, "ucs2", "ucs2_bin", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 91, "ujis", "ujis_bin", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis}, { 94, "latin1", "latin1_spanish_ci", 1, 1, 0, NULL, NULL}, { 96, "cp932", "cp932_bin", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932}, { 99, "cp1250", "cp1250_polish_ci", 1, 1, 0, NULL, NULL}, { 98, "eucjpms", "eucjpms_bin", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms}, { 128, "ucs2", "ucs2_unicode_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 129, "ucs2", "ucs2_icelandic_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 130, "ucs2", "ucs2_latvian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 131, "ucs2", "ucs2_romanian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 132, "ucs2", "ucs2_slovenian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 133, "ucs2", "ucs2_polish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 134, "ucs2", "ucs2_estonian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 135, "ucs2", "ucs2_spanish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 136, "ucs2", "ucs2_swedish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 137, "ucs2", "ucs2_turkish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 138, "ucs2", "ucs2_czech_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 139, "ucs2", "ucs2_danish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 140, "ucs2", "ucs2_lithunian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 141, "ucs2", "ucs2_slovak_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 142, "ucs2", "ucs2_spanish2_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 143, "ucs2", "ucs2_roman_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 144, "ucs2", "ucs2_persian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 145, "ucs2", "ucs2_esperanto_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 146, "ucs2", "ucs2_hungarian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 192, "utf8", "utf8_general_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, { 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, { 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, { 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, { 0, NULL, NULL, 0, 0, 0, NULL, NULL} }; /* }}} */ /* {{{ mysqlnd_find_charset_nr */ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint charsetnr) { const MYSQLND_CHARSET * c = mysqlnd_charsets; do { if (c->nr == charsetnr) { return c; } ++c; } while (c[0].nr != 0); return NULL; } /* }}} */ /* {{{ mysqlnd_find_charset_name */ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const name) { const MYSQLND_CHARSET *c = mysqlnd_charsets; do { if (!strcasecmp(c->name, name)) { return c; } ++c; } while (c[0].nr != 0); return NULL; } /* }}} */ /* {{{ mysqlnd_cset_escape_quotes */ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr, const char *escapestr, int escapestr_len) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; for (;escapestr < end; escapestr++) { uint len = 0; /* check unicode characters */ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && (len = cset->mb_valid(escapestr, end))) { /* check possible overflow */ if ((newstr + len) > newstr_e) { escape_overflow = TRUE; break; } /* copy mb char without escaping it */ while (len--) { *newstr++ = *escapestr++; } escapestr--; continue; } if (*newstr == '\'') { if (newstr + 2 > newstr_e) { escape_overflow = TRUE; break; } *newstr++ = '\''; *newstr++ = '\''; } else { if (newstr + 1 > newstr_e) { escape_overflow = TRUE; break; } *newstr++= *escapestr; } } *newstr = '\0'; if (escape_overflow) { return (ulong)~0; } return (ulong)(newstr - newstr_s); } /* }}} */ /* {{{ mysqlnd_cset_escape_slashes */ PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, const char *escapestr, int escapestr_len) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; for (;escapestr < end; escapestr++) { char esc = '\0'; uint len = 0; /* check unicode characters */ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && (len = cset->mb_valid(escapestr, end))) { /* check possible overflow */ if ((newstr + len) > newstr_e) { escape_overflow = TRUE; break; } /* copy mb char without escaping it */ while (len--) { *newstr++ = *escapestr++; } escapestr--; continue; } if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && (len = cset->mb_valid(escapestr, end))) { esc = *escapestr; } else { switch (*escapestr) { case 0: esc = '0'; break; case '\n': esc = 'n'; break; case '\r': esc = 'r'; break; case '\\': case '\'': case '"': esc = *escapestr; break; case '\032': esc = 'Z'; break; } } if (esc) { if (newstr + 2 > newstr_e) { escape_overflow = TRUE; break; } /* copy escaped character */ *newstr++ = '\\'; *newstr++ = esc; } else { if (newstr + 1 > newstr_e) { escape_overflow = TRUE; break; } /* copy non escaped character */ *newstr++ = *escapestr; } } *newstr = '\0'; if (escape_overflow) { return (ulong)~0; } return (ulong)(newstr - newstr_s); } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_charset.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_charset.h +++ php-src/ext/mysqlnd/mysqlnd_charset.h /* +----------------------------------------------------------------------+ | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Georg Richter <georg@mysql.com> | | Andrey Hristov <andrey@mysql.com> | | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char *newstr, const char *escapestr, int escapestr_len); PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, const char *escapestr, int escapestr_len); /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_enum_n_def.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_enum_n_def.h +++ php-src/ext/mysqlnd/mysqlnd_enum_n_def.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_enum_n_def.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_ENUM_N_DEF_H #define MYSQLND_ENUM_N_DEF_H #define MYSQLND_ERRMSG_SIZE 512 #define MYSQLND_SQLSTATE_LENGTH 5 #define MYSQLND_SQLSTATE_NULL "00000" #define MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define MYSQLND_SERVER_QUERY_NO_INDEX_USED 32 #define MYSQLND_NO_DATA 100 #define MYSQLND_DATA_TRUNCATED 101 #define SHA1_MAX_LENGTH 20 #define SCRAMBLE_LENGTH 20 #define SCRAMBLE_LENGTH_323 8 #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ #define CLIENT_LONG_FLAG 4 /* Get all column flags */ #define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */ #define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */ #define CLIENT_COMPRESS 32 /* Can use compression protocol */ #define CLIENT_ODBC 64 /* Odbc client */ #define CLIENT_LOCAL_FILES 128 /* Can use LOAD DATA LOCAL */ #define CLIENT_IGNORE_SPACE 256 /* Ignore spaces before '(' */ #define CLIENT_PROTOCOL_41 512 /* New 4.1 protocol */ #define CLIENT_INTERACTIVE 1024 /* This is an interactive client */ #define CLIENT_SSL 2048 /* Switch to SSL after handshake */ #define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */ #define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */ #define CLIENT_RESERVED 16384 /* Old flag for 4.1 protocol */ #define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ #define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */ #define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */ typedef enum mysqlnd_extension { MYSQLND_MYSQL = 0, MYSQLND_MYSQLI, } enum_mysqlnd_extension; enum { MYSQLND_FETCH_ASSOC = 1, MYSQLND_FETCH_NUM = 2, MYSQLND_FETCH_BOTH = 1|2, }; /* Follow libmysql convention */ typedef enum func_status { PASS = 0, FAIL = 1, } enum_func_status; typedef enum mysqlnd_query_type { QUERY_UPSERT, QUERY_SELECT, QUERY_LOAD_LOCAL } enum_mysqlnd_query_type; typedef enum mysqlnd_res_type { MYSQLND_RES_NORMAL = 1, MYSQLND_RES_PS_BUF, MYSQLND_RES_PS_UNBUF } enum_mysqlnd_res_type; typedef enum mysqlnd_option { MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE, MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE, MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200, #ifdef MYSQLND_STRING_TO_INT_CONVERSION MYSQLND_OPT_INT_AND_YEAR_AS_INT = 201, #endif } enum_mysqlnd_option; typedef enum mysqlnd_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR, MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, MYSQL_TYPE_BIT, MYSQL_TYPE_NEWDECIMAL=246, MYSQL_TYPE_ENUM=247, MYSQL_TYPE_SET=248, MYSQL_TYPE_TINY_BLOB=249, MYSQL_TYPE_MEDIUM_BLOB=250, MYSQL_TYPE_LONG_BLOB=251, MYSQL_TYPE_BLOB=252, MYSQL_TYPE_VAR_STRING=253, MYSQL_TYPE_STRING=254, MYSQL_TYPE_GEOMETRY=255 } enum_mysqlnd_field_types; /* Please update this if there is a new type after MYSQL_TYPE_GEOMETRY */ #define MYSQL_TYPE_LAST MYSQL_TYPE_GEOMETRY typedef enum mysqlnd_server_option { MYSQL_OPTION_MULTI_STATEMENTS_ON, MYSQL_OPTION_MULTI_STATEMENTS_OFF } enum_mysqlnd_server_option; #define FIELD_TYPE_DECIMAL MYSQL_TYPE_DECIMAL #define FIELD_TYPE_NEWDECIMAL MYSQL_TYPE_NEWDECIMAL #define FIELD_TYPE_TINY MYSQL_TYPE_TINY #define FIELD_TYPE_SHORT MYSQL_TYPE_SHORT #define FIELD_TYPE_LONG MYSQL_TYPE_LONG #define FIELD_TYPE_FLOAT MYSQL_TYPE_FLOAT #define FIELD_TYPE_DOUBLE MYSQL_TYPE_DOUBLE #define FIELD_TYPE_NULL MYSQL_TYPE_NULL #define FIELD_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP #define FIELD_TYPE_LONGLONG MYSQL_TYPE_LONGLONG #define FIELD_TYPE_INT24 MYSQL_TYPE_INT24 #define FIELD_TYPE_DATE MYSQL_TYPE_DATE #define FIELD_TYPE_TIME MYSQL_TYPE_TIME #define FIELD_TYPE_DATETIME MYSQL_TYPE_DATETIME #define FIELD_TYPE_YEAR MYSQL_TYPE_YEAR #define FIELD_TYPE_NEWDATE MYSQL_TYPE_NEWDATE #define FIELD_TYPE_ENUM MYSQL_TYPE_ENUM #define FIELD_TYPE_SET MYSQL_TYPE_SET #define FIELD_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB #define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB #define FIELD_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB #define FIELD_TYPE_BLOB MYSQL_TYPE_BLOB #define FIELD_TYPE_VAR_STRING MYSQL_TYPE_VAR_STRING #define FIELD_TYPE_STRING MYSQL_TYPE_STRING #define FIELD_TYPE_CHAR MYSQL_TYPE_TINY #define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM #define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY #define FIELD_TYPE_BIT MYSQL_TYPE_BIT #define NOT_NULL_FLAG 1 #define PRI_KEY_FLAG 2 #define UNIQUE_KEY_FLAG 4 #define MULTIPLE_KEY_FLAG 8 #define BLOB_FLAG 16 #define UNSIGNED_FLAG 32 #define ZEROFILL_FLAG 64 #define BINARY_FLAG 128 #define ENUM_FLAG 256 #define AUTO_INCREMENT_FLAG 512 #define TIMESTAMP_FLAG 1024 #define SET_FLAG 2048 #define NO_DEFAULT_VALUE_FLAG 4096 #define PART_KEY_FLAG 16384 #define GROUP_FLAG 32768 #define NUM_FLAG 32768 #define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG) #define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG) #define IS_BLOB(n) ((n) & BLOB_FLAG) #define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL) /* see mysqlnd_charset.c for more information */ #define MYSQLND_BINARY_CHARSET_NR 63 #endif /* MYSQLND_ENUM_N_DEF_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_libmysql_compat.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_libmysql_compat.h +++ php-src/ext/mysqlnd/mysqlnd_libmysql_compat.h /* +----------------------------------------------------------------------+ | 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 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_loaddata.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_loaddata.c +++ php-src/ext/mysqlnd/mysqlnd_loaddata.c /* +----------------------------------------------------------------------+ | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 2006-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Georg Richter <georg@mysql.com> | | Andrey Hristov <andrey@mysql.com> | | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_globals.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command TSRMLS_DC); #define ALLOC_CALLBACK_ARGS(a, b, c)\ if (c) {\ a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ for (i = b; i < c; i++) {\ a[i] = emalloc(sizeof(zval *));\ MAKE_STD_ZVAL(*a[i]);\ }\ } #define FREE_CALLBACK_ARGS(a, b, c)\ if (a) {\ for (i=b; i < c; i++) {\ zval_ptr_dtor(a[i]);\ efree(a[i]);\ }\ efree(a);\ } /* {{{ mysqlnd_local_infile_init */ static int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS_DC) { MYSQLND_INFILE_INFO *info; php_stream_context *context = NULL; *ptr= info= ((MYSQLND_INFILE_INFO *)ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); /* check open_basedir */ if (PG(open_basedir)) { if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) { strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file"); info->error_no = CR_UNKNOWN_ERROR; return 1; } } info->filename = filename; info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context); if (info->fd == NULL) { snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename); info->error_no = MYSQLND_EE_FILENOTFOUND; return 1; } return 0; } /* }}} */ /* {{{ mysqlnd_local_infile_read */ static int mysqlnd_local_infile_read(void *ptr, char *buf, uint buf_len TSRMLS_DC) { MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; int count; /* default processing */ if (!info->callback) { count = (int)php_stream_read(info->fd, buf, buf_len); if (count < 0) { strcpy(info->error_msg, "Error reading file"); info->error_no = MYSQLND_EE_READ; } return count; } else { zval ***callback_args; zval *retval; zval *fp; int argc = 4; int i; long rc; ALLOC_CALLBACK_ARGS(callback_args, 1, argc); /* set parameters: filepointer, buffer, buffer_len, errormsg */ MAKE_STD_ZVAL(fp); php_stream_to_zval(info->fd, fp); callback_args[0] = &fp; ZVAL_STRING(*callback_args[1], "", 1); ZVAL_LONG(*callback_args[2], buf_len); ZVAL_STRING(*callback_args[3], "", 1); if (call_user_function_ex(EG(function_table), NULL, info->callback, &retval, argc, callback_args, 0, NULL TSRMLS_CC) == SUCCESS) { rc = Z_LVAL_P(retval); zval_ptr_dtor(&retval); if (rc > 0) { const char * msg = NULL; if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) { msg = "Mismatch between the return value of the callback and the content " "length of the buffer."; php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); rc = -1; } else if (Z_STRLEN_P(*callback_args[1]) > buf_len) { /* check buffer overflow */ msg = "Too much data returned"; rc = -1; } else { memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1]))); } if (rc == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); strcpy(info->error_msg, msg); info->error_no = MYSQLND_EE_READ; } } else if (rc < 0) { strncpy(info->error_msg, Z_STRVAL_P(*callback_args[3]), MYSQLND_ERRMSG_SIZE); info->error_no = MYSQLND_EE_READ; } } else { strcpy(info->error_msg, "Can't execute load data local init callback function"); info->error_no = MYSQLND_EE_READ; rc = -1; } efree(fp); FREE_CALLBACK_ARGS(callback_args, 1, argc); return rc; } } /* }}} */ /* {{{ mysqlnd_local_infile_error */ static int mysqlnd_local_infile_error(void *ptr, char *error_buf, uint error_buf_len TSRMLS_DC) { MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; if (info) { strncpy(error_buf, info->error_msg, error_buf_len); return info->error_no; } strncpy(error_buf, "Unknown error", error_buf_len); return CR_UNKNOWN_ERROR; } /* }}} */ /* {{{ mysqlnd_local_infile_end */ static void mysqlnd_local_infile_end(void *ptr TSRMLS_DC) { MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; if (info) { /* php_stream_close segfaults on NULL */ if (info->fd) { php_stream_close(info->fd); } efree(info); } } /* }}} */ /* {{{ mysqlnd_local_infile_default */ PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback) { conn->infile.local_infile_init = mysqlnd_local_infile_init; conn->infile.local_infile_read = mysqlnd_local_infile_read; conn->infile.local_infile_error = mysqlnd_local_infile_error; conn->infile.local_infile_end = mysqlnd_local_infile_end; conn->infile.userdata = NULL; if (free_callback == TRUE && conn->infile.callback) { zval_ptr_dtor(&conn->infile.callback); conn->infile.callback = NULL; } } /* }}} */ /* {{{ mysqlnd_set_local_infile_handler */ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname) { if (!conn->infile.callback) { MAKE_STD_ZVAL(conn->infile.callback); } else { zval_dtor(conn->infile.callback); } ZVAL_STRING(conn->infile.callback, (char*) funcname, 1); } /* }}} */ static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file"; /* {{{ mysqlnd_handle_local_infile */ enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC) { char *buf; char empty_packet[MYSQLND_HEADER_SIZE]; enum_func_status result = FAIL; uint buflen = 4096; void *info = NULL; int bufsize; size_t ret; MYSQLND_INFILE infile; if (!(conn->options.flags & CLIENT_LOCAL_FILES)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden"); /* write empty packet to server */ ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); *is_warning = TRUE; goto infile_error; } /* check if we have valid functions */ if (!conn->infile.local_infile_init || !conn->infile.local_infile_read || !conn->infile.local_infile_error || !conn->infile.local_infile_end) { mysqlnd_local_infile_default(conn, FALSE); } infile = conn->infile; /* allocate buffer for reading data */ buf = (char *)ecalloc(1, buflen); *is_warning = FALSE; /* init handler: allocate read buffer and open file */ if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) { *is_warning = TRUE; /* error occured */ strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); /* write empty packet to server */ ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); goto infile_error; } /* pass callback handler */ if (infile.callback) { MYSQLND_INFILE_INFO *ptr = (MYSQLND_INFILE_INFO *)info; ptr->callback = infile.callback; } /* read data */ while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { if ((ret = mysqlnd_stream_write_w_header(conn, buf, bufsize TSRMLS_CC)) < 0) { SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); goto infile_error; } } /* send empty packet for eof */ if ((ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC)) < 0) { SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); goto infile_error; } /* error during read occured */ if (bufsize < 0) { *is_warning = TRUE; strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); goto infile_error; } result = PASS; infile_error: /* get response from server and update upsert values */ if (FAIL == mysqlnd_simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY TSRMLS_CC)) { result = FAIL; goto infile_error; } (*conn->infile.local_infile_end)(info TSRMLS_CC); efree(buf); return result; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_palloc.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_palloc.c +++ php-src/ext/mysqlnd/mysqlnd_palloc.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_palloc.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_palloc.h" #define MYSQLND_SILENT #ifdef ZTS #define LOCK_PCACHE(cache) tsrm_mutex_lock((cache)->LOCK_access) #define UNLOCK_PCACHE(cache) tsrm_mutex_unlock((cache)->LOCK_access) #else #define LOCK_PCACHE(cache) #define UNLOCK_PCACHE(cache) #endif #if PHP_MAJOR_VERSION < 6 #define IS_UNICODE_DISABLED (1) #else #define IS_UNICODE_DISABLED (!UG(unicode)) #endif /* {{{ mysqlnd_palloc_init_cache */ PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size) { MYSQLND_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_ZVAL_PCACHE)); unsigned int i; #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_init_cache %p]\n", ret); #endif #ifdef ZTS ret->LOCK_access = tsrm_mutex_alloc(); #endif ret->max_items = cache_size; ret->free_items = cache_size; ret->references = 1; /* 1. First initialize the free list part of the structure */ /* One more for empty position of last_added - always 0x0, bounds checking */ ret->free_list.ptr_line = calloc(ret->max_items + 1, sizeof(mysqlnd_zval *)); ret->free_list.last_added = ret->free_list.ptr_line + ret->max_items; /* 3. Allocate and initialize our zvals and initialize the free list */ ret->block = calloc(ret->max_items, sizeof(mysqlnd_zval)); ret->last_in_block = &(ret->block[ret->max_items]); for (i = 0; i < ret->max_items; i++) { /* 1. Initialize */ INIT_PZVAL(&(ret->block[i].zv)); ZVAL_NULL(&(ret->block[i].zv)); /* Assure it will never be freed before MSHUTDOWN */ ZVAL_ADDREF(&(ret->block[i].zv)); /* 2. Add to the free list */ *(--ret->free_list.last_added) = &(ret->block[i]); } return ret; } /* }}} */ /* {{{ mysqlnd_palloc_get_cache_reference */ MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_get_cache_reference(MYSQLND_ZVAL_PCACHE * const cache) { if (cache) { LOCK_PCACHE(cache); cache->references++; UNLOCK_PCACHE(cache); } return cache; } /* }}} */ /* {{{ mysqlnd_palloc_free_cache */ /* As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with copy_ctor but scrap what they point to with zval_dtor() and then just free our pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them to the free list after usage. We ZVAL_NULL() them when we allocate them in the constructor of the cache. */ void mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache) { #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_free_cache %p]\n", cache); #endif #ifdef ZTS tsrm_mutex_free(cache->LOCK_access); #endif /* Data in pointed by 'block' was cleaned in RSHUTDOWN */ free(cache->block); free(cache->free_list.ptr_line); free(cache); } /* }}} */ /* {{{ mysqlnd_palloc_init_thd_cache */ PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache) { MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE)); #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_init_thd_cache %p]\n", ret); #endif ret->parent = mysqlnd_palloc_get_cache_reference(cache); #ifdef ZTS ret->thread_id = tsrm_thread_id(); #endif ret->references = 1; /* 1. Initialize the GC list */ ret->gc_list.ptr_line = calloc(cache->max_items, sizeof(mysqlnd_zval *)); /* Backward and forward looping is possible */ ret->gc_list.last_added = ret->gc_list.ptr_line; return ret; } /* }}} */ /* {{{ mysqlnd_palloc_get_thd_cache_reference */ MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache) { if (cache) { ++cache->references; mysqlnd_palloc_get_cache_reference(cache->parent); } return cache; } /* }}} */ /* {{{ mysqlnd_palloc_free_cache */ /* As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with copy_ctor but scrap what they point to with zval_dtor() and then just free our pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them to the free list after usage. We ZVAL_NULL() them when we allocate them in the constructor of the cache. */ static void mysqlnd_palloc_free_thd_cache(MYSQLND_THD_ZVAL_PCACHE *cache) { #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_free_thd_cache %p]\n", cache); #endif free(cache->gc_list.ptr_line); free(cache); } /* }}} */ /* {{{ mysqlnd_palloc_free_thd_cache_reference */ PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache) { if (*cache) { #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_free_thd_cache_reference %p] refs=%d\n", *cache, (*cache)->references); #endif --(*cache)->parent->references; if (--(*cache)->references == 0) { mysqlnd_palloc_free_thd_cache(*cache); } *cache = NULL; } } /* }}} */ /* The cache line is a big contiguous array of zval pointers. Because the CPU cache will cache starting from an address, and not before it, then we have to organize our structure according to this. Thus, if 'last_added' is valid pointer (not NULL) then last_added is increased. When zval is cached, if there is room, last_added is decreased and then the zval pointer will be assigned to it. This means that some positions may become hot points and stay in the cache. Imagine we have 5 pointers in a line 1. last_added = list_item->ptr_line + cache->max_items; 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL 0x0 0x0 0x0 0x0 0x0 --- empty_position, always 0x0 <-- last_added 5. free_zval -> if (free_items++ != max_items) {// we can add more *(--last_added) = zval_ptr; } (memory addresses increase downwards) 0x0 0x0 0x0 0x0 0xA <-- last_added --- 0x0 6. free_zval -> if (free_items++ != max_items) {// we can add more *(--last_added) = zval_ptr; } 0x0 0x0 0x0 0xB <-- last_added 0xA --- 0x0 7. free_zval -> if (free_items++ != max_items) {// we can add more *(--last_added) = zval_ptr; } 0x0 0x0 0xC <-- last_added 0xB 0xA --- 0x0 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; 0x0 0x0 0x0 0xB <-- last_added 0xA --- 0x0 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; 0x0 0x0 0x0 0x0 0xA <-- last_added --- 0x0 10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; 0x0 0x0 0x0 0x0 0x0 --- 0x0 <-- last_added */ /* {{{ mysqlnd_palloc_get_zval */ void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bool *allocated) { void *ret = NULL; #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_get_zval %p] *last_added=%p free_items=%d\n", thd_cache, thd_cache? thd_cache->parent->free_list.last_added:NULL, thd_cache->parent->free_items); #endif if (thd_cache) { MYSQLND_ZVAL_PCACHE *cache = thd_cache->parent; LOCK_PCACHE(cache); if ((ret = *cache->free_list.last_added)) { *cache->free_list.last_added++ = NULL; *allocated = FALSE; #ifdef ZTS ((mysqlnd_zval *) ret)->thread_id = thd_cache->thread_id; #endif --cache->free_items; ++cache->get_hits; } else { ++cache->get_misses; } UNLOCK_PCACHE(cache); } if (!ret) { /* We allocate a bit more. The user of this function will use it, but at end it will use only the zval part. Because the zval part is first then when freeing the zval part the whole allocated block will be cleaned, not only the zval part (by the Engine when destructing the zval). */ ALLOC_ZVAL(ret); INIT_PZVAL((zval *) ret); *allocated = TRUE; } else { /* This will set the refcount to 1, increase it, to keep the variable */ INIT_PZVAL(&((mysqlnd_zval *) ret)->zv); ZVAL_ADDREF(&(((mysqlnd_zval *)ret)->zv)); } return ret; } /* }}} */ /* {{{ mysqlnd_palloc_zval_ptr_dtor */ void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd_cache, enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC) { MYSQLND_ZVAL_PCACHE *cache; #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_zval_ptr_dtor %p] parent_block=%p last_in_block=%p *zv=%p type=%d refc=%d\n", thd_cache, thd_cache->parent? thd_cache->parent->block:NULL, thd_cache->parent? thd_cache->parent->last_in_block:NULL, *zv, type, ZVAL_REFCOUNT(*zv)); #endif *copy_ctor_called = FALSE; /* Check whether cache is used and the zval is from the cache */ if (!thd_cache || !(cache = thd_cache->parent) || ((char *)*zv < (char *)thd_cache->parent->block || (char *)*zv > (char *)thd_cache->parent->last_in_block)) { /* This zval is not from the cache block. Thus the refcount is -1 than of a zval from the cache, because the zvals from the cache are owned by it. */ if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) { ; /* do nothing, zval_ptr_dtor will do the job*/ } else if (ZVAL_REFCOUNT(*zv) > 1) { /* Not a prepared statement, then we have to call copy_ctor and then zval_ptr_dtor() In Unicode mode the destruction of the zvals should not call zval_copy_ctor() because then we will leak. I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set to check if we need to call copy_ctor(). If the type is IS_UNICODE, which can happen with PHP6, then we don't need to copy_ctor, as the data doesn't point to our internal buffers. If it's string (in PHP5 always) and in PHP6 if data is binary, then it still points to internal buffers and has to be copied. */ if (Z_TYPE_PP(zv) == IS_STRING) { zval_copy_ctor(*zv); } *copy_ctor_called = TRUE; } else { if (Z_TYPE_PP(zv) == IS_STRING) { ZVAL_NULL(*zv); } } zval_ptr_dtor(zv); return; } /* The zval is from our cache */ /* refcount is always > 1, because we call ZVAL_ADDREF(). Thus test refcount > 2 */ if (ZVAL_REFCOUNT(*zv) > 2) { /* Because the zval is first element in mysqlnd_zval structure, then we can do upcasting from zval to mysqlnd_zval here. Because we know that this zval is part of our pre-allocated block. Now check whether this zval points to ZE allocated memory or to our buffers. If it points to the internal buffers, call copy_ctor() which will do estrndup for strings. And nothing for null, int, double. This branch will be skipped for PS, because there is no need to copy what is pointed by them, as they don't point to the internal buffers. */ if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_INT_BUFFER) { zval_copy_ctor(*zv); *copy_ctor_called = TRUE; ((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_EXT_BUFFER; } /* This will decrease the counter of the user-level (mysqlnd). When the engine layer (the script) has finished working this this zval, when the variable is no more used, out of scope whatever, then it will try zval_ptr_dtor() but and the refcount will reach 1 and the engine won't try to destruct the memory allocated by us. */ zval_ptr_dtor(zv); /* Unfortunately, we can't return this variable to the free_list because it's still used. And this cleaning up will happen at request shutdown :(. */ LOCK_PCACHE(cache); ++cache->put_misses; *(thd_cache->gc_list.last_added++) = (mysqlnd_zval *)*zv; UNLOCK_PCACHE(cache); } else { /* No user reference */ if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_EXT_BUFFER) { /* PS are here and also in Unicode mode, for non-binary */ zval_dtor(*zv); } LOCK_PCACHE(cache); ++cache->put_hits; ++cache->free_items; ((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_FREE; ZVAL_DELREF(*zv); /* Make it 1 */ ZVAL_NULL(*zv); #ifdef ZTS memset(&((mysqlnd_zval *)*zv)->thread_id, 0, sizeof(THREAD_T)); #endif *(--cache->free_list.last_added) = (mysqlnd_zval *)*zv; UNLOCK_PCACHE(cache); } } /* }}} */ /* {{{ mysqlnd_palloc_rinit */ PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache) { return mysqlnd_palloc_init_thd_cache(cache); } /* }}} */ /* {{{ mysqlnd_palloc_rshutdown */ PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache) { MYSQLND_ZVAL_PCACHE *cache; mysqlnd_zval **p; #ifndef MYSQLND_SILENT php_printf("[mysqlnd_palloc_rshutdown %p]\n", thd_cache); #endif if (!thd_cache || !(cache = thd_cache->parent)) { return; } /* Keep in mind that for pthreads pthread_equal() should be used to be fully standard compliant. However, the PHP code all-around, incl. the the Zend MM uses direct comparison. */ p = thd_cache->gc_list.ptr_line; while (p < thd_cache->gc_list.last_added) { zval_dtor(&(*p)->zv); p++; } p = thd_cache->gc_list.ptr_line; LOCK_PCACHE(cache); while (p < thd_cache->gc_list.last_added) { (*p)->point_type = MYSQLND_POINTS_FREE; *(--cache->free_list.last_added) = *p; ++cache->free_items; #ifdef ZTS memset(&((*p)->thread_id), 0, sizeof(THREAD_T)); #endif p++; } UNLOCK_PCACHE(cache); mysqlnd_palloc_free_thd_cache_reference(&thd_cache); } /* }}} */ /* {{{ mysqlnd_palloc_rshutdown */ PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value) { if (cache) { #if PHP_MAJOR_VERSION >= 6 TSRMLS_FETCH(); #endif LOCK_PCACHE(cache); array_init(return_value); #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { UChar *ustr; int ulen; zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_hits", sizeof("put_hits") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_misses", sizeof("put_misses") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_hits", sizeof("get_hits") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_misses", sizeof("get_misses") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "size", sizeof("size") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "free_items", sizeof("free_items") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "references", sizeof("references") TSRMLS_CC); add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits); efree(ustr); } else #endif { add_assoc_long_ex(return_value, "put_hits", sizeof("put_hits"), cache->put_hits); add_assoc_long_ex(return_value, "put_misses", sizeof("put_misses"), cache->put_misses); add_assoc_long_ex(return_value, "get_hits", sizeof("get_hits"), cache->get_hits); add_assoc_long_ex(return_value, "get_misses", sizeof("get_misses"), cache->get_misses); add_assoc_long_ex(return_value, "size", sizeof("size"), cache->max_items); add_assoc_long_ex(return_value, "free_items", sizeof("free_items"), cache->free_items); add_assoc_long_ex(return_value, "references", sizeof("references"), cache->references); } UNLOCK_PCACHE(cache); } else { ZVAL_NULL(return_value); } } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_palloc.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_palloc.h +++ php-src/ext/mysqlnd/mysqlnd_palloc.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_palloc.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_PALLOC_H #define MYSQLND_PALLOC_H /* Session caching allocator */ struct st_mysqlnd_zval_list { zval **ptr_line; zval **last_added; }; typedef struct st_mysqlnd_zval_cache MYSQLND_ZVAL_CACHE; struct st_mysqlnd_zval_cache { struct st_mysqlnd_zval_list *free_list; unsigned int free_items; unsigned int max_items; unsigned int references; unsigned long get_hits; unsigned long get_misses; unsigned long put_hits; unsigned long put_full_misses; unsigned long put_refcount_misses; }; enum mysqlnd_zval_ptr_type { MYSQLND_POINTS_INT_BUFFER, MYSQLND_POINTS_EXT_BUFFER, MYSQLND_POINTS_FREE }; /* Persistent caching allocator */ typedef struct st_mysqlnd_zval { /* Should be first */ zval zv; enum mysqlnd_zval_ptr_type point_type; #ifdef ZTS THREAD_T thread_id; #endif } mysqlnd_zval; typedef struct st_mysqlnd_ndzval_list { mysqlnd_zval **ptr_line; /* we allocate this, all are pointers to the block */ mysqlnd_zval **last_added; /* this points to the ptr_line, and moves left-right. It's our stack */ } mysqlnd_ndzval_list; struct st_mysqlnd_zval_pcache { mysqlnd_zval *block; mysqlnd_zval *last_in_block; mysqlnd_ndzval_list free_list; /* Fetch from here */ #ifdef ZTS MUTEX_T LOCK_access; #endif unsigned int references; /* These are just for statistics and not used for operational purposes */ unsigned int free_items; unsigned int max_items; unsigned long get_hits; unsigned long get_misses; unsigned long put_hits; unsigned long put_misses; }; struct st_mysqlnd_thread_zval_pcache { struct st_mysqlnd_zval_pcache *parent; unsigned int references; #ifdef ZTS THREAD_T thread_id; #endif mysqlnd_ndzval_list gc_list; /* GC these from time to time */ }; #endif /* MYSQLND_PALLOC_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_portability.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_portability.h +++ php-src/ext/mysqlnd/mysqlnd_portability.h /* 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 == 0) 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 (SIZEOF_ULONG == 0) && !defined(__USE_MISC) && !defined(ulong) typedef unsigned long ulong; /* Short for unsigned long */ #endif #ifndef longlong_defined #if defined(HAVE_LONG_LONG) && SIZEOF_LONG != 8 typedef unsigned long long int ulonglong; /* ulong or unsigned long long */ typedef long long int longlong; #else typedef unsigned long ulonglong; /* ulong or unsigned long long */ typedef long longlong; #endif #endif #define int1store(T,A) do { *((zend_uchar*) (T)) = (A); } while(0) #define uint1korr(A) (*(((uint8*)(A)))) /* Bit values are sent in reverted order of bytes, compared to normal !!! */ #define bit_uint2korr(A) ((uint16) (((uint16) (((uchar*) (A))[1])) +\ ((uint16) (((uchar*) (A))[0]) << 8))) #define bit_uint3korr(A) ((uint32) (((uint32) (((uchar*) (A))[2])) +\ (((uint32) (((uchar*) (A))[1])) << 8) +\ (((uint32) (((uchar*) (A))[0])) << 16))) #define bit_uint4korr(A) ((uint32) (((uint32) (((uchar*) (A))[3])) +\ (((uint32) (((uchar*) (A))[2])) << 8) +\ (((uint32) (((uchar*) (A))[1])) << 16) +\ (((uint32) (((uchar*) (A))[0])) << 24))) #define bit_uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16) +\ (((uint32) ((uchar) (A)[3])) << 24)) +\ (((ulonglong) ((uchar) (A)[4])) << 32)) #define bit_uint6korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[5])) +\ (((uint32) (((uchar*) (A))[4])) << 8) +\ (((uint32) (((uchar*) (A))[3])) << 16) +\ (((uint32) (((uchar*) (A))[2])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[1])) +\ (((uint32) (((uchar*) (A))[0]) << 8)))) << 32)) #define bit_uint7korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[6])) +\ (((uint32) (((uchar*) (A))[5])) << 8) +\ (((uint32) (((uchar*) (A))[4])) << 16) +\ (((uint32) (((uchar*) (A))[3])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[2])) +\ (((uint32) (((uchar*) (A))[1])) << 8) +\ (((uint32) (((uchar*) (A))[0])) << 16))) << 32)) #define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\ (((uint32) (((uchar*) (A))[6])) << 8) +\ (((uint32) (((uchar*) (A))[5])) << 16) +\ (((uint32) (((uchar*) (A))[4])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[3])) +\ (((uint32) (((uchar*) (A))[2])) << 8) +\ (((uint32) (((uchar*) (A))[1])) << 16) +\ (((uint32) (((uchar*) (A))[0])) << 24))) << 32)) /* ** Define-funktions for reading and storing in machine independent format ** (low byte first) */ /* Optimized store functions for Intel x86, non-valid for WIN64 */ #if defined(__i386__) && !defined(_WIN64) #define sint2korr(A) (*((int16 *) (A))) #define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ (((uint32) 255L << 24) | \ (((uint32) (uchar) (A)[2]) << 16) |\ (((uint32) (uchar) (A)[1]) << 8) | \ ((uint32) (uchar) (A)[0])) : \ (((uint32) (uchar) (A)[2]) << 16) |\ (((uint32) (uchar) (A)[1]) << 8) | \ ((uint32) (uchar) (A)[0]))) #define sint4korr(A) (*((long *) (A))) #define uint2korr(A) (*((uint16 *) (A))) #define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16)) #define uint4korr(A) (*((unsigned long *) (A))) #define uint8korr(A) (*((ulonglong *) (A))) #define sint8korr(A) (*((longlong *) (A))) #define int2store(T,A) *((uint16*) (T))= (uint16) (A) #define int3store(T,A) { \ *(T)= (uchar) ((A));\ *(T+1)=(uchar) (((uint) (A) >> 8));\ *(T+2)=(uchar) (((A) >> 16)); } #define int4store(T,A) *((long *) (T))= (long) (A) #define int5store(T,A) { \ *((uchar *)(T))= (uchar)((A));\ *(((uchar *)(T))+1)=(uchar) (((A) >> 8));\ *(((uchar *)(T))+2)=(uchar) (((A) >> 16));\ *(((uchar *)(T))+3)=(uchar) (((A) >> 24)); \ *(((uchar *)(T))+4)=(uchar) (((A) >> 32)); } /* From Andrey Hristov, based on int5store() */ #define int6store(T,A) { \ *(((uchar *)(T)))= (uchar)((A));\ *(((uchar *)(T))+1))=(uchar) (((A) >> 8));\ *(((uchar *)(T))+2))=(uchar) (((A) >> 16));\ *(((uchar *)(T))+3))=(uchar) (((A) >> 24)); \ *(((uchar *)(T))+4))=(uchar) (((A) >> 32)); \ *(((uchar *)(T))+5))=(uchar) (((A) >> 40)); } #define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A) typedef union { double v; long m[2]; } doubleget_union; #define doubleget(V,M) { ((doubleget_union *)&(V))->m[0] = *((long*) (M)); \ ((doubleget_union *)&(V))->m[1] = *(((long*) (M))+1); } #define doublestore(T,V) { *((long *) (T)) = ((doubleget_union *)&(V))->m[0]; \ *(((long *) (T))+1) = ((doubleget_union *)&(V))->m[1]; } #define float4get(V,M) { *((float *) &(V)) = *((float*) (M)); } #define float8get(V,M) doubleget((V),(M)) /* From Andrey Hristov based on doubleget */ #define floatget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(float)) #define floatstore float4store #define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float)) #define float8store(V,M) doublestore((V),(M)) #endif /* __i386__ */ #ifndef sint2korr #define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\ ((int16) ((int16) (A)[1]) << 8)) #define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ (((uint32) 255L << 24) | \ (((uint32) (uchar) (A)[2]) << 16) |\ (((uint32) (uchar) (A)[1]) << 8) | \ ((uint32) (uchar) (A)[0])) : \ (((uint32) (uchar) (A)[2]) << 16) |\ (((uint32) (uchar) (A)[1]) << 8) | \ ((uint32) (uchar) (A)[0]))) #define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\ (((int32) ((uchar) (A)[1]) << 8)) +\ (((int32) ((uchar) (A)[2]) << 16)) +\ (((int32) ((int16) (A)[3]) << 24))) #define sint8korr(A) (longlong) uint8korr(A) #define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\ ((uint16) ((uchar) (A)[1]) << 8)) #define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16)) #define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16) +\ (((uint32) ((uchar) (A)[3])) << 24)) #define bit_uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16) +\ (((uint32) ((uchar) (A)[3])) << 24)) +\ (((ulonglong) ((uchar) (A)[4])) << 32)) /* From Andrey Hristov, based on uint5korr */ #define bit_uint6korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[5])) +\ (((uint32) (((uchar*) (A))[4])) << 8) +\ (((uint32) (((uchar*) (A))[3])) << 16) +\ (((uint32) (((uchar*) (A))[2])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[1])) +\ (((uint32) (((uchar*) (A))[0]) << 8)))) << 32)) #define bit_uint7korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[6])) +\ (((uint32) (((uchar*) (A))[5])) << 8) +\ (((uint32) (((uchar*) (A))[4])) << 16) +\ (((uint32) (((uchar*) (A))[3])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[2])) +\ (((uint32) (((uchar*) (A))[1])) << 8) +\ (((uint32) (((uchar*) (A))[0])) << 16))) << 32)) #define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\ (((uint32) (((uchar*) (A))[6])) << 8) +\ (((uint32) (((uchar*) (A))[5])) << 16) +\ (((uint32) (((uchar*) (A))[4])) << 24)) +\ (((ulonglong) (((uint32) (((uchar*) (A))[3])) +\ (((uint32) (((uchar*) (A))[2])) << 8) +\ (((uint32) (((uchar*) (A))[1])) << 16) +\ (((uint32) (((uchar*) (A))[0])) << 24))) << 32)) #define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ (((uint32) ((uchar) (A)[1])) << 8) +\ (((uint32) ((uchar) (A)[2])) << 16) +\ (((uint32) ((uchar) (A)[3])) << 24)) +\ (((ulonglong) (((uint32) ((uchar) (A)[4])) +\ (((uint32) ((uchar) (A)[5])) << 8) +\ (((uint32) ((uchar) (A)[6])) << 16) +\ (((uint32) ((uchar) (A)[7])) << 24))) << 32)) #define int2store(T,A) do { uint def_temp= (uint) (A) ;\ *((uchar*) (T)) = (uchar)(def_temp); \ *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0) #define int3store(T,A) do { /*lint -save -e734 */\ *(((char *)(T))) = (char) ((A));\ *(((char *)(T))+1) = (char) (((A) >> 8));\ *(((char *)(T))+2) = (char) (((A) >> 16)); \ /*lint -restore */} while (0) #define int4store(T,A) do { \ *(((char *)(T))) = (char) ((A));\ *(((char *)(T))+1) = (char) (((A) >> 8));\ *(((char *)(T))+2) = (char) (((A) >> 16));\ *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0) #define int5store(T,A) do { \ *(((char *)(T))) = (char)((A));\ *(((char *)(T))+1) = (char)(((A) >> 8));\ *(((char *)(T))+2) = (char)(((A) >> 16));\ *(((char *)(T))+3) = (char)(((A) >> 24)); \ *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0) /* Based on int5store() from Andrey Hristov */ #define int6store(T,A) do { \ *(((char *)(T))) = (char)((A));\ *(((char *)(T))+1) = (char)(((A) >> 8));\ *(((char *)(T))+2) = (char)(((A) >> 16));\ *(((char *)(T))+3) = (char)(((A) >> 24)); \ *(((char *)(T))+4) = (char)(((A) >> 32)); \ *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0) #define int8store(T,A) { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \ int4store((T),def_temp); \ int4store((T+4),def_temp2); \ } #ifdef WORDS_BIGENDIAN #define float4store(T,A) do { \ *(((char *)(T))) = (char) ((char *) &A)[3];\ *(((char *)(T))+1) = (char) ((char *) &A)[2];\ *(((char *)(T))+2) = (char) ((char *) &A)[1];\ *(((char *)(T))+3) = (char) ((char *) &A)[0]; } while (0) #define float4get(V,M) do { float def_temp;\ ((char*) &def_temp)[0] = (M)[3];\ ((char*) &def_temp)[1] = (M)[2];\ ((char*) &def_temp)[2] = (M)[1];\ ((char*) &def_temp)[3] = (M)[0];\ (V)=def_temp; } while (0) #define float8store(T,V) do { \ *(((char *)(T))) = (char) ((char *) &(V))[7];\ *(((char *)(T))+1) = (char) ((char *) &(V))[6];\ *(((char *)(T))+2) = (char) ((char *) &(V))[5];\ *(((char *)(T))+3) = (char) ((char *) &(V))[4];\ *(((char *)(T))+4) = (char) ((char *) &(V))[3];\ *(((char *)(T))+5) = (char) ((char *) &(V))[2];\ *(((char *)(T))+6) = (char) ((char *) &(V))[1];\ *(((char *)(T))+7) = (char) ((char *) &(V))[0]; } while (0) #define float8get(V,M) do { double def_temp;\ ((char*) &def_temp)[0] = (M)[7];\ ((char*) &def_temp)[1] = (M)[6];\ ((char*) &def_temp)[2] = (M)[5];\ ((char*) &def_temp)[3] = (M)[4];\ ((char*) &def_temp)[4] = (M)[3];\ ((char*) &def_temp)[5] = (M)[2];\ ((char*) &def_temp)[6] = (M)[1];\ ((char*) &def_temp)[7] = (M)[0];\ (V) = def_temp; \ } while (0) #else #define float4get(V,M) memcpy((char*) &(V),(char*) (M),sizeof(float)) #define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float)) #if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) #define doublestore(T,V) do { \ *(((char *)(T)))= ((char *) &(V))[4];\ *(((char *)(T))+1)=(char) ((char *) &(V))[5];\ *(((char *)(T))+2)=(char) ((char *) &(V))[6];\ *(((char *)(T))+3)=(char) ((char *) &(V))[7];\ *(((char *)(T))+4)=(char) ((char *) &(V))[0];\ *(((char *)(T))+5)=(char) ((char *) &(V))[1];\ *(((char *)(T))+6)=(char) ((char *) &(V))[2];\ *(((char *)(T))+7)=(char) ((char *) &(V))[3];} while (0) #define doubleget(V,M) do { double def_temp;\ ((char*) &def_temp)[0]=(M)[4];\ ((char*) &def_temp)[1]=(M)[5];\ ((char*) &def_temp)[2]=(M)[6];\ ((char*) &def_temp)[3]=(M)[7];\ ((char*) &def_temp)[4]=(M)[0];\ ((char*) &def_temp)[5]=(M)[1];\ ((char*) &def_temp)[6]=(M)[2];\ ((char*) &def_temp)[7]=(M)[3];\ (V) = def_temp; } while (0) #endif /* __FLOAT_WORD_ORDER */ #define float8get(V,M) doubleget((V),(M)) #define float8store(V,M) doublestore((V),(M)) #endif /* WORDS_BIGENDIAN */ #endif /* sint2korr */ /* Define-funktions for reading and storing in machine format from/to short/long to/from some place in memory V should be a (not register) variable, M is a pointer to byte */ #ifdef WORDS_BIGENDIAN #define ushortget(V,M) { V = (uint16) (((uint16) ((uchar) (M)[1]))+\ ((uint16) ((uint16) (M)[0]) << 8)); } #define shortget(V,M) { V = (short) (((short) ((uchar) (M)[1]))+\ ((short) ((short) (M)[0]) << 8)); } #define longget(V,M) do { int32 def_temp;\ ((char*) &def_temp)[0]=(M)[0];\ ((char*) &def_temp)[1]=(M)[1];\ ((char*) &def_temp)[2]=(M)[2];\ ((char*) &def_temp)[3]=(M)[3];\ (V)=def_temp; } while (0) #define ulongget(V,M) do { uint32 def_temp;\ ((char*) &def_temp)[0]=(M)[0];\ ((char*) &def_temp)[1]=(M)[1];\ ((char*) &def_temp)[2]=(M)[2];\ ((char*) &def_temp)[3]=(M)[3];\ (V)=def_temp; } while (0) #define shortstore(T,A) do { \ uint def_temp=(uint) (A) ;\ *(((char *)(T))+1)=(char)(def_temp); \ *(((char *)(T))+0)=(char)(def_temp >> 8); } while (0) #define longstore(T,A) do { \ *(((char *)(T))+3)=(char)((A));\ *(((char *)(T))+2)=(char)(((A) >> 8));\ *(((char *)(T))+1)=(char)(((A) >> 16));\ *(((char *)(T))+0)=(char)(((A) >> 24)); } while (0) #define doubleget(V,M) memcpy((char*) &(V),(char*) (M), sizeof(double)) #define doublestore(T,V) memcpy((char*) (T),(char*) &(V), sizeof(double)) #define longlongget(V,M) memcpy((char*) &(V),(char*) (M), sizeof(ulonglong)) #define longlongstore(T,V) memcpy((char*) (T),(char*) &(V), sizeof(ulonglong)) #else #define ushortget(V,M) { V = uint2korr((M)); } #define shortget(V,M) { V = sint2korr((M)); } #define longget(V,M) { V = sint4korr((M)); } #define ulongget(V,M) { V = uint4korr((M)); } #define shortstore(T,V) int2store((T),(V)) #define longstore(T,V) int4store((T),(V)) #ifndef doubleget #define doubleget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(double)) #define doublestore(T,V) memcpy((char*) (T),(char*) &(V),sizeof(double)) #endif /* doubleget */ #define longlongget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(ulonglong)) #define longlongstore(T,V) memcpy((char*) (T),(char*) &(V),sizeof(ulonglong)) #endif /* WORDS_BIGENDIAN */ #ifdef PHP_WIN32 #define MYSQLND_LLU_SPEC "%I64u" #define MYSQLND_LL_SPEC "%I64d" #ifndef L64 #define L64(x) x##i64 #endif typedef unsigned __int64 my_uint64; typedef __int64 my_int64; typedef unsigned __int64 mynd_ulonglong; typedef __int64 mynd_longlong; #else #define MYSQLND_LLU_SPEC "%llu" #define MYSQLND_LL_SPEC "%lld" #ifndef L64 #define L64(x) x##LL #endif #ifndef HAVE_UINT64_T typedef unsigned long long my_uint64; typedef unsigned long long mynd_ulonglong; #else typedef uint64_t my_uint64; typedef uint64_t mynd_ulonglong; #endif #ifndef HAVE_INT64_T == 0) 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 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_priv.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_priv.h +++ php-src/ext/mysqlnd/mysqlnd_priv.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_priv.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_PRIV_H #define MYSQLND_PRIV_H #ifdef ZTS #include "TSRM.h" #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef pestrndup #define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length))) #endif #define MYSQLND_CLASS_METHODS_START(class) static \ struct st_##class##_methods mysqlnd_##class##_methods = { #define MYSQLND_CLASS_METHODS_END } #define MYSQLND_METHOD(class, method) php_##class##_##method##_pub #define MYSQLND_METHOD_PRIVATE(class, method) php_##class##_##method##_priv #if PHP_MAJOR_VERSION < 6 #define mysqlnd_array_init(arg, field_count) \ { \ ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\ zend_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0); \ Z_TYPE_P(arg) = IS_ARRAY;\ } #else #define mysqlnd_array_init(arg, field_count) \ { \ ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\ zend_u_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0, 0);\ Z_TYPE_P(arg) = IS_ARRAY;\ } #endif #define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ #define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ #define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ /* The server was able to fulfill the clients request and opened a read-only non-scrollable cursor for a query. This flag comes in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. */ #define SERVER_STATUS_CURSOR_EXISTS 64 /* This flag is sent when a read-only cursor is exhausted, in reply to COM_STMT_FETCH command. */ #define SERVER_STATUS_LAST_ROW_SENT 128 #define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ #define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512 /* Client Error codes */ #define CR_UNKNOWN_ERROR 2000 #define CR_CONNECTION_ERROR 2002 #define CR_SERVER_GONE_ERROR 2006 #define CR_OUT_OF_MEMORY 2008 #define CR_SERVER_LOST 2013 #define CR_COMMANDS_OUT_OF_SYNC 2014 #define CR_CANT_FIND_CHARSET 2019 #define CR_MALFORMED_PACKET 2027 #define CR_NOT_IMPLEMENTED 2054 #define CR_NO_PREPARE_STMT 2030 #define CR_PARAMS_NOT_BOUND 2031 #define CR_INVALID_PARAMETER_NO 2034 #define CR_INVALID_BUFFER_USE 2035 #define MYSQLND_EE_READ 2 #define MYSQLND_EE_FILENOTFOUND 29 #define UNKNOWN_SQLSTATE "HY000" #define MAX_CHARSET_LEN 32 #define SET_ERROR_AFF_ROWS(s) (s)->upsert_status.affected_rows = (mynd_ulonglong) ~0 /* Error handling */ #define SET_NEW_MESSAGE(buf, buf_len, message, len) \ {\ if ((buf)) { \ efree((buf)); \ } \ (buf) = (message); \ (buf_len) = (len); \ /* Transfer ownership*/ \ (message) = NULL; \ } #define SET_EMPTY_MESSAGE(buf, buf_len) \ {\ if ((buf)) { \ efree((buf)); \ (buf) = NULL; \ } \ (buf_len) = 0; \ } #define SET_EMPTY_ERROR(error_info) \ { \ error_info.error_no = 0; \ error_info.error[0] = '\0'; \ strcpy(error_info.sqlstate, "00000"); \ } #define SET_CLIENT_ERROR(error_info, a, b, c) \ { \ error_info.error_no = a; \ strncpy(error_info.sqlstate, b, sizeof(error_info.sqlstate)); \ strncpy(error_info.error, c, sizeof(error_info.error)); \ } #define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(stmt->error_info, a, b, c) /* PS stuff */ typedef void (*ps_field_fetch_func)(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool everything_as_unicode TSRMLS_DC); struct st_mysqlnd_perm_bind { ps_field_fetch_func func; /* should be signed int */ int pack_len; unsigned int php_type; zend_bool is_possibly_blob; zend_bool can_ret_as_str_in_uni; }; extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; extern const char * mysqlnd_out_of_sync; enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC); void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */ void mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, size_t *buf_len, unsigned int null_byte_offset); void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode, unsigned int byte_count, zend_bool is_bit TSRMLS_DC); int mysqlnd_set_sock_no_delay(php_stream *stream); #endif /* MYSQLND_PRIV_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_ps.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_ps.c +++ php-src/ext/mysqlnd/mysqlnd_ps.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_ps.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" #include "mysqlnd_result.h" #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" #define MYSQLND_SILENT const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types"; const char * const mysqlnd_stmt_not_prepared = "Statement not prepared"; /* Exported by mysqlnd.c */ enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum php_mysql_packet_type ok_packet, zend_bool silent TSRMLS_DC); /* Exported by mysqlnd_ps_codec.c */ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, zend_bool *free_buffer); MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC); enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC); enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC); void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt); /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) { enum_func_status ret; MYSQLND *conn = stmt->conn; MYSQLND_RES *result; zend_bool to_cache = FALSE; /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { return NULL; } if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ return stmt->m->use_result(stmt TSRMLS_CC); } /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } stmt->default_rset_handler = stmt->m->store_result; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PS_BUFFERED_SETS); result = stmt->result; result->type = MYSQLND_RES_PS_BUF; result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered; result->m.fetch_lengths = NULL;/* makes no sense */ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); /* Create room for 'next_extend' rows */ /* Not set for SHOW statements at PREPARE stage */ if (result->conn) { result->conn->m->free_reference(result->conn TSRMLS_CC); result->conn = NULL; /* store result does not reference the connection */ } ret = mysqlnd_store_result_fetch_data(conn, result, result->meta, TRUE, stmt->update_max_length, to_cache TSRMLS_CC); if (PASS == ret) { /* libmysql API docs say it should be so for SELECT statements */ stmt->upsert_status.affected_rows = stmt->result->data->row_count; stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; } else { conn->error_info = result->data->error_info; stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); efree(stmt->result); stmt->result = NULL; stmt->state = MYSQLND_STMT_PREPARED; } return result; } /* }}} */ /* {{{ mysqlnd_stmt::get_result */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) { MYSQLND *conn = stmt->conn; MYSQLND_RES *result; /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { return NULL; } if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ return stmt->m->use_result(stmt TSRMLS_CC); } /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); result = mysqlnd_result_init(stmt->result->field_count, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE); /* Not set for SHOW statements at PREPARE stage */ if (stmt->result->conn) { stmt->result->conn->m->free_reference(stmt->result->conn TSRMLS_CC); stmt->result->conn = NULL; /* store result does not reference the connection */ } if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { stmt->upsert_status.affected_rows = result->data->row_count; stmt->state = MYSQLND_STMT_PREPARED; result->type = MYSQLND_RES_PS_BUF; } else { stmt->error_info = conn->error_info; stmt->state = MYSQLND_STMT_PREPARED; } return result; } /* }}} */ /* {{{ mysqlnd_stmt_skip_metadata */ static enum_func_status mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) { /* Follows parameter metadata, we have just to skip it, as libmysql does */ unsigned int i = 0; php_mysql_packet_res_field field_packet; PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); field_packet.skip_parsing = TRUE; for (;i < stmt->param_count; i++) { if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) { PACKET_FREE_ALLOCA(field_packet); return FAIL; } } PACKET_FREE_ALLOCA(field_packet); return PASS; } /* }}} */ /* {{{ mysqlnd_stmt_read_prepare_response */ static enum_func_status mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) { php_mysql_packet_prepare_response prepare_resp; PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET); if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) { PACKET_FREE_ALLOCA(prepare_resp); return FAIL; } if (0xFF == prepare_resp.error_code) { stmt->error_info = stmt->conn->error_info = prepare_resp.error_info; return FAIL; } stmt->stmt_id = prepare_resp.stmt_id; stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp.warning_count; stmt->field_count = stmt->conn->field_count = prepare_resp.field_count; stmt->param_count = prepare_resp.param_count; PACKET_FREE_ALLOCA(prepare_resp); return PASS; } /* }}} */ /* {{{ mysqlnd_stmt_prepare_read_eof */ static enum_func_status mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) { php_mysql_packet_eof fields_eof; enum_func_status ret; PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, stmt->conn))) { if (stmt->result) { stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); efree(stmt->result); memset(stmt, 0, sizeof(MYSQLND_STMT)); stmt->state = MYSQLND_STMT_INITTED; } } else { stmt->upsert_status.server_status = fields_eof.server_status; stmt->upsert_status.warning_count = fields_eof.warning_count; stmt->state = MYSQLND_STMT_PREPARED; } PACKET_FREE_ALLOCA(fields_eof); return ret; } /* }}} */ /* {{{ mysqlnd_stmt::prepare */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC) { MYSQLND_STMT *stmt_to_prepare = stmt; SET_ERROR_AFF_ROWS(stmt); SET_ERROR_AFF_ROWS(stmt->conn); SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->state > MYSQLND_STMT_INITTED) { /* See if we have to clean the wire */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = stmt->m->use_result; stmt->default_rset_handler(stmt TSRMLS_CC); } /* No 'else' here please :) */ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* Create a new test statement, which we will prepare, but if anything fails, we will scrap it. */ stmt_to_prepare = mysqlnd_stmt_init(stmt->conn); } if (FAIL == mysqlnd_simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, query_len, PROT_LAST, FALSE TSRMLS_CC) || FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) { goto fail; } if (stmt_to_prepare->param_count) { if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) || FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) { goto fail; } } /* Read metadata only if there is actual result set. Beware that SHOW statements bypass the PS framework and thus they send no metadata at prepare. */ if (stmt_to_prepare->field_count) { MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count, NULL); /* Allocate the result now as it is needed for the reading of metadata */ stmt_to_prepare->result = result; result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn); result->type = MYSQLND_RES_PS_BUF; if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) || FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) { goto fail; } } if (stmt_to_prepare != stmt) { /* Free old buffers, binding and resources on server */ stmt->m->close(stmt, TRUE TSRMLS_CC); memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); /* Now we will have a clean new statement object */ efree(stmt_to_prepare); } stmt->state = MYSQLND_STMT_PREPARED; return PASS; fail: if (stmt_to_prepare != stmt) { stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC); } stmt->state = MYSQLND_STMT_INITTED; return FAIL; } /* }}} */ /* {{{ mysqlnd_stmt::execute */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) { enum_func_status ret; MYSQLND *conn = stmt->conn; zend_uchar *request; size_t request_len; zend_bool free_request; SET_ERROR_AFF_ROWS(stmt); SET_ERROR_AFF_ROWS(stmt->conn); if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) { if (stmt->result_bind && stmt->result_zvals_separated_once == TRUE && stmt->state >= MYSQLND_STMT_USER_FETCHING) { /* We need to copy the data from the buffers which we will clean. The bound variables point to them only if the user has started to fetch data (MYSQLND_STMT_USER_FETCHING). We need to check 'result_zvals_separated_once' or we will leak in the following scenario prepare("select 1 from dual"); execute(); fetch(); <-- no binding, but that's not a problem bind_result(); execute(); <-- here we will leak because we separate without need */ unsigned int i; for (i = 0; i < stmt->field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { zval_copy_ctor(stmt->result_bind[i].zv); } } } /* Executed, but the user hasn't started to fetch This will clean also the metadata, but after the EXECUTE call we will have it again. */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); } else if (stmt->state < MYSQLND_STMT_PREPARED) { /* Only initted - error */ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } if (stmt->param_count && !stmt->param_bind) { SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, "No data supplied for parameters in prepared statement"); return FAIL; } request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request); /* support for buffer types should be added here ! */ ret = mysqlnd_simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, PROT_LAST /* we will handle the response packet*/, FALSE TSRMLS_CC); if (free_request) { efree(request); } if (ret == FAIL) { stmt->error_info = conn->error_info; return FAIL; } ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); if (ret == FAIL) { stmt->error_info = conn->error_info; stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; if (conn->state == CONN_QUIT_SENT) { /* close the statement here, the connection has been closed */ } } else { SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); stmt->send_types_to_server = 0; stmt->upsert_status = conn->upsert_status; stmt->state = MYSQLND_STMT_EXECUTED; if (conn->last_query_type == QUERY_UPSERT) { stmt->upsert_status = conn->upsert_status; return PASS; } else if (conn->last_query_type == QUERY_LOAD_LOCAL) { return PASS; } stmt->result->type = MYSQLND_RES_PS_BUF; if (!stmt->result->conn) { /* For SHOW we don't create (bypasses PS in server) a result set at prepare and thus a connection was missing */ stmt->result->conn = stmt->conn->m->get_reference(stmt->conn); } /* Update stmt->field_count as SHOW sets it to 0 at prepare */ stmt->field_count = stmt->result->field_count = conn->field_count; stmt->result->lengths = NULL; if (stmt->field_count) { stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE; /* We need to set this because the user might not call use_result() or store_result() and we should be able to scrap the data on the line, if he just decides to close the statement. */ #ifndef MYSQLND_SILENT php_printf("server_status=%d cursor=%d\n", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); #endif if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) { stmt->cursor_exists = TRUE; conn->state = CONN_READY; /* Only cursor read */ stmt->default_rset_handler = stmt->m->use_result; } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) { /* We have asked for CURSOR but got no cursor, because the condition above is not fulfilled. Then... This is a single-row result set, a result set with no rows, EXPLAIN, SHOW VARIABLES, or some other command which either a) bypasses the cursors framework in the server and writes rows directly to the network or b) is more efficient if all (few) result set rows are precached on client and server's resources are freed. */ /* preferred is buffered read */ stmt->default_rset_handler = stmt->m->store_result; } else { /* preferred is unbuffered read */ stmt->default_rset_handler = stmt->m->use_result; } } } return ret; } /* }}} */ /* {{{ mysqlnd_fetch_stmt_row_buffered */ enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { unsigned int i; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; /* If we haven't read everything */ if (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) { /* The user could have skipped binding - don't crash*/ if (stmt->result_bind) { zval **current_row = *result->data->data_cursor; for (i = 0; i < result->field_count; i++) { /* Clean what we copied last time */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_dtor(stmt->result_bind[i].zv); #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { if (Z_TYPE_P(current_row[i]) != IS_NULL) { /* Copy the value. Pre-condition is that the zvals in the result_bind buffer have been ZVAL_NULL()-ed or to another simple type (int, double, bool but not string). Because of the reference counting the user can't delete the strings the variables point to. */ Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]); stmt->result_bind[i].zv->value = current_row[i]->value; #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_copy_ctor(stmt->result_bind[i].zv); #endif } else { ZVAL_NULL(stmt->result_bind[i].zv); } } } } result->data->data_cursor++; *fetched_anything = TRUE; } else { result->data->data_cursor = NULL; *fetched_anything = FALSE; #ifndef MYSQLND_SILENT php_printf("NO MORE DATA\n "); #endif } return PASS; } /* }}} */ /* {{{ mysqlnd_stmt_fetch_row_unbuffered */ enum_func_status mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; unsigned int i, field_count = result->field_count; php_mysql_packet_row *row_packet = result->row_packet; if (result->unbuf->eof_reached) { /* No more rows obviously */ *fetched_anything = FALSE; return PASS; } if (result->conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } /* Let the row packet fill our buffer and skip additional malloc + memcpy */ row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE; /* If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to mysqlnd_unbuffered_free_last_data() before it. The function returns always true. */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { result->unbuf->row_count++; *fetched_anything = TRUE; if (!row_packet->skip_extraction) { mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; row_packet->row_buffer = NULL; for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { zval *data = result->unbuf->last_row_data[i]; /* stmt->result_bind[i].zv has been already destructed in mysqlnd_unbuffered_free_last_data() */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_dtor(stmt->result_bind[i].zv); #endif if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { stmt->result_bind[i].zv->value = data->value; #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_copy_ctor(stmt->result_bind[i].zv); #endif if ( (Z_TYPE_P(data) == IS_STRING #if PHP_MAJOR_VERSION >= 6 || Z_TYPE_P(data) == IS_UNICODE #endif ) && (result->meta->fields[i].max_length < Z_STRLEN_P(data))) { result->meta->fields[i].max_length = Z_STRLEN_P(data); } } } } } else { /* Data has been allocated and usually mysqlnd_unbuffered_free_last_data() frees it but we can't call this function as it will cause problems with the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ efree(row_packet->row_buffer); row_packet->row_buffer = NULL; } } else if (ret == FAIL) { if (row_packet->error_info.error_no) { stmt->conn->error_info = row_packet->error_info; stmt->error_info = row_packet->error_info; } *fetched_anything = FALSE; result->conn->state = CONN_READY; result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; result->conn->upsert_status.warning_count = row_packet->warning_count; result->conn->upsert_status.server_status = row_packet->server_status; /* result->row_packet will be cleaned when destroying the result object */ if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { result->conn->state = CONN_NEXT_RESULT_PENDING; } else { result->conn->state = CONN_READY; } *fetched_anything = FALSE; } return ret; } /* }}} */ /* {{{ mysqlnd_stmt::use_result */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) { MYSQLND_RES *result; MYSQLND *conn = stmt->conn; if (!stmt->field_count || (!stmt->cursor_exists && conn->state != CONN_FETCHING_DATA) || (stmt->cursor_exists && conn->state != CONN_READY) || (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_PS_UNBUFFERED_SETS); result = stmt->result; result->type = MYSQLND_RES_PS_UNBUF; result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: mysqlnd_stmt_fetch_row_unbuffered; result->m.fetch_lengths = NULL; /* makes no sense */ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); /* Will be freed in the mysqlnd_internal_free_result_contents() called by the resource destructor. mysqlnd_fetch_row_unbuffered() expects this to be not NULL. */ PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *); result->row_packet->field_count = result->field_count; result->row_packet->binary_protocol = TRUE; result->row_packet->fields_metadata = stmt->result->meta->fields; result->row_packet->bit_fields_count = result->meta->bit_fields_count; result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; result->lengths = NULL; stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; /* No multithreading issues as we don't share the connection :) */ return result; } /* }}} */ #define STMT_ID_LENGTH 4 /* {{{ mysqlnd_fetch_row_cursor */ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */]; php_mysql_packet_row *row_packet = result->row_packet; if (!stmt) { return FAIL; } if (stmt->state < MYSQLND_STMT_USER_FETCHING) { /* Only initted - error */ SET_CLIENT_ERROR(stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); int4store(buf, stmt->stmt_id); int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */ if (FAIL == mysqlnd_simple_command(stmt->conn, COM_STMT_FETCH, (char *)buf, sizeof(buf), PROT_LAST /* we will handle the response packet*/, FALSE TSRMLS_CC)) { stmt->error_info = stmt->conn->error_info; return FAIL; } row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE; if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; row_packet->row_buffer = NULL; if (!row_packet->skip_extraction) { /* If no result bind, do nothing. We consumed the data */ for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { zval *data = result->unbuf->last_row_data[i]; /* stmt->result_bind[i].zv has been already destructed in mysqlnd_unbuffered_free_last_data() */ if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { stmt->result_bind[i].zv->value = data->value; #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_copy_ctor(stmt->result_bind[i].zv); #endif if ((Z_TYPE_P(data) == IS_STRING #if PHP_MAJOR_VERSION >= 6 || Z_TYPE_P(data) == IS_UNICODE #endif ) && (result->meta->fields[i].max_length < Z_STRLEN_P(data))) { result->meta->fields[i].max_length = Z_STRLEN_P(data); } } } } } result->unbuf->row_count++; *fetched_anything = TRUE; /* We asked for one row, the next one should be EOF, eat it */ ret = PACKET_READ(row_packet, result->conn); if (row_packet->row_buffer) { efree(row_packet->row_buffer); row_packet->row_buffer = NULL; } } else { *fetched_anything = FALSE; stmt->upsert_status.warning_count = stmt->conn->upsert_status.warning_count = row_packet->warning_count; stmt->upsert_status.server_status = stmt->conn->upsert_status.server_status = row_packet->server_status; result->unbuf->eof_reached = row_packet->eof; } stmt->upsert_status.warning_count = stmt->conn->upsert_status.warning_count = row_packet->warning_count; stmt->upsert_status.server_status = stmt->conn->upsert_status.server_status = row_packet->server_status; return ret; } /* }}} */ /* {{{ mysqlnd_stmt_fetch */ PHPAPI enum_func_status MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC) { if (!stmt->result || stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Execute only once. We have to free the previous contents of user's bound vars */ stmt->default_rset_handler(stmt TSRMLS_CC); } stmt->state = MYSQLND_STMT_USER_FETCHING; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); /* The user might have not bound any variables for result. Do the binding once she does it. */ if (stmt->result_bind && !stmt->result_zvals_separated_once) { unsigned int i; /* mysqlnd_stmt_store_result() has been called free the bind variables to prevent leaking of their previous content. */ for (i = 0; i < stmt->result->field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { zval_dtor(stmt->result_bind[i].zv); ZVAL_NULL(stmt->result_bind[i].zv); } } stmt->result_zvals_separated_once = TRUE; } MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT); return stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); } /* }}} */ /* {{{ mysqlnd_stmt::reset */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) { enum_func_status ret = PASS; MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->stmt_id) { if (stmt->param_bind) { unsigned int i; /* Reset Long Data */ for (i = 0; i < stmt->param_count; i++) { if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) { stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } } } /* If the user decided to close the statement right after execute() We have to call the appropriate use_result() or store_result() and clean. */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { stmt->default_rset_handler(stmt TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } if (stmt->result) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* Now the line should be free, if it wasn't */ int4store(cmd_buf, stmt->stmt_id); if (conn->state == CONN_READY && FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf, sizeof(cmd_buf), PROT_OK_PACKET, FALSE TSRMLS_CC))) { stmt->error_info = conn->error_info; } stmt->upsert_status = conn->upsert_status; stmt->state = MYSQLND_STMT_PREPARED; } return ret; } /* }}} */ /* {{{ mysqlnd_stmt::send_long_data */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_no, const char * const data, unsigned long length TSRMLS_DC) { enum_func_status ret = FAIL; MYSQLND * conn = stmt->conn; zend_uchar *cmd_buf; size_t packet_len; enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); return FAIL; } if (!stmt->param_bind) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } if (param_no >= stmt->param_count) { SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number"); return FAIL; } if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) { SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob); return FAIL; } /* XXX: Unfortunately we have to allocate additional buffer to be able the additional data, which is like a header inside the payload. This should be optimised, but it will be a pervasive change, so mysqlnd_simple_command() will accept not a buffer, but actually MYSQLND_STRING* terminated by NULL, to send. If the strings are not big, we can collapse them on the buffer every connection has, but otherwise we will just send them one by one to the wire. */ if (conn->state == CONN_READY) { stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; cmd_buf = emalloc(packet_len = STMT_ID_LENGTH + 2 + length); int4store(cmd_buf, stmt->stmt_id); int2store(cmd_buf + STMT_ID_LENGTH, param_no); memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ ret = mysqlnd_simple_command(conn, cmd, (char *)cmd_buf, packet_len, PROT_LAST , FALSE TSRMLS_CC); efree(cmd_buf); if (FAIL == ret) { stmt->error_info = conn->error_info; } /* Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not sent response packets. According to documentation the only way to get an error is to have out-of-memory on the server-side. However, that's not true, as if max_allowed_packet_size is smaller than the chunk being sent to the server, the latter will complain with an error message. However, normally we don't expect an error message, thus we continue. When sending the next command, which expects response we will read the unexpected data and error message will look weird. Therefore we do non-blocking read to clean the line, if there is a need. Nevertheless, there is a built-in protection when sending a command packet, that checks if the line is clear - useful for debug purposes and to be switched off in release builds. Maybe we can make it automatic by checking what's the value of max_allowed_packet_size on the server and resending the data. */ #if HAVE_USLEEP && !defined(PHP_WIN32) usleep(120000); #endif if ((packet_len = php_mysqlnd_consume_uneaten_data(conn, cmd TSRMLS_CC))) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error " "while sending long data. Probably max_allowed_packet_size " "is smaller than the data. You have to increase it or send " "smaller chunks of data. Answer was %u bytes long.", packet_len); SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Server responded to COM_STMT_SEND_LONG_DATA."); ret = FAIL; } } return ret; } /* }}} */ /* {{{ _mysqlnd_stmt_bind_param */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind) { unsigned int i = 0; if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); return FAIL; } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->param_count) { if (!param_bind) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported"); return FAIL; } else if (stmt->param_bind) { /* There is already result bound. Forbid for now re-binding!! */ for (i = 0; i < stmt->param_count; i++) { /* For BLOBS zv is NULL */ if (stmt->param_bind[i].zv) { /* We may have the last reference, then call zval_ptr_dtor() or we may leak memory. */ zval_ptr_dtor(&stmt->param_bind[i].zv); stmt->param_bind[i].zv = NULL; } } efree(stmt->param_bind); } stmt->param_bind = param_bind; for (i = 0; i < stmt->param_count; i++) { /* The client will use stmt_send_long_data */ if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) { /* Prevent from freeing */ ZVAL_ADDREF(stmt->param_bind[i].zv); /* Don't update is_ref, or we will leak during conversion */ } else { stmt->param_bind[i].zv = NULL; stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } } stmt->send_types_to_server = 1; } return PASS; } /* }}} */ /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind) { uint i = 0; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); if (result_bind) { efree(result_bind); } return FAIL; } if (stmt->field_count) { if (!result_bind) { return FAIL; } mysqlnd_stmt_separate_result_bind(stmt); stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { /* Prevent from freeing */ ZVAL_ADDREF(stmt->result_bind[i].zv); /* Don't update is_ref !!! it's not our job Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt will fail. */ stmt->result_bind[i].bound = TRUE; } } else if (result_bind) { efree(result_bind); } return PASS; } /* }}} */ /* {{{ mysqlnd_stmt::insert_id */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt) { return stmt->upsert_status.last_insert_id; } /* }}} */ /* {{{ mysqlnd_stmt::affected_rows */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const stmt) { return stmt->upsert_status.affected_rows; } /* }}} */ /* {{{ mysqlnd_stmt::num_rows */ static mynd_ulonglong MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const stmt) { return stmt->result? mysqlnd_num_rows(stmt->result):0; } /* }}} */ /* {{{ mysqlnd_stmt::warning_count */ static unsigned int MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const stmt) { return stmt->upsert_status.warning_count; } /* }}} */ /* {{{ mysqlnd_stmt::field_count */ static unsigned int MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const stmt) { return stmt->field_count; } /* }}} */ /* {{{ mysqlnd_stmt::param_count */ static unsigned int MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const stmt) { return stmt->param_count; } /* }}} */ /* {{{ mysqlnd_stmt::errno */ static unsigned int MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const stmt) { return stmt->error_info.error_no; } /* }}} */ /* {{{ mysqlnd_stmt::error */ static const char * MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const stmt) { return stmt->error_info.error; } /* }}} */ /* {{{ mysqlnd_stmt::sqlstate */ static const char * MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt) { return stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; } /* }}} */ /* {{{ mysqlnd_stmt::data_seek */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, mynd_ulonglong row) { return stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL; } /* }}} */ /* {{{ mysqlnd_stmt::param_metadata */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt) { if (!stmt->param_count) { return NULL; } return NULL; } /* }}} */ /* {{{ mysqlnd_stmt::result_metadata */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt) { MYSQLND_RES *result; if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) { return NULL; } /* TODO: This implementation is kind of a hack, find a better way to do it. In different functions I have put fuses to check for result->m.fetch_row() being NULL. This should be handled in a better way. In the meantime we don't need a zval cache reference for this fake result set, so we don't get one. */ result = mysqlnd_result_init(stmt->field_count, NULL); result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); result->unbuf->eof_reached = TRUE; result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE); return result; } /* }}} */ /* {{{ mysqlnd_stmt::attr_set */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value) { unsigned long val = *(unsigned long *) value; switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: /* XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack and mysqlnd won't be used out of the scope of PHP -> use ulong. */ stmt->update_max_length = val? TRUE:FALSE; break; case STMT_ATTR_CURSOR_TYPE: { if (val > (unsigned long) CURSOR_TYPE_READ_ONLY) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } stmt->flags = val; break; } case STMT_ATTR_PREFETCH_ROWS: { if (val == 0) { val = MYSQLND_DEFAULT_PREFETCH_ROWS; } else if (val > 1) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } stmt->prefetch_rows = val; break; } default: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } return PASS; } /* }}} */ /* {{{ _mysqlnd_stmt_attr_get */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value) { switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: *(zend_bool *) value= stmt->update_max_length; break; case STMT_ATTR_CURSOR_TYPE: *(unsigned long *) value= stmt->flags; break; case STMT_ATTR_PREFETCH_ROWS: *(unsigned long *) value= stmt->prefetch_rows; break; default: return FAIL; } return PASS; } /* }}} */ /* {{{ mysqlnd_stmt::free_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) { if (!stmt->result) { return PASS; } if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = stmt->m->use_result; stmt->default_rset_handler(stmt TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Flush if anything is left and unbuffered set */ stmt->result->m.skip_result(stmt->result TSRMLS_CC); /* Separate the bound variables, which point to the result set, then destroy the set. */ mysqlnd_stmt_separate_result_bind(stmt); /* Now we can destroy the result set */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); } /* As the buffers have been freed, we should go back to PREPARED */ stmt->state = MYSQLND_STMT_PREPARED; /* Line is free! */ stmt->conn->state = CONN_READY; return PASS; } /* }}} */ /* {{{ mysqlnd_stmt_separate_result_bind */ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt) { int i; if (!stmt->result_bind) { return; } /* Because only the bound variables can point to our internal buffers, then separate or free only them. Free is possible because the user could have lost reference. */ for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free */ if (ZVAL_REFCOUNT(stmt->result_bind[i].zv) > 1) { #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_copy_ctor(stmt->result_bind[i].zv); #endif zval_ptr_dtor(&stmt->result_bind[i].zv); } else { /* If it is a string, what is pointed will be freed later in free_result(). We need to remove the variable to which the user has lost reference. */ #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF ZVAL_NULL(stmt->result_bind[i].zv); #endif zval_ptr_dtor(&stmt->result_bind[i].zv); } } } efree(stmt->result_bind); stmt->result_bind = NULL; } /* }}} */ /* {{{ mysqlnd_internal_free_stmt_content */ static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) { /* Destroy the input bind */ if (stmt->param_bind) { int i; /* Because only the bound variables can point to our internal buffers, then separate or free only them. Free is possible because the user could have lost reference. */ for (i = 0; i < stmt->param_count; i++) { /* For BLOBs zv is NULL */ if (stmt->param_bind[i].zv) { zval_ptr_dtor(&stmt->param_bind[i].zv); } } efree(stmt->param_bind); stmt->param_bind = NULL; } /* First separate the bound variables, which point to the result set, then destroy the set. */ mysqlnd_stmt_separate_result_bind(stmt); /* Not every statement has a result set attached */ if (stmt->result) { stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); stmt->result = NULL; } if (stmt->cmd_buffer.buffer) { efree(stmt->cmd_buffer.buffer); stmt->cmd_buffer.buffer = NULL; } if (stmt->conn) { stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); stmt->conn = NULL; } } /* }}} */ /* {{{ mysqlnd_stmt::close */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) { MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); /* If the user decided to close the statement right after execute() We have to call the appropriate use_result() or store_result() and clean. */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { stmt->default_rset_handler(stmt TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } /* unbuffered set not fetched to the end ? Clean the line */ if (stmt->result) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* After this point we are allowed to free the result set, as we have cleaned the line */ if (stmt->stmt_id) { MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: STAT_FREE_RESULT_EXPLICIT); int4store(cmd_buf, stmt->stmt_id); if (conn->state == CONN_READY && FAIL == mysqlnd_simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf), PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/, FALSE TSRMLS_CC)) { stmt->error_info = conn->error_info; return FAIL; } } mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); return PASS; } /* }}} */ /* {{{ mysqlnd_stmt::dtor */ static enum_func_status MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) { enum_func_status ret; MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: STAT_STMT_CLOSE_EXPLICIT); if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) { efree(stmt); } return ret; } /* }}} */ static struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { MYSQLND_METHOD(mysqlnd_stmt, prepare), MYSQLND_METHOD(mysqlnd_stmt, execute), MYSQLND_METHOD(mysqlnd_stmt, use_result), MYSQLND_METHOD(mysqlnd_stmt, store_result), MYSQLND_METHOD(mysqlnd_stmt, get_result), MYSQLND_METHOD(mysqlnd_stmt, free_result), MYSQLND_METHOD(mysqlnd_stmt, data_seek), MYSQLND_METHOD(mysqlnd_stmt, reset), MYSQLND_METHOD(mysqlnd_stmt, close), MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor), MYSQLND_METHOD(mysqlnd_stmt, fetch), MYSQLND_METHOD(mysqlnd_stmt, bind_param), MYSQLND_METHOD(mysqlnd_stmt, bind_result), MYSQLND_METHOD(mysqlnd_stmt, send_long_data), MYSQLND_METHOD(mysqlnd_stmt, param_metadata), MYSQLND_METHOD(mysqlnd_stmt, result_metadata), MYSQLND_METHOD(mysqlnd_stmt, insert_id), MYSQLND_METHOD(mysqlnd_stmt, affected_rows), MYSQLND_METHOD(mysqlnd_stmt, num_rows), MYSQLND_METHOD(mysqlnd_stmt, param_count), MYSQLND_METHOD(mysqlnd_stmt, field_count), MYSQLND_METHOD(mysqlnd_stmt, warning_count), MYSQLND_METHOD(mysqlnd_stmt, errno), MYSQLND_METHOD(mysqlnd_stmt, error), MYSQLND_METHOD(mysqlnd_stmt, sqlstate), MYSQLND_METHOD(mysqlnd_stmt, attr_get), MYSQLND_METHOD(mysqlnd_stmt, attr_set), }; /* {{{ _mysqlnd_stmt_init */ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn) { MYSQLND_STMT *stmt = ecalloc(1, sizeof(MYSQLND_STMT)); stmt->m = &mysqlnd_stmt_methods; stmt->state = MYSQLND_STMT_INITTED; stmt->cmd_buffer.length = 4096; stmt->cmd_buffer.buffer = emalloc(stmt->cmd_buffer.length); stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; /* Mark that we reference the connection, thus it won't be be destructed till there is open statements. The last statement or normal query result will close it then. */ stmt->conn = conn->m->get_reference(conn); return stmt; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_ps_codec.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_ps_codec.c +++ php-src/ext/mysqlnd/mysqlnd_ps_codec.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_ps_codec.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" #define MYSQLND_SILENT typedef int8 my_int8; typedef uint8 my_uint8; typedef int16 my_int16; typedef uint16 my_uint16; typedef int32 my_int32; typedef uint32 my_uint32; enum mysqlnd_timestamp_type { MYSQLND_TIMESTAMP_NONE= -2, MYSQLND_TIMESTAMP_ERROR= -1, MYSQLND_TIMESTAMP_DATE= 0, MYSQLND_TIMESTAMP_DATETIME= 1, MYSQLND_TIMESTAMP_TIME= 2 }; struct st_mysqlnd_time { unsigned int year, month, day, hour, minute, second; unsigned long second_part; zend_bool neg; enum mysqlnd_timestamp_type time_type; }; struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; #define MYSQLND_PS_SKIP_RESULT_W_LEN -1 #define MYSQLND_PS_SKIP_RESULT_STR -2 /* {{{ ps_fetch_from_1_to_8_bytes */ void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode, unsigned int byte_count, zend_bool is_bit TSRMLS_DC) { char tmp[22]; size_t tmp_len = 0; if (field->flags & UNSIGNED_FLAG) { my_uint64 uval = 0; switch (byte_count) { case 8:uval = is_bit? (my_uint64) bit_uint8korr(*row):(my_uint64) uint8korr(*row);break; case 7:uval = bit_uint7korr(*row);break; case 6:uval = bit_uint6korr(*row);break; case 5:uval = bit_uint5korr(*row);break; case 4:uval = is_bit? (my_uint64) bit_uint4korr(*row):(my_uint64) uint4korr(*row);break; case 3:uval = is_bit? (my_uint64) bit_uint3korr(*row):(my_uint64) uint3korr(*row);break; case 2:uval = is_bit? (my_uint64) bit_uint2korr(*row):(my_uint64) uint2korr(*row);break; case 1:uval = (my_uint64) uint1korr(*row);break; } #if SIZEOF_LONG==4 if (uval > INT_MAX) { tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval); } else #endif /* #if SIZEOF_LONG==4 */ { if (byte_count < 8 || uval <= L64(9223372036854775807)) { ZVAL_LONG(zv, uval); } else { tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval); } } } else { /* SIGNED */ my_int64 lval = 0; switch (byte_count) { case 8:lval = (my_int64) sint8korr(*row);break; /* 7, 6 and 5 are not possible. BIT is only unsigned, thus only uint5|6|7 macroses exist */ case 4:lval = (my_int64) sint4korr(*row);break; case 3:lval = (my_int64) sint3korr(*row);break; case 2:lval = (my_int64) sint2korr(*row);break; case 1:lval = (my_int64) *(my_int8*)*row;break; } #if SIZEOF_LONG==4 if ((L64(2147483647) < (my_int64) lval) || (L64(-2147483648) > (my_int64) lval)) { tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval); } else #endif /* SIZEOF */ { ZVAL_LONG(zv, lval); } } if (tmp_len) { #if PHP_MAJOR_VERSION >= 6 if (as_unicode) { ZVAL_UTF8_STRINGL(zv, tmp, tmp_len, ZSTR_DUPLICATE); } else #endif { ZVAL_STRINGL(zv, tmp, tmp_len, 1); } } (*row)+= byte_count; } /* }}} */ /* {{{ ps_fetch_null */ static void ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { ZVAL_NULL(zv); } /* }}} */ /* {{{ ps_fetch_int8 */ static void ps_fetch_int8(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 1, FALSE TSRMLS_CC); #if 0 if (field->flags & UNSIGNED_FLAG) { ZVAL_LONG(zv, *(my_uint8*)*row); } else { ZVAL_LONG(zv, *(my_int8*)*row); } (*row)++; #endif } /* }}} */ /* {{{ ps_fetch_int16 */ static void ps_fetch_int16(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 2, FALSE TSRMLS_CC); #if 0 if (field->flags & UNSIGNED_FLAG) { ZVAL_LONG(zv, (my_uint16) sint2korr(*row)); } else { ZVAL_LONG(zv, (my_int16) sint2korr(*row)); } (*row)+= 2; #endif } /* }}} */ /* {{{ ps_fetch_int32 */ static void ps_fetch_int32(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 4, FALSE TSRMLS_CC); #if 0 if (field->flags & UNSIGNED_FLAG) { my_uint32 uval; /* unsigned int (11) */ uval= (my_uint32) sint4korr(*row); #if SIZEOF_LONG==4 if (uval > INT_MAX) { char *tmp, *p; int j=10; tmp= emalloc(11); p= &tmp[9]; do { *p-- = (uval % 10) + 48; uval = uval / 10; } while (--j > 0); tmp[10]= '\0'; /* unsigned int > INT_MAX is 10 digits - ALWAYS */ #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRING(zv, tmp, 0); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, tmp, 10, ZSTR_AUTOFREE); } #endif /* PHP_MAJOR_VERSION >= 6 */ } else #endif /* #if SIZEOF_LONG==4 */ { ZVAL_LONG(zv, uval); } } else { ZVAL_LONG(zv, (my_int32) sint4korr(*row)); } (*row)+= 4; #endif /* 0 */ } /* }}} */ /* {{{ ps_fetch_int64 */ static void ps_fetch_int64(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 8, FALSE TSRMLS_CC); #if 0 my_uint64 llval = (my_uint64) sint8korr(*row); zend_bool uns = field->flags & UNSIGNED_FLAG? TRUE:FALSE; #if SIZEOF_LONG==8 if (uns == TRUE && llval > 9223372036854775807L) { #elif SIZEOF_LONG==4 if ((uns == TRUE && llval > L64(2147483647)) || (uns == FALSE && ((L64( 2147483647) < (my_int64) llval) || (L64(-2147483648) > (my_int64) llval)))) { #endif char tmp[22]; /* even though lval is declared as unsigned, the value * may be negative. Therefor we cannot use MYSQLND_LLU_SPEC and must * use MYSQLND_LL_SPEC. */ sprintf((char *)&tmp, uns == TRUE? MYSQLND_LLU_SPEC : MYSQLND_LL_SPEC, llval); #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRING(zv, tmp, 1); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRING(zv, tmp, ZSTR_DUPLICATE); } #endif } else { /* This cast is safe, as we have checked the values above */ ZVAL_LONG(zv, (long) llval); } (*row)+= 8; #endif /* 0 */ } /* }}} */ /* {{{ ps_fetch_float */ static void ps_fetch_float(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { float value; float4get(value, *row); ZVAL_DOUBLE(zv, value); (*row)+= 4; } /* }}} */ /* {{{ ps_fetch_double */ static void ps_fetch_double(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { double value; float8get(value, *row); ZVAL_DOUBLE(zv, value); (*row)+= 8; } /* }}} */ /* {{{ ps_fetch_time */ static void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { struct st_mysqlnd_time t; unsigned int length; /* First byte encodes the length*/ char *to; if ((length = php_mysqlnd_net_field_length(row))) { zend_uchar *to= *row; t.time_type = MYSQLND_TIMESTAMP_TIME; t.neg = (zend_bool) to[0]; t.day = (unsigned long) sint4korr(to+1); t.hour = (unsigned int) to[5]; t.minute = (unsigned int) to[6]; t.second = (unsigned int) to[7]; t.second_part = (length > 8) ? (unsigned long) sint4korr(to+8) : 0; t.year = t.month= 0; if (t.day) { /* Convert days to hours at once */ t.hour += t.day*24; t.day = 0; } (*row) += length; } else { memset(&t, 0, sizeof(t)); t.time_type = MYSQLND_TIMESTAMP_TIME; } /* QQ : How to make this unicode without copying two times the buffer - Unicode equivalent of spprintf? */ length = spprintf(&to, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second); #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif } /* }}} */ /* {{{ ps_fetch_date */ static void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { struct st_mysqlnd_time t = {0}; unsigned int length; /* First byte encodes the length*/ char *to; if ((length = php_mysqlnd_net_field_length(row))) { zend_uchar *to= *row; t.time_type= MYSQLND_TIMESTAMP_DATE; t.neg= 0; t.second_part = t.hour = t.minute = t.second = 0; t.year = (unsigned int) sint2korr(to); t.month = (unsigned int) to[2]; t.day = (unsigned int) to[3]; (*row)+= length; } else { memset(&t, 0, sizeof(t)); t.time_type = MYSQLND_TIMESTAMP_DATE; } /* QQ : How to make this unicode without copying two times the buffer - Unicode equivalent of spprintf? */ length = spprintf(&to, 0, "%04u-%02u-%02u", t.year, t.month, t.day); #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif } /* }}} */ /* {{{ ps_fetch_datetime */ static void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { struct st_mysqlnd_time t; unsigned int length; /* First byte encodes the length*/ char *to; if ((length = php_mysqlnd_net_field_length(row))) { zend_uchar *to= *row; t.time_type = MYSQLND_TIMESTAMP_DATETIME; t.neg = 0; t.year = (unsigned int) sint2korr(to); t.month = (unsigned int) to[2]; t.day = (unsigned int) to[3]; if (length > 4) { t.hour = (unsigned int) to[4]; t.minute = (unsigned int) to[5]; t.second = (unsigned int) to[6]; } else { t.hour = t.minute = t.second= 0; } t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0; (*row)+= length; } else { memset(&t, 0, sizeof(t)); t.time_type = MYSQLND_TIMESTAMP_DATETIME; } /* QQ : How to make this unicode without copying two times the buffer - Unicode equivalent of spprintf? */ length = spprintf(&to, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second); #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif } /* }}} */ /* {{{ ps_fetch_string */ static void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { /* For now just copy, before we make it possible to write \0 to the row buffer */ unsigned long length= php_mysqlnd_net_field_length(row); #if PHP_MAJOR_VERSION < 6 ZVAL_STRINGL(zv, (char *)*row, length, 1); #else if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) { ZVAL_STRINGL(zv, (char *)*row, length, 1); } else { ZVAL_UTF8_STRINGL(zv, (char*)*row, length, ZSTR_DUPLICATE); } #endif (*row) += length; } /* }}} */ /* {{{ ps_fetch_bit */ static void ps_fetch_bit(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode TSRMLS_DC) { unsigned long length= php_mysqlnd_net_field_length(row); ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, length, TRUE TSRMLS_CC); } /* }}} */ /* {{{ _mysqlnd_init_ps_subsystem */ void _mysqlnd_init_ps_subsystem() { memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions)); mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func = ps_fetch_null; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type = IS_DOUBLE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_time; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_date; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_date; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type= IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bit; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = 8; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type = IS_LONG; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING; } /* }}} */ /* {{{ mysqlnd_stmt_execute_store_params */ void mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, size_t *buf_len, unsigned int null_byte_offset) { unsigned int i = 0; unsigned left = (*buf_len - (*p - *buf)); unsigned int data_size = 0; /* 1. Store type information */ if (stmt->send_types_to_server) { /* 2 bytes per type, and leave 20 bytes for future use */ if (left < ((stmt->param_count * 2) + 20)) { unsigned int offset = *p - *buf; zend_uchar *tmp_buf; *buf_len = offset + stmt->param_count * 2 + 20; tmp_buf = emalloc(*buf_len); memcpy(tmp_buf, *buf, offset); *buf = tmp_buf; /* Update our pos pointer */ *p = *buf + offset; } for (i = 0; i < stmt->param_count; i++) { /* our types are not unsigned */ #if SIZEOF_LONG==8 if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) { stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG; } #endif int2store(*p, stmt->param_bind[i].type); *p+= 2; } } /* 2. Store data */ /* 2.1 Calculate how much space we need */ for (i = 0; i < stmt->param_count; i++) { if (stmt->param_bind[i].zv && Z_TYPE_P(stmt->param_bind[i].zv) == IS_NULL) { continue; } switch (stmt->param_bind[i].type) { case MYSQL_TYPE_DOUBLE: data_size += 8; break; #if SIZEOF_LONG==8 case MYSQL_TYPE_LONGLONG: data_size += 8; break; #elif SIZEOF_LONG==4 case MYSQL_TYPE_LONG: data_size += 4; break; #else #error "Should not happen" #endif case MYSQL_TYPE_LONG_BLOB: if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) { /* User hasn't sent anything, we will send empty string. Empty string has length of 0, encoded in 1 byte. No real data will follow after it. */ data_size++; } break; case MYSQL_TYPE_VAR_STRING: data_size += 8; /* max 8 bytes for size */ convert_to_string_ex(&stmt->param_bind[i].zv); data_size += Z_STRLEN_P(stmt->param_bind[i].zv); break; } } /* 2.2 Enlarge the buffer, if needed */ left = (*buf_len - (*p - *buf)); if (left < data_size) { unsigned int offset = *p - *buf; zend_uchar *tmp_buf; *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */ tmp_buf = emalloc(*buf_len); memcpy(tmp_buf, *buf, offset); *buf = tmp_buf; /* Update our pos pointer */ *p = *buf + offset; } /* 2.3 Store the actual data */ for (i = 0; i < stmt->param_count; i++) { zval *data = stmt->param_bind[i].zv; /* Handle long data */ if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) { (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7)); } else { switch (stmt->param_bind[i].type) { case MYSQL_TYPE_DOUBLE: convert_to_double_ex(&data); float8store(*p, Z_DVAL_P(data)); (*p) += 8; break; #if SIZEOF_LONG==8 case MYSQL_TYPE_LONGLONG: convert_to_long_ex(&data); int8store(*p, Z_LVAL_P(data)); (*p) += 8; break; #elif SIZEOF_LONG==4 case MYSQL_TYPE_LONG: convert_to_long_ex(&data); int4store(*p, Z_LVAL_P(data)); (*p) += 4; break; #else #error "Should not happen" #endif case MYSQL_TYPE_LONG_BLOB: if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) { stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } else { /* send_long_data() not called, send empty string */ *p = php_mysqlnd_net_store_length(*p, 0); } break; case MYSQL_TYPE_VAR_STRING: /* If the user uses refs, it could be that the type has has changed and we need to convert, again. Which is noop, if the type hasn't changed. */ convert_to_string_ex(&stmt->param_bind[i].zv); { unsigned int len = Z_STRLEN_P(data); /* to is after p. The latter hasn't been moved */ *p = php_mysqlnd_net_store_length(*p, len); memcpy(*p, Z_STRVAL_P(data), len); (*p) += len; } break; default: /* Won't happen, but set to NULL */ (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7)); break; } } } } /* }}} */ /* {{{ mysqlnd_stmt_execute_generate_request */ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, zend_bool *free_buffer) { zend_uchar *p = stmt->cmd_buffer.buffer, *cmd_buffer = stmt->cmd_buffer.buffer; size_t cmd_buffer_length = stmt->cmd_buffer.length; unsigned int null_byte_offset, null_count= (stmt->param_count + 7) / 8; int4store(p, stmt->stmt_id); p += 4; /* flags is 4 bytes, we store just 1 */ int1store(p, (zend_uchar) stmt->flags); p++; /* Make it all zero */ int4store(p, 0); int1store(p, 1); /* and send 1 for iteration count */ p+= 4; null_byte_offset = p - cmd_buffer; memset(p, 0, null_count); p += null_count; int1store(p, stmt->send_types_to_server); p++; mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset); *free_buffer = (cmd_buffer != stmt->cmd_buffer.buffer); *request_len = (p - cmd_buffer); return cmd_buffer; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_qcache.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_qcache.c +++ php-src/ext/mysqlnd/mysqlnd_qcache.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_qcache.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #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 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_result.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_result.c +++ php-src/ext/mysqlnd/mysqlnd_result.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_result.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" #include "mysqlnd_result.h" #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" #include "mysqlnd_charset.h" #include "ext/standard/basic_functions.h" #define MYSQLND_SILENT /* {{{ mysqlnd_unbuffered_free_last_data */ void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC) { MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf; if (!unbuf) { return; } if (unbuf->last_row_data) { unsigned int i, ctor_called_count = 0; zend_bool copy_ctor_called; MYSQLND_STATS *global_stats = result->conn? &result->conn->stats:NULL; for (i = 0; i < result->field_count; i++) { mysqlnd_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->zval_cache, result->type, &copy_ctor_called TSRMLS_CC); if (copy_ctor_called) { ctor_called_count++; } } /* By using value3 macros we hold a mutex only once, there is no value2 */ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(global_stats, STAT_COPY_ON_WRITE_PERFORMED, ctor_called_count, STAT_COPY_ON_WRITE_SAVED, result->field_count - ctor_called_count, STAT_COPY_ON_WRITE_PERFORMED, 0); /* Free last row's zvals */ efree(unbuf->last_row_data); unbuf->last_row_data = NULL; } if (unbuf->last_row_buffer) { /* Nothing points to this buffer now, free it */ efree(unbuf->last_row_buffer); unbuf->last_row_buffer = NULL; } } /* }}} */ /* {{{ mysqlnd_free_buffered_data */ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) { MYSQLND_THD_ZVAL_PCACHE *zval_cache = result->zval_cache; MYSQLND_RES_BUFFERED *set = result->data; unsigned int field_count = result->field_count; unsigned int row; for (row = 0; row < result->data->row_count; row++) { unsigned int col; zval **current_row = current_row = set->data[row]; zend_uchar *current_buffer = set->row_buffers[row]; for (col = 0; col < field_count; col++) { zend_bool copy_ctor_called; mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache, result->type, &copy_ctor_called TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(NULL, copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: STAT_COPY_ON_WRITE_SAVED); } pefree(current_row, set->persistent); pefree(current_buffer, set->persistent); } pefree(set->data, set->persistent); pefree(set->row_buffers, set->persistent); set->data = NULL; set->row_buffers = NULL; set->data_cursor = NULL; set->row_count = 0; if (set->qcache) { mysqlnd_qcache_free_cache_reference(&set->qcache); } pefree(set, set->persistent); } /* }}} */ /* {{{ mysqlnd_res::free_result_buffers */ void MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) { if (result->unbuf) { mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); efree(result->unbuf); result->unbuf = NULL; } else if (result->data) { mysqlnd_free_buffered_data(result TSRMLS_CC); result->data = NULL; } if (result->lengths) { efree(result->lengths); result->lengths = NULL; } } /* }}} */ /* {{{ mysqlnd_internal_free_result_contents */ static void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) { result->m.free_result_buffers(result TSRMLS_CC); if (result->row_packet) { PACKET_FREE(result->row_packet); result->row_packet = NULL; } result->conn = NULL; if (result->meta) { result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); result->meta = NULL; } if (result->zval_cache) { mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache); result->zval_cache = NULL; } } /* }}} */ /* {{{ mysqlnd_internal_free_result */ static void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) { /* result->conn is an address if this is an unbuffered query. In this case, decrement the reference counter in the connection object and if needed free the latter. */ if (result->conn) { result->conn->m->free_reference(result->conn TSRMLS_CC); result->conn = NULL; } result->m.free_result_contents(result TSRMLS_CC); efree(result); } /* }}} */ /* {{{ mysqlnd_res::read_result_metadata */ static enum_func_status MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC) { /* Make it safe to call it repeatedly for PS - better free and allocate a new because the number of field might change (select *) with altered table. Also for statements which skip the PS infrastructure! */ if (result->meta) { result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); result->meta = NULL; } result->meta = mysqlnd_result_meta_init(result->field_count); /* 1. Read all fields metadata */ /* It's safe to reread without freeing */ if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) { result->m.free_result_contents(result TSRMLS_CC); return FAIL; } /* 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata() should consume. 3. If there is a result set, it follows. The last packet will have 'eof' set If PS, then no result set follows. */ return PASS; } /* }}} */ /* {{{ mysqlnd_query_read_result_set_header */ enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC) { enum_func_status ret; php_mysql_packet_rset_header rset_header; ret = FAIL; PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET); do { if (FAIL == (ret = PACKET_READ_ALLOCA(rset_header, conn))) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header"); break; } if (rset_header.error_info.error_no) { /* Cover a protocol design error: error packet does not contain the server status. Therefore, the client has no way to find out whether there are more result sets of a multiple-result-set statement pending. Luckily, in 5.0 an error always aborts execution of a statement, wherever it is a multi-statement or a stored procedure, so it should be safe to unconditionally turn off the flag here. */ conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS; conn->upsert_status.affected_rows = -1; /* This will copy the error code and the messages, as they are buffers in the struct */ conn->error_info = rset_header.error_info; ret = FAIL; break; } conn->error_info.error_no = 0; switch (rset_header.field_count) { case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */ zend_bool is_warning; conn->last_query_type = QUERY_LOAD_LOCAL; conn->state = CONN_SENDING_LOAD_DATA; ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file, &is_warning TSRMLS_CC); conn->state = (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT; MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY); break; } case 0: /* UPSERT */ conn->last_query_type = QUERY_UPSERT; conn->field_count = rset_header.field_count; conn->upsert_status.warning_count = rset_header.warning_count; conn->upsert_status.server_status = rset_header.server_status; conn->upsert_status.affected_rows = rset_header.affected_rows; conn->upsert_status.last_insert_id = rset_header.last_insert_id; SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, rset_header.info_or_local_file, rset_header.info_or_local_file_len); /* Result set can follow UPSERT statement, check server_status */ if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { conn->state = CONN_NEXT_RESULT_PENDING; } else { conn->state = CONN_READY; } ret = PASS; MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY); break; default:{ /* Result set */ php_mysql_packet_eof fields_eof; MYSQLND_RES *result; uint stat = -1; SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_RSET_QUERY); memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); conn->last_query_type = QUERY_SELECT; conn->state = CONN_FETCHING_DATA; /* PS has already allocated it */ if (!stmt) { conn->field_count = rset_header.field_count; result = conn->current_result= mysqlnd_result_init(rset_header.field_count, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); } else { if (!stmt->result) { /* This is 'SHOW'/'EXPLAIN'-like query. Current implementation of prepared statements can't send result set metadata for these queries on prepare stage. Read it now. */ conn->field_count = rset_header.field_count; result = stmt->result = mysqlnd_result_init(rset_header.field_count, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); } else { /* Update result set metadata if it for some reason changed between prepare and execute, i.e.: - in case of 'SELECT ?' we don't know column type unless data was supplied to mysql_stmt_execute, so updated column type is sent now. - if data dictionary changed between prepare and execute, for example a table used in the query was altered. Note, that now (4.1.3) we always send metadata in reply to COM_STMT_EXECUTE (even if it is not necessary), so either this or previous branch always works. */ } result = stmt->result; } if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) { /* For PS, we leave them in Prepared state */ if (!stmt) { efree(conn->current_result); conn->current_result = NULL; } break; } /* Check for SERVER_STATUS_MORE_RESULTS if needed */ PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) { result->m.free_result_contents(result TSRMLS_CC); efree(result); if (!stmt) { conn->current_result = NULL; } else { stmt->result = NULL; memset(stmt, 0, sizeof(MYSQLND_STMT)); stmt->state = MYSQLND_STMT_INITTED; } } else { conn->upsert_status.warning_count = fields_eof.warning_count; conn->upsert_status.server_status = fields_eof.server_status; if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) { stat = STAT_BAD_INDEX_USED; } else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) { stat = STAT_NO_INDEX_USED; } if (stat != -1) { MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); } } PACKET_FREE_ALLOCA(fields_eof); break; } } } while (0); PACKET_FREE_ALLOCA(rset_header); return ret; } /* }}} */ /* {{{ mysqlnd_fetch_lengths_buffered */ /* Do lazy initialization for buffered results. As PHP strings have length inside, this function makes not much sense in the context of PHP, to be called as separate function. But let's have it for completeness. */ static unsigned long * mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result) { int i; zval **previous_row; /* If: - unbuffered result - first row has not been read - last_row has been read */ if (result->data->data_cursor == NULL || result->data->data_cursor == result->data->data || ((result->data->data_cursor - result->data->data) > result->data->row_count)) { return NULL;/* No rows or no more rows */ } previous_row = *(result->data->data_cursor - 1); for (i = 0; i < result->field_count; i++) { result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]); } return result->lengths; } /* }}} */ /* {{{ mysqlnd_fetch_lengths_unbuffered */ static unsigned long * mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result) { return result->lengths; } /* }}} */ /* {{{ mysqlnd_res::fetch_lengths */ PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result) { return result->m.fetch_lengths? result->m.fetch_lengths(result):NULL; } /* }}} */ /* {{{ mysqlnd_fetch_row_unbuffered */ static enum_func_status mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; zval *row = (zval *) param; unsigned int i, field_count = result->field_count; php_mysql_packet_row *row_packet = result->row_packet; unsigned long *lengths = result->lengths; if (result->unbuf->eof_reached) { /* No more rows obviously */ *fetched_anything = FALSE; return PASS; } if (result->conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return FAIL; } /* Let the row packet fill our buffer and skip additional malloc + memcpy */ row_packet->skip_extraction = row? FALSE:TRUE; /* If we skip rows (row == NULL) we have to mysqlnd_unbuffered_free_last_data() before it. The function returns always true. */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { result->unbuf->row_count++; *fetched_anything = TRUE; mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; row_packet->row_buffer = NULL; MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT); if (!row_packet->skip_extraction) { HashTable *row_ht = Z_ARRVAL_P(row); for (i = 0; i < field_count; i++) { zval *data = result->unbuf->last_row_data[i]; int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); MYSQLND_RES_METADATA *meta = result->meta; if (lengths) { lengths[i] = len; } /* Forbid ZE to free it, we will clean it */ ZVAL_ADDREF(data); if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) { ZVAL_ADDREF(data); } if (flags & MYSQLND_FETCH_NUM) { zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ /* QQ: Error handling ? */ /* zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether the index is a numeric and convert it to it. This however means constant hashing of the column name, which is not needed as it can be precomputed. */ if (meta->zend_hash_keys[i].is_numeric == FALSE) { #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, meta->zend_hash_keys[i].ustr, meta->zend_hash_keys[i].ulen + 1, meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } else #endif { zend_hash_quick_update(Z_ARRVAL_P(row), meta->fields[i].name, meta->fields[i].name_length + 1, meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } else { zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } if (meta->fields[i].max_length < len) { meta->fields[i].max_length = len; } } } } else if (ret == FAIL) { if (row_packet->error_info.error_no) { result->conn->error_info = row_packet->error_info; } *fetched_anything = FALSE; result->conn->state = CONN_READY; result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; result->conn->upsert_status.warning_count = row_packet->warning_count; result->conn->upsert_status.server_status = row_packet->server_status; /* result->row_packet will be cleaned when destroying the result object */ if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { result->conn->state = CONN_NEXT_RESULT_PENDING; } else { result->conn->state = CONN_READY; } mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); *fetched_anything = FALSE; } return PASS; } /* }}} */ /* {{{ mysqlnd_res::use_result */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC) { result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); /* Will be freed in the mysqlnd_internal_free_result_contents() called by the resource destructor. mysqlnd_fetch_row_unbuffered() expects this to be not NULL. */ PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *); result->row_packet->field_count = result->field_count; result->row_packet->binary_protocol = FALSE; result->row_packet->fields_metadata = result->meta->fields; result->row_packet->bit_fields_count = result->meta->bit_fields_count; result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); /* No multithreading issues as we don't share the connection :) */ return result; } /* }}} */ /* {{{ mysqlnd_fetch_row_buffered */ static enum_func_status mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { unsigned int i; zval *row = (zval *) param; /* If we haven't read everything */ if (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) { zval **current_row = *result->data->data_cursor; for (i = 0; i < result->field_count; i++) { zval *data = current_row[i]; /* Let us later know what to do with this zval. If ref_count > 1, we will just decrease it, otherwise free it. zval_ptr_dtor() make this very easy job. */ ZVAL_ADDREF(data); if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) { ZVAL_ADDREF(data); } if (flags & MYSQLND_FETCH_NUM) { zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ /* QQ: Error handling ? */ /* zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether the index is a numeric and convert it to it. This however means constant hashing of the column name, which is not needed as it can be precomputed. */ if (result->meta->zend_hash_keys[i].is_numeric == FALSE) { #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, result->meta->zend_hash_keys[i].ustr, result->meta->zend_hash_keys[i].ulen + 1, result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } else #endif { zend_hash_quick_update(Z_ARRVAL_P(row), result->meta->fields[i].name, result->meta->fields[i].name_length + 1, result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } else { zend_hash_index_update(Z_ARRVAL_P(row), result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } } result->data->data_cursor++; *fetched_anything = TRUE; } else { result->data->data_cursor = NULL; *fetched_anything = FALSE; #ifndef MYSQLND_SILENT php_printf("NO MORE DATA\n "); #endif } return PASS; } /* }}} */ #define STORE_RESULT_PREALLOCATED_SET 32 /* {{{ mysqlnd_store_result_fetch_data */ enum_func_status mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool update_max_length, zend_bool to_cache TSRMLS_DC) { enum_func_status ret; php_mysql_packet_row row_packet; unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows; MYSQLND_RES_BUFFERED *set; free_rows = next_extend; result->data = set = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); set->data = pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache); set->row_buffers= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *), to_cache); set->persistent = to_cache; set->qcache = to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL; set->references = 1; PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET); row_packet.field_count = meta->field_count; row_packet.binary_protocol = binary_protocol; row_packet.fields_metadata = meta->fields; row_packet.bit_fields_count = meta->bit_fields_count; row_packet.bit_fields_total_len = meta->bit_fields_total_len; /* Let the row packet fill our buffer and skip additional malloc + memcpy */ while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) { int i; zval **current_row; if (!free_rows) { mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */ total_rows += set->row_count; set->data = perealloc(set->data, total_rows * sizeof(zval **), set->persistent); set->row_buffers = perealloc(set->row_buffers, total_rows * sizeof(zend_uchar *), set->persistent); } free_rows--; current_row = set->data[set->row_count] = row_packet.fields; set->row_buffers[set->row_count] = row_packet.row_buffer; set->row_count++; /* So row_packet's destructor function won't efree() it */ row_packet.fields = NULL; row_packet.row_buffer = NULL; if (update_max_length == TRUE) { for (i = 0; i < row_packet.field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. Thus for NULL and zero-length we are quite efficient. */ if (Z_TYPE_P(current_row[i]) >= IS_STRING) { unsigned long len = Z_STRLEN_P(current_row[i]); if (meta->fields[i].max_length < len) { meta->fields[i].max_length = len; } } } } /* No need to FREE_ALLOCA as we can reuse the 'lengths' and 'fields' arrays. For lengths its absolutely safe. 'fields' is reused because the ownership of the strings has been transfered above. */ } MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT, set->row_count); /* Finally clean */ if (row_packet.eof) { conn->upsert_status.warning_count = row_packet.warning_count; conn->upsert_status.server_status = row_packet.server_status; } /* save some memory */ if (free_rows) { set->data = perealloc(set->data, (size_t) set->row_count * sizeof(zval **), set->persistent); set->row_buffers = perealloc(set->row_buffers, (size_t) set->row_count * sizeof(zend_uchar *), set->persistent); } if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { conn->state = CONN_NEXT_RESULT_PENDING; } else { conn->state = CONN_READY; } if (ret == FAIL) { set->error_info = row_packet.error_info; } else { /* Position at the first row */ set->data_cursor = set->data; /* libmysql's documentation says it should be so for SELECT statements */ conn->upsert_status.affected_rows = result->data->row_count; } PACKET_FREE_ALLOCA(row_packet); return ret; } /* }}} */ /* {{{ mysqlnd_res::store_result */ MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps_protocol TSRMLS_DC) { enum_func_status ret; zend_bool to_cache = FALSE; result->conn = NULL; /* store result does not reference the connection */ result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; conn->state = CONN_FETCHING_DATA; result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); ret = mysqlnd_store_result_fetch_data(conn, result, result->meta, ps_protocol, TRUE, to_cache TSRMLS_CC); if (PASS == ret) { /* libmysql's documentation says it should be so for SELECT statements */ conn->upsert_status.affected_rows = result->data->row_count; } else { conn->error_info = result->data->error_info; result->m.free_result_internal(result TSRMLS_CC); result = NULL; } return result; } /* }}} */ /* {{{ mysqlnd_res::skip_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) { zend_bool fetched_anything; /* Unbuffered sets A PS could be prepared - there is metadata and thus a stmt->result but the fetch_row function isn't actually set (NULL), thus we have to skip these. */ if (!result->data && result->conn && result->unbuf && !result->unbuf->eof_reached && result->m.fetch_row) { /* We have to fetch all data to clean the line */ MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS: STAT_FLUSHED_PS_SETS); while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && fetched_anything == TRUE) { /* do nothing */; } } return PASS; } /* }}} */ /* {{{ mysqlnd_res::free_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC) { result->m.skip_result(result TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL, implicit == TRUE? STAT_FREE_RESULT_EXPLICIT: STAT_FREE_RESULT_IMPLICIT); result->m.free_result_internal(result TSRMLS_CC); return PASS; } /* }}} */ /* {{{ _mysqlnd_data_seek */ static enum_func_status MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong row) { if (!result->data) { return FAIL; } /* libmysql just moves to the end, it does traversing of a linked list */ if (row >= result->data->row_count) { result->data->data_cursor = NULL; } else { result->data->data_cursor = result->data->data + row; } return PASS; } /* }}} */ /* {{{ mysqlnd_res::num_fields */ mynd_ulonglong MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res) { /* Be compatible with libmysql. We count row_count, but will return 0 */ return res->data? res->data->row_count:0; } /* }}} */ /* {{{ mysqlnd_res::num_fields */ unsigned int MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res) { return res->field_count; } /* }}} */ /* {{{ mysqlnd_res::fetch_field */ static MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result) { return result->meta? result->meta->m->fetch_field(result->meta):NULL; } /* }}} */ /* {{{ mysqlnd_res::fetch_field_direct */ static MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr) { return result->meta? &result->meta->fields[fieldnr]:NULL; } /* }}} */ /* {{{ mysqlnd_res::field_seek */ static MYSQLND_FIELD_OFFSET MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset) { MYSQLND_FIELD_OFFSET return_value = 0; if (result->meta) { return_value = result->meta->current_field; result->meta->current_field = field_offset; } return return_value; } /* }}} */ /* {{{ mysqlnd_res::field_tell */ static MYSQLND_FIELD_OFFSET MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result) { return result->meta? result->meta->m->field_tell(result->meta):0; } /* }}} */ /* {{{ mysqlnd_res::fetch_into */ static void MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC) { zend_bool fetched_anything; if (!result->m.fetch_row) { RETURN_NULL(); } /* Hint Zend how many elements we will have in the hash. Thus it won't extend and rehash the hash constantly. */ mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2); if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row"); RETURN_FALSE; } else if (fetched_anything == FALSE) { zval_dtor(return_value); switch (extension) { case MYSQLND_MYSQLI: RETURN_NULL(); break; case MYSQLND_MYSQL: RETURN_FALSE; break; default:exit(0); } } /* return_value is IS_NULL for no more data and an array for data. Thus it's ok to return here. */ } /* }}} */ /* {{{ mysqlnd_res::fetch_all */ static void MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { zval *row; ulong i = 0; /* mysqlnd_res::fetch_all works with buffered resultsets only */ if (result->conn || !result->data || !result->data->row_count || !result->data->data_cursor || result->data->data_cursor >= result->data->data + result->data->row_count) { RETURN_NULL(); } mysqlnd_array_init(return_value, (uint) result->data->row_count); while (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) { MAKE_STD_ZVAL(row); mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI); add_index_zval(return_value, i++, row); } } /* }}} */ /* {{{ mysqlnd_res::fetch_into */ static void MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC) { zval row; zval **entry; uint i = 0; if (!result->m.fetch_row) { RETURN_NULL(); } /* Hint Zend how many elements we will have in the hash. Thus it won't extend and rehash the hash constantly. */ INIT_PZVAL(&row); mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL); if (Z_TYPE(row) != IS_ARRAY) { zval_dtor(&row); RETURN_NULL(); } zend_hash_internal_pointer_reset(Z_ARRVAL(row)); while (i++ < offset) { zend_hash_move_forward(Z_ARRVAL(row)); zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry); } zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry); *return_value = **entry; zval_copy_ctor(return_value); ZVAL_REFCOUNT(return_value) = 1; zval_dtor(&row); } /* }}} */ /* {{{ mysqlnd_result_init */ MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache) { MYSQLND_RES *ret = ecalloc(1, sizeof(MYSQLND_RES)); ret->field_count = field_count; ret->zval_cache = cache; ret->m.use_result = MYSQLND_METHOD(mysqlnd_res, use_result); ret->m.store_result = MYSQLND_METHOD(mysqlnd_res, store_result); ret->m.free_result = MYSQLND_METHOD(mysqlnd_res, free_result); ret->m.seek_data = MYSQLND_METHOD(mysqlnd_res, data_seek); ret->m.num_rows = MYSQLND_METHOD(mysqlnd_res, num_rows); ret->m.num_fields = MYSQLND_METHOD(mysqlnd_res, num_fields); ret->m.fetch_into = MYSQLND_METHOD(mysqlnd_res, fetch_into); ret->m.fetch_all = MYSQLND_METHOD(mysqlnd_res, fetch_all); ret->m.fetch_field_data = MYSQLND_METHOD(mysqlnd_res, fetch_field_data); ret->m.seek_field = MYSQLND_METHOD(mysqlnd_res, field_seek); ret->m.field_tell = MYSQLND_METHOD(mysqlnd_res, field_tell); ret->m.fetch_field = MYSQLND_METHOD(mysqlnd_res, fetch_field); ret->m.fetch_field_direct = MYSQLND_METHOD(mysqlnd_res, fetch_field_direct); ret->m.skip_result = MYSQLND_METHOD(mysqlnd_res, skip_result); ret->m.free_result_buffers = MYSQLND_METHOD(mysqlnd_res, free_result_buffers); ret->m.free_result_internal = mysqlnd_internal_free_result; ret->m.free_result_contents = mysqlnd_internal_free_result_contents; ret->m.read_result_metadata = MYSQLND_METHOD(mysqlnd_res, read_result_metadata); ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered; ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered; return ret; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_result.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_result.h +++ php-src/ext/mysqlnd/mysqlnd_result.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_result.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_RESULT_H #define MYSQLND_RESULT_H MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache); void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC); enum_func_status mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool update_max_length, zend_bool to_cache TSRMLS_DC); enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC); #endif /* MYSQLND_RESULT_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_result_meta.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_result_meta.c +++ php-src/ext/mysqlnd/mysqlnd_result_meta.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_result_meta.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_result.h" #include "mysqlnd_wireprotocol.h" #include "ext/standard/basic_functions.h" /* {{{ php_mysqlnd_free_field_metadata */ static void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent) { if (meta) { if (meta->root) { pefree(meta->root, persistent); meta->root = NULL; } if (meta->def) { pefree(meta->def, persistent); meta->def = NULL; } } } /* }}} */ /* {{{ mysqlnd_handle_numeric */ /* The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c and modified for the needs of mysqlnd. */ static zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx) { register char *tmp=key; if (*tmp=='-') { tmp++; } if ((*tmp>='0' && *tmp<='9')) { do { /* possibly a numeric index */ char *end=key+length-1; if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */ break; } while (tmp<end) { if (!(*tmp>='0' && *tmp<='9')) { break; } tmp++; } if (tmp==end && *tmp=='\0') { /* a numeric index */ if (*key=='-') { *idx = strtol(key, NULL, 10); if (*idx!=LONG_MIN) { return TRUE; } } else { *idx = strtol(key, NULL, 10); if (*idx!=LONG_MAX) { return TRUE; } } } } while (0); } return FALSE; } /* }}} */ #if PHP_MAJOR_VERSION >= 6 /* {{{ mysqlnd_unicode_is_key_numeric */ static zend_bool mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx) { register UChar *tmp=key; if (*tmp==0x2D /*'-'*/) { tmp++; } if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric index */ do { UChar *end=key+length-1; if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros */ break; } while (tmp<end) { if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { break; } tmp++; } if (tmp==end && *tmp==0) { /* a numeric index */ if (*key==0x2D /*'-'*/) { *idx = zend_u_strtol(key, NULL, 10); if (*idx!=LONG_MIN) { return TRUE; } } else { *idx = zend_u_strtol(key, NULL, 10); if (*idx!=LONG_MAX) { return TRUE; } } } } while (0); } return FALSE; } /* }}} */ #endif /* {{{ mysqlnd_res_meta::read_metadata */ static enum_func_status MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC) { int i = 0; php_mysql_packet_res_field field_packet; PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); for (;i < meta->field_count; i++) { long idx; if (meta->fields[i].root) { /* We re-read metadata for PS */ efree(meta->fields[i].root); meta->fields[i].root = NULL; } field_packet.metadata = &(meta->fields[i]); if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) { PACKET_FREE_ALLOCA(field_packet); return FAIL; } if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %d sent by the server. " "Please send a report to the developers", meta->fields[i].type); PACKET_FREE_ALLOCA(field_packet); return FAIL; } if (meta->fields[i].type == MYSQL_TYPE_BIT) { size_t field_len; ++meta->bit_fields_count; /* .length is in bits */ field_len = meta->fields[i].length / 8; /* If there is rest, add one byte : 8 bits = 1 byte but 9 bits = 2 bytes */ if (meta->fields[i].length % 8) { ++field_len; } switch (field_len) { case 8: case 7: case 6: case 5: meta->bit_fields_total_len += 20;/* 21 digis, no sign*/ break; case 4: meta->bit_fields_total_len += 10;/* 2 000 000 000*/ break; case 3: meta->bit_fields_total_len += 8;/* 12 000 000*/ break; case 2: meta->bit_fields_total_len += 5;/* 32 500 */ break; case 1: meta->bit_fields_total_len += 3;/* 120 */ break; } } #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { UChar *ustr; int ulen; zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, meta->fields[i].name, meta->fields[i].name_length TSRMLS_CC); if ((meta->zend_hash_keys[i].is_numeric = mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx))) { meta->zend_hash_keys[i].key = idx; efree(ustr); } else { meta->zend_hash_keys[i].ustr.u = ustr; meta->zend_hash_keys[i].ulen = ulen; meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1); } } else #endif { /* For BC we have to check whether the key is numeric and use it like this */ if ((meta->zend_hash_keys[i].is_numeric = mysqlnd_is_key_numeric(field_packet.metadata->name, field_packet.metadata->name_length + 1, &idx))) { meta->zend_hash_keys[i].key = idx; } else { meta->zend_hash_keys[i].key = zend_get_hash_value(field_packet.metadata->name, field_packet.metadata->name_length + 1); } } } PACKET_FREE_ALLOCA(field_packet); return PASS; } /* }}} */ /* {{{ mysqlnd_res_meta::free */ static void MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC) { int i; MYSQLND_FIELD *fields; if ((fields = meta->fields)) { i = meta->field_count; while (i--) { php_mysqlnd_free_field_metadata(fields++, persistent); } pefree(meta->fields, persistent); meta->fields = NULL; } if (meta->zend_hash_keys) { #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { for (i = 0; i < meta->field_count; i++) { if (meta->zend_hash_keys[i].ustr.v) { pefree(meta->zend_hash_keys[i].ustr.v, persistent); } } } #endif pefree(meta->zend_hash_keys, persistent); meta->zend_hash_keys = NULL; } pefree(meta, persistent); } /* }}} */ /* {{{ mysqlnd_res::clone_metadata */ static MYSQLND_RES_METADATA * MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent) { unsigned int i; /* +1 is to have empty marker at the end */ MYSQLND_RES_METADATA *new_meta = pemalloc(sizeof(MYSQLND_RES_METADATA), persistent); MYSQLND_FIELD *new_fields = pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); MYSQLND_FIELD *orig_fields = meta->fields; size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key); new_meta->zend_hash_keys = pemalloc(len, persistent); memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len); new_meta->m = meta->m; /* This will copy also the strings and the root, which we will have to adjust in the loop */ memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD)); for (i = 0; i < meta->field_count; i++) { /* First copy the root, then field by field adjust the pointers */ new_fields[i].root = pemalloc(orig_fields[i].root_len, persistent); memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len); if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) { new_fields[i].name = new_fields[i].root + (orig_fields[i].name - orig_fields[i].root); } if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) { new_fields[i].org_name = new_fields[i].root + (orig_fields[i].org_name - orig_fields[i].root); } if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) { new_fields[i].table = new_fields[i].root + (orig_fields[i].table - orig_fields[i].root); } if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) { new_fields[i].org_table = new_fields[i].root + (orig_fields[i].org_table - orig_fields[i].root); } if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) { new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root); } if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) { new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root); } /* def is not on the root, if allocated at all */ if (orig_fields[i].def) { new_fields[i].def = pemalloc(orig_fields[i].def_length + 1, persistent); /* copy the trailing \0 too */ memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1); } #if PHP_MAJOR_VERSION >= 6 if (new_meta->zend_hash_keys[i].ustr.u) { new_meta->zend_hash_keys[i].ustr.u = eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen); } #endif } new_meta->current_field = 0; new_meta->field_count = meta->field_count; new_meta->fields = new_fields; return new_meta; } /* }}} */ /* {{{ mysqlnd_res_meta::fetch_field */ static MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta) { if (meta->current_field >= meta->field_count) return NULL; return &meta->fields[meta->current_field++]; } /* }}} */ /* {{{ mysqlnd_res::field_tell */ static MYSQLND_FIELD_OFFSET MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta) { return meta->current_field; } /* }}} */ MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta) MYSQLND_METHOD(mysqlnd_res_meta, fetch_field), MYSQLND_METHOD(mysqlnd_res_meta, field_tell), MYSQLND_METHOD(mysqlnd_res_meta, read_metadata), MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata), MYSQLND_METHOD(mysqlnd_res_meta, free), MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_result_meta_init */ MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count) { MYSQLND_RES_METADATA *ret; /* +1 is to have empty marker at the end */ ret = ecalloc(1, sizeof(MYSQLND_RES_METADATA)); ret->field_count = field_count; ret->fields = ecalloc(field_count + 1, sizeof(MYSQLND_FIELD)); ret->zend_hash_keys = ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key)); ret->m = & mysqlnd_mysqlnd_res_meta_methods; return ret; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_result_meta.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_result_meta.h +++ php-src/ext/mysqlnd/mysqlnd_result_meta.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_result_meta.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_RESULT_META_H #define MYSQLND_RESULT_META_H MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count); #endif /* MYSQLND_RESULT_META_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_statistics.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_statistics.c +++ php-src/ext/mysqlnd/mysqlnd_statistics.c /* +----------------------------------------------------------------------+ | 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: mysqlnd_statistics.c,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_statistics.h" typedef struct st_mysqlnd_string { char *s; size_t l; } MYSQLND_STRING; #define STR_W_LEN(str) str, (sizeof(str) - 1) /* {{{ mysqlnd_stats_values_names */ static const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = { { STR_W_LEN("bytes_sent") }, { STR_W_LEN("bytes_received") }, { STR_W_LEN("packets_sent") }, { STR_W_LEN("packets_received") }, { STR_W_LEN("protocol_overhead_in") }, { STR_W_LEN("protocol_overhead_out") }, { STR_W_LEN("result_set_queries") }, { STR_W_LEN("non_result_set_queries") }, { STR_W_LEN("no_index_used") }, { STR_W_LEN("bad_index_used") }, { STR_W_LEN("buffered_sets") }, { STR_W_LEN("unbuffered_sets") }, { STR_W_LEN("ps_buffered_sets") }, { STR_W_LEN("ps_unbuffered_sets") }, { STR_W_LEN("flushed_normal_sets") }, { STR_W_LEN("flushed_ps_sets") }, { STR_W_LEN("rows_fetched_from_server") }, { STR_W_LEN("rows_fetched_from_client") }, { STR_W_LEN("rows_skipped") }, { STR_W_LEN("copy_on_write_saved") }, { STR_W_LEN("copy_on_write_performed") }, { STR_W_LEN("command_buffer_too_small") }, { STR_W_LEN("connect_success") }, { STR_W_LEN("connect_failure") }, { STR_W_LEN("connection_reused") }, { STR_W_LEN("explicit_close") }, { STR_W_LEN("implicit_close") }, { STR_W_LEN("disconnect_close") }, { STR_W_LEN("in_middle_of_command_close") }, { STR_W_LEN("explicit_free_result") }, { STR_W_LEN("implicit_free_result") }, { STR_W_LEN("explicit_stmt_close") }, { STR_W_LEN("implicit_stmt_close") }, }; /* }}} */ void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { unsigned int i; mysqlnd_array_init(return_value, STAT_LAST); for (i = 0; i < STAT_LAST; i++) { char tmp[22]; sprintf((char *)&tmp, MYSQLND_LLU_SPEC, stats->values[i]); #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { UChar *ustr, *tstr; int ulen, tlen; zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, mysqlnd_stats_values_names[i].s, mysqlnd_stats_values_names[i].l + 1 TSRMLS_CC); zend_string_to_unicode(UG(utf8_conv), &tstr, &tlen, tmp, strlen(tmp) + 1 TSRMLS_CC); add_u_assoc_unicode_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen, tstr, 1); efree(ustr); efree(tstr); } else #endif { add_assoc_string_ex(return_value, mysqlnd_stats_values_names[i].s, mysqlnd_stats_values_names[i].l + 1, tmp, 1); } } } /* {{{ _mysqlnd_get_client_stats */ PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats; if (!stats_ptr) { memset(&stats, 0, sizeof(stats)); stats_ptr = &stats; } mysqlnd_fill_stats_hash(stats_ptr, return_value TSRMLS_CC ZEND_FILE_LINE_CC); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_statistics.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_statistics.h +++ php-src/ext/mysqlnd/mysqlnd_statistics.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_statistics.h,v 1.1 2007/07/25 23:47:32 jani Exp $ */ #ifndef MYSQLND_STATISTICS_H #define MYSQLND_STATISTICS_H extern MYSQLND_STATS *mysqlnd_global_stats; #ifdef ZTS #define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ { \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ mysqlnd_global_stats->values[statistic]++; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic]++; \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ { \ my_uint64 v = (value); \ \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ mysqlnd_global_stats->values[statistic]+= v; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic]+= v; \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ { \ my_uint64 v1 = (value1); \ my_uint64 v2 = (value2); \ my_uint64 v3 = (value3); \ \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ mysqlnd_global_stats->values[statistic1]+= v1; \ mysqlnd_global_stats->values[statistic2]+= v2; \ mysqlnd_global_stats->values[statistic3]+= v3; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic1]+= v1; \ ((MYSQLND_STATS *) conn_stats)->values[statistic2]+= v2; \ ((MYSQLND_STATS *) conn_stats)->values[statistic3]+= v3; \ } \ } #else /* NON-ZTS */ #define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ { \ mysqlnd_global_stats->values[statistic]++; \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic]++; \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ { \ my_uint64 v = (value); \ \ mysqlnd_global_stats->values[statistic]+= v; \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic]+= v; \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ { \ my_uint64 v1 = (value1); \ my_uint64 v2 = (value2); \ my_uint64 v3 = (value3); \ \ mysqlnd_global_stats->values[statistic1]+= v1; \ mysqlnd_global_stats->values[statistic2]+= v2; \ mysqlnd_global_stats->values[statistic3]+= v3; \ \ if ((conn_stats)) { \ ((MYSQLND_STATS *) conn_stats)->values[statistic1]+= v1; \ ((MYSQLND_STATS *) conn_stats)->values[statistic2]+= v2; \ ((MYSQLND_STATS *) conn_stats)->values[statistic3]+= v3; \ } \ } #endif void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); #endif /* MYSQLND_STATISTICS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_wireprotocol.c?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_wireprotocol.c +++ php-src/ext/mysqlnd/mysqlnd_wireprotocol.c /* +----------------------------------------------------------------------+ | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 2006-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Georg Richter <georg@mysql.com> | | Andrey Hristov <andrey@mysql.com> | | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_globals.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_statistics.h" #include "mysqlnd_palloc.h" #include "ext/standard/sha1.h" #include "php_network.h" #ifndef PHP_WIN32 #include <netinet/tcp.h> #else #endif #define USE_CORK 0 #define MYSQLND_SILENT #define MYSQLND_DUMP_HEADER_N_BODY2 #define MYSQLND_DUMP_HEADER_N_BODY_FULL2 #define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1) #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type) \ { \ if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\ return FAIL;\ }\ if ((buf_size) < (packet)->header.size) { \ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Packet buffer wasn't big enough" \ "%u bytes will be unread", (packet)->header.size - (buf_size));\ }\ if (!mysqlnd_read_body((conn), (buf), \ MIN((buf_size), (packet)->header.size) TSRMLS_CC)) { \ php_error(E_WARNING, "Empty %s packet body", (packet_type));\ return FAIL; \ } \ } extern mysqlnd_packet_methods packet_methods[]; static const char *unknown_sqlstate= "HY000"; char * const mysqlnd_empty_string = ""; /* {{{ mysqlnd_command_to_text */ const char * const mysqlnd_command_to_text[COM_END] = { "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST", "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS", "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING", "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP", "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE", "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE", "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON" }; /* }}} */ /* {{{ php_mysqlnd_net_field_length Get next field's length */ unsigned long php_mysqlnd_net_field_length(zend_uchar **packet) { register zend_uchar *p= (zend_uchar *)*packet; if (*p < 251) { (*packet)++; return (unsigned long) *p; } switch (*p) { case 251: (*packet)++; return MYSQLND_NULL_LENGTH; case 252: (*packet) += 3; return (unsigned long) uint2korr(p+1); case 253: (*packet) += 4; return (unsigned long) uint3korr(p+1); default: (*packet) += 9; return (unsigned long) uint4korr(p+1); } } /* }}} */ /* {{{ php_mysqlnd_net_field_length_ll Get next field's length */ mynd_ulonglong php_mysqlnd_net_field_length_ll(zend_uchar **packet) { register zend_uchar *p= (zend_uchar *)*packet; if (*p < 251) { (*packet)++; return (mynd_ulonglong) *p; } switch (*p) { case 251: (*packet)++; return (mynd_ulonglong) MYSQLND_NULL_LENGTH; case 252: (*packet) += 3; return (mynd_ulonglong) uint2korr(p + 1); case 253: (*packet) += 4; return (mynd_ulonglong) uint3korr(p + 1); default: (*packet) += 9; return (mynd_ulonglong) uint8korr(p + 1); } } /* }}} */ /* {{{ php_mysqlnd_net_store_length */ zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length) { if (length < (mynd_ulonglong) L64(251)) { *packet = (zend_uchar) length; return packet + 1; } if (length < (mynd_ulonglong) L64(65536)) { *packet++ = 252; int2store(packet,(uint) length); return packet + 2; } if (length < (mynd_ulonglong) L64(16777216)) { *packet++ = 253; int3store(packet,(ulong) length); return packet + 3; } *packet++ = 254; int8store(packet, length); return packet + 8; } /* }}} */ /* {{{ php_mysqlnd_consume_uneaten_data */ size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC) { /* Switch to non-blocking mode and try to consume something from the line, if possible, then continue. This saves us from looking for the actuall place where out-of-order packets have been sent. If someone is completely sure that everything is fine, he can switch it off. */ char tmp_buf[256]; MYSQLND_NET *net = &conn->net; size_t skipped_bytes = 0; int opt = PHP_STREAM_OPTION_BLOCKING; int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC); if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) { /* Do a read of 1 byte */ int bytes_consumed; do { skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf))); } while (bytes_consumed == sizeof(tmp_buf)); if (was_blocked) { net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC); } if (bytes_consumed) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't " "consumed all the output from the server", bytes_consumed, mysqlnd_command_to_text[net->last_command]); } } net->last_command = cmd; return skipped_bytes; } /* }}} */ /* {{{ php_mysqlnd_read_error_from_line */ static enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, char *error, int error_buf_len, unsigned int *error_no, char *sqlstate) { zend_uchar *p = buf; int error_msg_len= 0; if (buf_len > 2) { *error_no = uint2korr(p); p+= 2; /* sqlstate is following */ if (*p == '#') { memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH); p+= MYSQLND_SQLSTATE_LENGTH; } error_msg_len = buf_len - (p - buf); error_msg_len = MIN(error_msg_len, error_buf_len - 1); memcpy(error, p, error_msg_len); } else { *error_no = CR_UNKNOWN_ERROR; memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH); } sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0'; error[error_msg_len]= '\0'; return FAIL; } /* }}} */ /* {{{ mysqlnd_set_sock_no_delay */ int mysqlnd_set_sock_no_delay(php_stream *stream) { int socketd = ((php_netstream_data_t*)stream->abstract)->socket; int ret = SUCCESS; int flag = 1; int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); if (result == -1) { ret = FAILURE; } return ret; } /* }}} */ /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */ #define STORE_HEADER_SIZE(safe_storage, buffer) int4store((safe_storage), (*(uint32 *)(buffer))) #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) /* {{{ mysqlnd_stream_write_w_header */ /* IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!! This is done for performance reasons in the caller of this function. Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. Neither are quick, thus the clients of this function are obligated to do what they are asked for. `count` is actually the length of the payload data. Thus : count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer) */ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC) { zend_uchar safe_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, packets_sent = 1; zend_uchar *p = (zend_uchar *) buf; net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; while (left > MYSQLND_MAX_PACKET_SIZE) { STORE_HEADER_SIZE(safe_storage, p); int3store(p, MYSQLND_MAX_PACKET_SIZE); int1store(p + 3, net->packet_no); net->packet_no++; ret = php_stream_write(net->stream, (char *)p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE); RESTORE_HEADER_SIZE(p, safe_storage); p += MYSQLND_MAX_PACKET_SIZE; left -= MYSQLND_MAX_PACKET_SIZE; packets_sent++; } /* Even for zero size payload we have to send a packet */ STORE_HEADER_SIZE(safe_storage, p); int3store(p, left); int1store(p + 3, net->packet_no); net->packet_no++; ret = php_stream_write(net->stream, (char *)p, left + MYSQLND_HEADER_SIZE); RESTORE_HEADER_SIZE(p, safe_storage); MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, STAT_PACKETS_SENT, packets_sent); net->stream->chunk_size = old_chunk_size; return ret; } /* }}} */ /* {{{ mysqlnd_stream_write_w_command */ #if USE_CORK && defined(TCP_CORK) static size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum php_mysqlnd_server_command command, const char * const buf, size_t count TSRMLS_DC) { zend_uchar safe_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; net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); int1store(safe_storage + MYSQLND_HEADER_SIZE, command); while (left > MYSQLND_MAX_PACKET_SIZE) { size_t body_size = MYSQLND_MAX_PACKET_SIZE; int3store(safe_storage, MYSQLND_MAX_PACKET_SIZE); int1store(safe_storage + 3, net->packet_no); net->packet_no++; ret = php_stream_write(net->stream, (char *)safe_storage, header_len); if (command_sent == FALSE) { --header_len; /* Sent one byte less*/ --body_size; command_sent = TRUE; } ret = php_stream_write(net->stream, (char *)p, body_size); p += body_size; left -= body_size; packets_sent++; } /* Even for zero size payload we have to send a packet */ int3store(safe_storage, header_len == MYSQLND_HEADER_SIZE? left:left+1); int1store(safe_storage + 3, net->packet_no); net->packet_no++; ret = php_stream_write(net->stream, (char *)safe_storage, header_len); if (left) { ret = php_stream_write(net->stream, (char *)p, left); } corked = 0; setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE); STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE); STAT_PACKETS_SENT, packets_sent); net->stream->chunk_size = old_chunk_size; return ret; } #endif /* }}} */ /* {{{ mysqlnd_read_header */ static enum_func_status mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC) { MYSQLND_NET *net = &conn->net; char buffer[MYSQLND_HEADER_SIZE]; char *p = buffer; int to_read = MYSQLND_HEADER_SIZE, ret; do { if (!(ret= php_stream_read(net->stream, p, to_read))) { php_error(E_WARNING, "Error while reading header from socket"); return FAIL; } p += ret; to_read -= ret; } while (to_read); header->size = uint3korr(buffer); header->packet_no = uint1korr(buffer + 3); MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, STAT_BYTES_RECEIVED, MYSQLND_HEADER_SIZE, STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE, STAT_PACKETS_RECEIVED, 1); if (net->packet_no == header->packet_no) { /* Have to increase the number, so we can send correct number back. It will round at 255 as this is unsigned char. The server needs this for simple flow control checking. */ net->packet_no++; #ifdef MYSQLND_DUMP_HEADER_N_BODY php_printf("\nHEADER: packet_no=%d size=%3d\n", header->packet_no, header->size); #endif return PASS; } php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d", net->packet_no, header->packet_no, header->size); return FAIL; } /* }}} */ /* {{{ mysqlnd_read_body */ static size_t mysqlnd_read_body(MYSQLND *conn, zend_uchar *buf, size_t size TSRMLS_DC) { size_t ret; char *p = (char *)buf; #ifdef MYSQLND_DUMP_HEADER_N_BODY int iter = 0; #endif MYSQLND_NET *net = &conn->net; do { size -= (ret = php_stream_read(net->stream, p, size)); #ifdef MYSQLND_DUMP_HEADER_N_BODY if (size || iter++) { php_printf("read=%d buf=%p p=%p chunk_size=%d left=%d\n", ret, buf, p , net->stream->chunk_size, size); } #endif p += ret; } while (size > 0); MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, p - (char*)buf); #ifdef MYSQLND_DUMP_HEADER_N_BODY_FULL { int i; php_printf("\tBODY: requested=%d last_read=%3d\n\t", p - (char*)buf, ret); for (i = 0 ; i < p - (char*)buf; i++) printf("[%c]", *(char *)(&(buf[i]))); php_printf("\n\t"); for (i = 0 ; i < p - (char*)buf; i++) printf("%.2X ", (int)*((char*)&(buf[i])));php_printf("\n-=-=-=-=-\n"); } #endif return p - (char*)buf; } /* }}} */ /* {{{ php_mysqlnd_greet_read */ static enum_func_status php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) { zend_uchar buf[512]; zend_uchar *p= buf; zend_uchar *begin = buf; php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting"); packet->protocol_version = uint1korr(p); p++; if (packet->protocol_version == 0xFF) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), &packet->error_no, packet->sqlstate); /* The server doesn't send sqlstate in the greet packet. It's a bug#26426 , so we have to set it correctly ourselves. It's probably "Too many connections, which has SQL state 08004". */ if (packet->error_no == 1040) { memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH); } return PASS; } packet->server_version = pestrdup((char *)p, conn->persistent); p+= strlen(packet->server_version) + 1; /* eat the '\0' */ packet->thread_id = uint4korr(p); p+=4; memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323); p+= 8; /* pad1 */ p++; packet->server_capabilities = uint2korr(p); p+= 2; packet->charset_no = uint1korr(p); p++; packet->server_status = uint2korr(p); p+= 2; /* pad2 */ p+= 13; if (p - buf < packet->header.size) { /* scramble_buf is split into two parts */ memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323); } else { packet->pre41 = TRUE; } if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected", p - begin - packet->header.size); } return PASS; } /* }}} */ /* {{{ php_mysqlnd_greet_free_mem */ static void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet; if (p->server_version) { efree(p->server_version); p->server_version = NULL; } if (!alloca) { efree(p); } } /* }}} */ #define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \ CLIENT_MULTI_RESULTS) /* {{{ php_mysqlnd_crypt */ static void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len) { const unsigned char *s1_end= s1 + len; while (s1 < s1_end) { *buffer++= *s1++ ^ *s2++; } } /* }}} */ /* {{{ php_mysqlnd_scramble */ void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password) { PHP_SHA1_CTX context; unsigned char sha1[SHA1_MAX_LENGTH]; unsigned char sha2[SHA1_MAX_LENGTH]; /* Phase 1: hash password */ PHP_SHA1Init(&context); PHP_SHA1Update(&context, password, strlen((char *)password)); PHP_SHA1Final(sha1, &context); /* Phase 2: hash sha1 */ PHP_SHA1Init(&context); PHP_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH); PHP_SHA1Final(sha2, &context); /* Phase 3: hash scramble + sha2 */ PHP_SHA1Init(&context); PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH); PHP_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH); PHP_SHA1Final(buffer, &context); /* let's crypt buffer now */ php_mysqlnd_crypt(buffer, (const unsigned char *)buffer, (const unsigned char *)sha1, SHA1_MAX_LENGTH); } /* }}} */ /* {{{ php_mysqlnd_auth_write */ static size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) { char buffer[1024]; register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */ int len; register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet; packet->client_flags |= MYSQLND_CAPABILITIES; if (packet->db) { packet->client_flags |= CLIENT_CONNECT_WITH_DB; } if (PG(open_basedir) && strlen(PG(open_basedir))) { packet->client_flags ^= CLIENT_LOCAL_FILES; } /* don't allow multi_queries via connect parameter */ packet->client_flags ^= CLIENT_MULTI_STATEMENTS; int4store(p, packet->client_flags); p+= 4; int4store(p, packet->max_packet_size); p+= 4; int1store(p, packet->charset_no); p++; memset(p, 0, 23); /* filler */ p+= 23; len= strlen(packet->user); strncpy(p, packet->user, len); p+= len; *p++ = '\0'; /* copy scrambled pass*/ if (packet->password && packet->password[0]) { /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ int1store(p, 20); p++; php_mysqlnd_scramble((unsigned char*)p, packet->server_scramble_buf, (unsigned char *)packet->password); p+= 20; } else { /* Zero length */ int1store(p, 0); p++; } if (packet->db) { memcpy(p, packet->db, packet->db_len); p+= packet->db_len; *p++= '\0'; } /* Handle CLIENT_CONNECT_WITH_DB */ /* no \0 for no DB */ return mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC); } /* }}} */ /* {{{ php_mysqlnd_auth_free_mem */ static void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca) { if (!alloca) { efree((php_mysql_packet_auth *) _packet); } } /* }}} */ /* {{{ php_mysqlnd_ok_read */ static enum_func_status php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) { zend_uchar buf[1024]; zend_uchar *p = buf; zend_uchar *begin = buf; int i; register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "OK"); /* Should be always 0x0 or 0xFF for error */ packet->field_count= uint1korr(p); p++; if (0xFF == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), &packet->error_no, packet->sqlstate); return PASS; } /* Everything was fine! */ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p); packet->server_status = uint2korr(p); p+= 2; packet->warning_count = uint2korr(p); p+= 2; /* There is a message */ if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) { packet->message = estrndup((char *)p, MIN(i, sizeof(buf) - (p - buf))); packet->message_len = i; } else { packet->message = NULL; } #ifndef MYSQLND_SILENT php_printf("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d\n", packet->affected_rows, packet->last_insert_id, packet->server_status, packet->warning_count); #endif if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet %d bytes shorter than expected", p - begin - packet->header.size); } return PASS; } /* }}} */ /* {{{ php_mysqlnd_ok_free_mem */ static void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet; if (p->message) { efree(p->message); p->message = NULL; } if (!alloca) { efree(p); } } /* }}} */ /* {{{ php_mysqlnd_eof_read */ static enum_func_status php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) { /* EOF packet is since 4.1 five bytes long, but we can get also an error, make it bigger. Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE */ php_mysql_packet_eof *packet= (php_mysql_packet_eof *) _packet; zend_uchar buf[5 + 10 + sizeof(packet->sqlstate) + sizeof(packet->error)]; zend_uchar *p= buf; zend_uchar *begin = buf; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "EOF"); /* Should be always 0xFE */ packet->field_count= uint1korr(p); p++; if (0xFF == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), &packet->error_no, packet->sqlstate); return PASS; } /* 4.1 sends 1 byte EOF packet after metadata of PREPARE/EXECUTE but 5 bytes after the result. This is not according to the Docs@Forge!!! */ if (packet->header.size > 1) { packet->warning_count = uint2korr(p); p+= 2; packet->server_status = uint2korr(p); p+= 2; } else { packet->warning_count = 0; packet->server_status = 0; } if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet %d bytes shorter than expected", p - begin - packet->header.size); } #ifndef MYSQLND_SILENT php_printf("EOF packet: server_status=%d warnings=%d\n", packet->server_status, packet->warning_count); #endif return PASS; } /* }}} */ /* {{{ php_mysqlnd_eof_free_mem */ static void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca) { if (!alloca) { efree(_packet); } } /* }}} */ /* {{{ php_mysqlnd_cmd_write */ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) { /* Let's have some space, which we can use, if not enough, we will allocate new buffer */ php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet; MYSQLND_NET *net = &conn->net; /* Reset packet_no, or we will get bad handshake! Every command starts a new TX and packet numbers are reset to 0. */ net->packet_no = 0; #if MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC); #endif if (!packet->argument || !packet->arg_len) { char buffer[MYSQLND_HEADER_SIZE + 1]; int1store(buffer + MYSQLND_HEADER_SIZE, packet->command); return mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC); } else { #if USE_CORK && defined(TCP_CORK) return mysqlnd_stream_write_w_command(conn, packet->command, packet->argument, packet->arg_len TSRMLS_CC); #else size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret; zend_uchar *tmp, *p; tmp = (tmp_len > net->cmd_buffer.length)? emalloc(tmp_len):net->cmd_buffer.buffer; p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */ int1store(p, packet->command); p++; memcpy(p, packet->argument, packet->arg_len); ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC); if (tmp != net->cmd_buffer.buffer) { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL); efree(tmp); } return ret; #endif } } /* }}} */ /* {{{ php_mysqlnd_cmd_free_mem */ static void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca) { if (!alloca) { efree((php_mysql_packet_command *) _packet); } } /* }}} */ /* {{{ php_mysqlnd_rset_header_read */ static enum_func_status php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) { zend_uchar buf[1024]; zend_uchar *p = buf; zend_uchar *begin = buf; size_t len; php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "resultset header"); /* Don't increment. First byte is 0xFF on error, but otherwise is starting byte of encoded sequence for length. */ if (*p == 0xFF) { /* Error */ p++; php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate); return PASS; } packet->field_count= php_mysqlnd_net_field_length(&p); switch (packet->field_count) { case MYSQLND_NULL_LENGTH: /* First byte in the packet is the field count. Thus, the name is size - 1. And we add 1 for a trailing \0. */ len = packet->header.size - 1; packet->info_or_local_file = emalloc(len + 1); memcpy(packet->info_or_local_file, p, len); packet->info_or_local_file[len] = '\0'; packet->info_or_local_file_len = len; break; case 0x00: packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p); packet->server_status = uint2korr(p); p+=2; packet->warning_count = uint2korr(p); p+=2; /* Check for additional textual data */ if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { packet->info_or_local_file = emalloc(len + 1); memcpy(packet->info_or_local_file, p, len); packet->info_or_local_file[len] = '\0'; packet->info_or_local_file_len = len; } break; default: /* Result set */ break; } if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected", p - begin - packet->header.size); } return PASS; } /* }}} */ /* {{{ php_mysqlnd_rset_header_free_mem */ static void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet; if (p->info_or_local_file) { efree(p->info_or_local_file); p->info_or_local_file = NULL; } if (!alloca) { efree(p); } } /* }}} */ static size_t rset_field_offsets[] = { STRUCT_OFFSET(MYSQLND_FIELD, catalog), STRUCT_OFFSET(MYSQLND_FIELD, catalog_length), STRUCT_OFFSET(MYSQLND_FIELD, db), STRUCT_OFFSET(MYSQLND_FIELD, db_length), STRUCT_OFFSET(MYSQLND_FIELD, table), STRUCT_OFFSET(MYSQLND_FIELD, table_length), STRUCT_OFFSET(MYSQLND_FIELD, org_table), STRUCT_OFFSET(MYSQLND_FIELD, org_table_length), STRUCT_OFFSET(MYSQLND_FIELD, name), STRUCT_OFFSET(MYSQLND_FIELD, name_length), STRUCT_OFFSET(MYSQLND_FIELD, org_name), STRUCT_OFFSET(MYSQLND_FIELD, org_name_length) }; /* {{{ php_mysqlnd_rset_field_read */ static enum_func_status php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) { /* Should be enough for the metadata of a single row */ php_mysql_packet_res_field *packet= (php_mysql_packet_res_field *) _packet; zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer; zend_uchar *p = buf; zend_uchar *begin = buf; char *root_ptr; size_t buf_len = conn->net.cmd_buffer.length, len, total_len = 0; MYSQLND_FIELD *meta; unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field"); if (packet->skip_parsing) { return PASS; } meta = packet->metadata; for (i = 0; i < field_count; i += 2) { switch ((len = php_mysqlnd_net_field_length(&p))) { case 0: *(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0; break; case MYSQLND_NULL_LENGTH: goto faulty_fake; default: *(char **)(((char *)meta) + rset_field_offsets[i]) = p; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len; p += len; total_len += len + 1; break; } } /* 1 byte filler */ p++; meta->charsetnr = uint2korr(p); p += 2; meta->length = uint4korr(p); p += 4; meta->type = uint1korr(p); p += 1; meta->flags = uint2korr(p); p += 2; meta->decimals = uint2korr(p); p += 1; /* 2 byte filler */ p +=2; /* Should we set NUM_FLAG (libmysql does it) ? */ if ( (meta->type <= MYSQL_TYPE_INT24 && (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8) ) || meta->type == MYSQL_TYPE_YEAR) { meta->flags |= NUM_FLAG; } /* def could be empty, thus don't allocate on the root */ if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { meta->def = emalloc(len + 1); memcpy(meta->def, p, len); meta->def[len] = '\0'; meta->def_length = len; p += len; } if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes " "shorter than expected", p - begin - packet->header.size); } root_ptr = meta->root = emalloc(total_len); meta->root_len = total_len; /* Now do allocs */ if (meta->catalog && meta->catalog != mysqlnd_empty_string) { len = meta->catalog_length; meta->catalog = memcpy(root_ptr, meta->catalog, len); *(root_ptr +=len) = '\0'; root_ptr++; } if (meta->db && meta->db != mysqlnd_empty_string) { len = meta->db_length; meta->db = memcpy(root_ptr, meta->db, len); *(root_ptr + len) = '\0'; } if (meta->table && meta->table != mysqlnd_empty_string) { len = meta->table_length; meta->table = memcpy(root_ptr, meta->table, len); *(root_ptr +=len) = '\0'; root_ptr++; } if (meta->org_table && meta->org_table != mysqlnd_empty_string) { len = meta->org_table_length; meta->org_table = memcpy(root_ptr, meta->org_table, len); *(root_ptr +=len) = '\0'; root_ptr++; } if (meta->name && meta->name != mysqlnd_empty_string) { len = meta->name_length; meta->name = memcpy(root_ptr, meta->name, len); *(root_ptr +=len) = '\0'; root_ptr++; } if (meta->org_name && meta->org_name != mysqlnd_empty_string) { len = meta->org_name_length; meta->org_name = memcpy(root_ptr, meta->org_name, len); *(root_ptr +=len) = '\0'; root_ptr++; } #ifndef MYSQLND_SILENT php_printf("\tFIELD=[%s.%s.%s]\n", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*", meta->name? meta->name:"*NA*"); #endif return PASS; faulty_fake: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH.", " The server is faulty"); return FAIL; } /* }}} */ /* {{{ php_mysqlnd_rset_field_free_mem */ static void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet; /* p->metadata was passed to us as temporal buffer */ if (!alloca) { efree(p); } } /* }}} */ static enum_func_status php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, size_t *data_size, zend_bool persistent_alloc, unsigned int prealloc_more_bytes TSRMLS_DC) { enum_func_status ret = PASS; mysqlnd_packet_header header; zend_uchar *new_buf = NULL, *p = *buf; zend_bool first_iteration = TRUE; MYSQLND_NET *net = &conn->net; /* To ease the process the server splits everything in packets up to 2^24 - 1. Even in the case the payload is evenly divisible by this value, the last packet will be empty, namely 0 bytes. Thus, we can read every packet and ask for next one if they have 2^24 - 1 sizes. But just read the header of a zero-length byte, don't read the body, there is no such. */ *data_size = prealloc_more_bytes; while (1) { if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) { ret = FAIL; break; } *data_size += header.size; if (first_iteration && header.size > buf_size) { first_iteration = FALSE; /* We need a trailing \0 for the last string, in case of text-mode, to be able to implement read-only variables. Thus, we add + 1. */ p = new_buf = pemalloc(*data_size + 1, persistent_alloc); net->stream->chunk_size = header.size; } else if (!first_iteration) { /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */ if (!header.size) { break; } /* We have to realloc the buffer. We need a trailing \0 for the last string, in case of text-mode, to be able to implement read-only variables. */ new_buf = perealloc(new_buf, *data_size + 1, persistent_alloc); /* The position could have changed, recalculate */ p = new_buf + (*data_size - header.size); } if (!mysqlnd_read_body(conn, p, header.size TSRMLS_CC)) { php_error(E_WARNING, "Empty row packet body"); ret = FAIL; break; } if (header.size < MYSQLND_MAX_PACKET_SIZE) { break; } } if (ret == PASS && new_buf) { *buf = new_buf; } *data_size -= prealloc_more_bytes; return ret; } /* {{{ php_mysqlnd_rowp_read_binary_protocol */ static void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND *conn, zend_uchar *p, size_t data_size TSRMLS_DC) { int i; zend_uchar *null_ptr, bit; zval **current_field, **end_field, **start_field; zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode; zend_bool allocated; void *obj; end_field = (current_field = start_field = packet->fields) + packet->field_count; /* skip the first byte, not 0xFE -> 0x0, status */ p++; null_ptr= p; p += (packet->field_count + 9)/8; /* skip null bits */ bit = 4; /* first 2 bits are reserved */ for (i = 0; current_field < end_field; current_field++, i++) { #if 1 obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated); if (allocated) { *current_field = (zval *) obj; } else { /* It's from the cache, so we can upcast here */ *current_field = &((mysqlnd_zval *) obj)->zv; ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER; } #else MAKE_STD_ZVAL(*current_field); #endif if (*null_ptr & bit) { ZVAL_NULL(*current_field); } else { enum_mysqlnd_field_types type = packet->fields_metadata[i].type; mysqlnd_ps_fetch_functions[type].func(*current_field, &packet->fields_metadata[i], 0, &p, as_unicode TSRMLS_CC); } if (!((bit<<=1) & 255)) { bit= 1; /* To next byte */ null_ptr++; } } /* Normal queries: The buffer has one more byte at the end, because we need it */ packet->row_buffer[data_size] = '\0'; } /* }}} */ /* {{{ php_mysqlnd_rowp_read_text_protocol */ static void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *conn, zend_uchar *p, size_t data_size TSRMLS_DC) { int i; zend_bool last_field_was_string; zval **current_field, **end_field, **start_field; zend_uchar *bit_area = packet->row_buffer + data_size + 1; /* we allocate from here */ zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode; #ifdef MYSQLND_STRING_TO_INT_CONVERSION zend_bool as_int = conn->options.int_and_year_as_int; #endif end_field = (current_field = start_field = packet->fields) + packet->field_count; for (i = 0; current_field < end_field; current_field++, i++) { /* Don't reverse the order. It is significant!*/ void *obj; zend_bool allocated; zend_uchar *this_field_len_pos = p; /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */ unsigned long len = php_mysqlnd_net_field_length(&p); obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated); if (allocated) { *current_field = (zval *) obj; } else { /* It's from the cache, so we can upcast here */ *current_field = &((mysqlnd_zval *) obj)->zv; ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_FREE; } if (current_field > start_field && last_field_was_string) { /* Normal queries: We have to put \0 now to the end of the previous field, if it was a string. IS_NULL doesn't matter. Because we have already read our length, then we can overwrite it in the row buffer. This statement terminates the previous field, not the current one. NULL_LENGTH is encoded in one byte, so we can stick a \0 there. Any string's length is encoded in at least one byte, so we can stick a \0 there. */ *this_field_len_pos = '\0'; } /* NULL or NOT NULL, this is the question! */ if (len == MYSQLND_NULL_LENGTH) { ZVAL_NULL(*current_field); last_field_was_string = FALSE; } else { struct st_mysqlnd_perm_bind perm_bind = mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type]; #ifdef MYSQLND_STRING_TO_INT_CONVERSION if (as_int && perm_bind.php_type == IS_LONG && perm_bind.pack_len <= SIZEOF_LONG) { zend_uchar save = *(p + len); /* We have to make it ASCIIZ temporarily */ *(p + len) = '\0'; if (perm_bind.pack_len < SIZEOF_LONG) { /* direct conversion */ my_int64 v = atoll((char *) p); ZVAL_LONG(*current_field, v); } else { my_uint64 v = (my_uint64) atoll((char *) p); zend_bool uns = packet->fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE; /* We have to make it ASCIIZ temporarily */ #if SIZEOF_LONG==8 if (uns == TRUE && v > 9223372036854775807L) #elif SIZEOF_LONG==4 if ((uns == TRUE && v > L64(2147483647)) || (uns == FALSE && (( L64(2147483647) < (my_int64) v) || (L64(-2147483648) > (my_int64) v)))) #endif /* SIZEOF */ { ZVAL_STRINGL(*current_field, (char *)p, len, 0); } else { ZVAL_LONG(*current_field, (my_int64)v); } } *(p + len) = save; } else #endif if (packet->fields_metadata[i].type == MYSQL_TYPE_BIT) { /* BIT fields are specially handled. As they come as bit mask, we have to convert it to human-readable representation. As the bits take less space in the protocol than the numbers they represent, we don't have enough space in the packet buffer to overwrite inside. Thus, a bit more space is pre-allocated at the end of the buffer, see php_mysqlnd_rowp_read(). And we add the strings at the end. Definitely not nice, _hackish_ :(, but works. */ zend_uchar *start = bit_area; ps_fetch_from_1_to_8_bytes(*current_field, &(packet->fields_metadata[i]), 0, &p, as_unicode, len, TRUE TSRMLS_CC); /* We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because later in this function there will be an advancement. */ p -= len; if (Z_TYPE_PP(current_field) == IS_LONG) { bit_area += 1 + sprintf((char *)start, MYSQLND_LLU_SPEC, (my_int64) Z_LVAL_PP(current_field)); #if PHP_MAJOR_VERSION >= 6 if (as_unicode) { ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0); } else #endif { ZVAL_STRINGL(*current_field, start, bit_area - start - 1, 0); } } else if (Z_TYPE_PP(current_field) == IS_STRING){ memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field)); bit_area += Z_STRLEN_PP(current_field); *bit_area++ = '\0'; zval_dtor(*current_field); #if PHP_MAJOR_VERSION >= 6 if (as_unicode) { ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0); } else #endif { ZVAL_STRINGL(*current_field, start, bit_area - start - 1, 0); } } /* IS_UNICODE should not be specially handled. In unicode mode the buffers are not pointed - everything is copied. */ } else #if PHP_MAJOR_VERSION < 6 { ZVAL_STRINGL(*current_field, (char *)p, len, 0); if (allocated == FALSE) { ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; } } #else /* Here we have to convert to UTF16, which means not reusing the buffer. Which in turn means that we can free the buffers once we have stored the result set, if we use store_result(). Also the destruction of the zvals should not call zval_copy_ctor() because then we will leak. I suppose we can use UG(unicode) in mysqlnd.c/mysqlnd_palloc.c when freeing a result set to check if we need to call copy_ctor(). XXX: Keep in mind that up there there is an open `else` in #ifdef MYSQLND_STRING_TO_INT_CONVERSION which will make with this `if` an `else if`. */ if ((perm_bind.is_possibly_blob == TRUE && packet->fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) || (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE)) { /* BLOB - no conversion please */ ZVAL_STRINGL(*current_field, (char *)p, len, 0); } else { ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0); } if (allocated == FALSE) { /* The zval cache will check and see that the type is IS_STRING. In this case it will call copy_ctor(). This is valid when allocated == TRUE . In this case we can't upcast. Thus for non-PS point_type doesn't matter much, as the valuable information is in the type of result set. Still good to set it. */ if (Z_TYPE_P(*current_field) == IS_STRING) { ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; } else { ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER; } } #endif p += len; last_field_was_string = TRUE; } } if (last_field_was_string) { /* Normal queries: The buffer has one more byte at the end, because we need it */ packet->row_buffer[data_size] = '\0'; } } /* }}} */ /* {{{ php_mysqlnd_rowp_read */ /* if normal statements => packet->fields is created by this function, if PS => packet->fields is passed from outside */ static enum_func_status php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) { MYSQLND_NET *net = &conn->net; zend_uchar *p; enum_func_status ret = PASS; size_t data_size = 0; size_t old_chunk_size = net->stream->chunk_size; php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet; size_t post_alloc_for_bit_fields = 0; if (!packet->binary_protocol && packet->bit_fields_count) { /* For every field we need terminating \0 */ post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count; } ret = php_mysqlnd_read_row_ex(conn, &packet->row_buffer, 0, &data_size, packet->persistent_alloc, post_alloc_for_bit_fields TSRMLS_CC); if (FAIL == ret) { goto end; } /* packet->row_buffer is of size 'data_size + 1' */ packet->header.size = data_size; if ((*(p = packet->row_buffer)) == 0xFF) { /* Error message as part of the result set, not good but we should not hang. See: Bug #27876 : SF with cyrillic variable name fails during execution */ ret = FAIL; php_mysqlnd_read_error_from_line(p + 1, data_size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate); } else if (*p == 0xFE && data_size < 8) { /* EOF */ packet->eof = TRUE; p++; if (data_size > 1) { packet->warning_count = uint2korr(p); p += 2; packet->server_status = uint2korr(p); /* Seems we have 3 bytes reserved for future use */ } } else { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_ROWS_FETCHED_FROM_SERVER); packet->eof = FALSE; /* packet->field_count is set by the user of the packet */ if (!packet->skip_extraction) { if (!packet->fields) { /* old-API will probably set packet->fields to NULL every time, though for unbuffered sets it makes not much sense as the zvals in this buffer matter, not the buffer. Constantly allocating and deallocating brings nothing. For PS - if stmt_store() is performed, thus we don't have a cursor, it will behave just like old-API buffered. Cursors will behave like a bit different, but mostly like old-API unbuffered and thus will populate this array with value. */ packet->fields = (zval **) pemalloc(packet->field_count * sizeof(zval *), packet->persistent_alloc); } if (packet->binary_protocol) { php_mysqlnd_rowp_read_binary_protocol(packet, conn, p, data_size TSRMLS_CC); } else { php_mysqlnd_rowp_read_text_protocol(packet, conn, p, data_size TSRMLS_CC); } } else { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_ROWS_SKIPPED); } } end: net->stream->chunk_size = old_chunk_size; return ret; } /* }}} */ /* {{{ php_mysqlnd_rowp_free_mem */ static void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_row *p= (php_mysql_packet_row *) _packet; if (p->row_buffer) { pefree(p->row_buffer, p->persistent_alloc); p->row_buffer = NULL; } /* Don't free packet->fields : - normal queries -> store_result() | fetch_row_unbuffered() will transfer the ownership and NULL it. - PS will pass in it the bound variables, we have to use them! and of course not free the array. As it is passed to us, we should not clean it ourselves. */ if (!alloca) { efree(p); } } /* }}} */ /* {{{ php_mysqlnd_stats_read */ static enum_func_status php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC) { zend_uchar buf[1024]; php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "statistics"); packet->message = emalloc(packet->header.size + 1); memcpy(packet->message, buf, packet->header.size); packet->message[packet->header.size] = '\0'; packet->message_len = packet->header.size; return PASS; } /* }}} */ /* {{{ php_mysqlnd_stats_free_mem */ static void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet; if (p->message) { efree(p->message); p->message = NULL; } if (!alloca) { efree(p); } } /* }}} */ /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */ #define PREPARE_RESPONSE_SIZE_41 9 #define PREPARE_RESPONSE_SIZE_50 12 /* {{{ php_mysqlnd_prepare_read */ static enum_func_status php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) { /* In case of an error, we should have place to put it */ zend_uchar buf[1024]; zend_uchar *p = buf; zend_uchar *begin = buf; unsigned int data_size; php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "prepare"); data_size = packet->header.size; packet->error_code = uint1korr(p); p++; if (0xFF == packet->error_code) { php_mysqlnd_read_error_from_line(p, data_size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate); return PASS; } if (data_size != PREPARE_RESPONSE_SIZE_41 && data_size != PREPARE_RESPONSE_SIZE_50 && !(data_size > PREPARE_RESPONSE_SIZE_50)) { php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size); return FAIL; } packet->stmt_id = uint4korr(p); p += 4; /* Number of columns in result set */ packet->field_count = uint2korr(p); p += 2; packet->param_count = uint2korr(p); p += 2; if (data_size > 9) { /* 0x0 filler sent by the server for 5.0+ clients */ p++; packet->warning_count= uint2korr(p); } #ifndef MYSQLND_SILENT php_printf("\tPrepare packet read: stmt_id=%d fields=%d params=%d\n", packet->stmt_id, packet->field_count, packet->param_count); #endif if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size); } return PASS; } /* }}} */ /* {{{ php_mysqlnd_prepare_free_mem */ static void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca) { php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet; if (!alloca) { efree(p); } } /* }}} */ /* {{{ php_mysqlnd_chg_user_read */ static enum_func_status php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) { /* There could be an error message */ zend_uchar buf[1024]; zend_uchar *p = buf; zend_uchar *begin = buf; php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet; PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "change user response "); /* Don't increment. First byte is 0xFF on error, but otherwise is starting byte of encoded sequence for length. */ /* Should be always 0x0 or 0xFF for error */ packet->field_count= uint1korr(p); p++; if (packet->header.size == 1 && buf[0] == 0xFE && packet->server_capabilities & CLIENT_SECURE_CONNECTION) { /* We don't handle 3.23 authentication */ return FAIL; } if (0xFF == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate); } if (p - begin > packet->header.size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size); } return PASS; } /* }}} */ /* {{{ php_mysqlnd_chg_user_free_mem */ static void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca) { if (!alloca) { efree(_packet); } } /* }}} */ /* {{{ packet_methods */ mysqlnd_packet_methods packet_methods[PROT_LAST] = { { sizeof(php_mysql_packet_greet), php_mysqlnd_greet_read, NULL, /* write */ php_mysqlnd_greet_free_mem, }, /* PROT_GREET_PACKET */ { sizeof(php_mysql_packet_auth), NULL, /* read */ php_mysqlnd_auth_write, php_mysqlnd_auth_free_mem, }, /* PROT_AUTH_PACKET */ { sizeof(php_mysql_packet_ok), php_mysqlnd_ok_read, /* read */ NULL, /* write */ php_mysqlnd_ok_free_mem, }, /* PROT_OK_PACKET */ { sizeof(php_mysql_packet_eof), php_mysqlnd_eof_read, /* read */ NULL, /* write */ php_mysqlnd_eof_free_mem, }, /* PROT_EOF_PACKET */ { sizeof(php_mysql_packet_command), NULL, /* read */ php_mysqlnd_cmd_write, /* write */ php_mysqlnd_cmd_free_mem, }, /* PROT_CMD_PACKET */ { sizeof(php_mysql_packet_rset_header), php_mysqlnd_rset_header_read, /* read */ NULL, /* write */ php_mysqlnd_rset_header_free_mem, }, /* PROT_RSET_HEADER_PACKET */ { sizeof(php_mysql_packet_res_field), php_mysqlnd_rset_field_read, /* read */ NULL, /* write */ php_mysqlnd_rset_field_free_mem, }, /* PROT_RSET_FLD_PACKET */ { sizeof(php_mysql_packet_row), php_mysqlnd_rowp_read, /* read */ NULL, /* write */ php_mysqlnd_rowp_free_mem, }, /* PROT_ROW_PACKET */ { sizeof(php_mysql_packet_stats), php_mysqlnd_stats_read, /* read */ NULL, /* write */ php_mysqlnd_stats_free_mem, }, /* PROT_STATS_PACKET */ { sizeof(php_mysql_packet_prepare_response), php_mysqlnd_prepare_read, /* read */ NULL, /* write */ php_mysqlnd_prepare_free_mem, }, /* PROT_PREPARE_RESP_PACKET */ { sizeof(php_mysql_packet_chg_user_resp), php_mysqlnd_chg_user_read, /* read */ NULL, /* write */ php_mysqlnd_chg_user_free_mem, } /* PROT_CHG_USER_PACKET */ }; /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_wireprotocol.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/mysqlnd_wireprotocol.h +++ php-src/ext/mysqlnd/mysqlnd_wireprotocol.h /* +----------------------------------------------------------------------+ | 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: mysqlnd_wireprotocol.h,v 1.1 2007/07/25 23:47:33 jani Exp $ */ #ifndef MYSQLND_WIREPROTOCOL_H #define MYSQLND_WIREPROTOCOL_H #define MYSQLND_HEADER_SIZE 4 #define MYSQLND_NULL_LENGTH (unsigned long) ~0 typedef zend_uchar mysqlnd_1b; typedef zend_ushort mysqlnd_2b; typedef zend_uint mysqlnd_4b; /* Packet handling */ #define PACKET_INIT(packet, enum_type, c_type) \ { \ packet = (c_type) ecalloc(1, packet_methods[enum_type].struct_size); \ ((c_type) (packet))->header.m = &packet_methods[enum_type]; \ } #define PACKET_WRITE(packet, conn) ((packet)->header.m->write_to_net((packet), (conn) TSRMLS_CC)) #define PACKET_READ(packet, conn) ((packet)->header.m->read_from_net((packet), (conn) TSRMLS_CC)) #define PACKET_FREE(packet) \ do { \ ((packet)->header.m->free_mem((packet), FALSE)); \ } while (0); #define PACKET_INIT_ALLOCA(packet, enum_type) \ { \ memset(&(packet), 0, packet_methods[enum_type].struct_size); \ (packet).header.m = &packet_methods[enum_type]; \ } #define PACKET_WRITE_ALLOCA(packet, conn) PACKET_WRITE(&(packet), (conn)) #define PACKET_READ_ALLOCA(packet, conn) PACKET_READ(&(packet), (conn)) #define PACKET_FREE_ALLOCA(packet) (packet.header.m->free_mem(&(packet), TRUE)) /* Enums */ enum php_mysql_packet_type { PROT_GREET_PACKET= 0, PROT_AUTH_PACKET, PROT_OK_PACKET, PROT_EOF_PACKET, PROT_CMD_PACKET, PROT_RSET_HEADER_PACKET, PROT_RSET_FLD_PACKET, PROT_ROW_PACKET, PROT_STATS_PACKET, PROT_PREPARE_RESP_PACKET, PROT_CHG_USER_PACKET, PROT_LAST, /* should always be last */ }; enum php_mysqlnd_server_command { COM_SLEEP = 0, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB, COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO, COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME = 15, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, COM_CONNECT_OUT = 20, COM_REGISTER_SLAVE, COM_STMT_PREPARE = 22, COM_STMT_EXECUTE = 23, COM_STMT_SEND_LONG_DATA = 24, COM_STMT_CLOSE = 25, COM_STMT_RESET = 26, COM_SET_OPTION = 27, COM_STMT_FETCH = 28, COM_DAEMON, COM_END }; extern const char * const mysqlnd_command_to_text[COM_END]; /* Low-level extraction functionality */ typedef struct st_mysqlnd_packet_methods { size_t struct_size; enum_func_status (*read_from_net)(void *packet, MYSQLND *conn TSRMLS_DC); size_t (*write_to_net)(void *packet, MYSQLND *conn TSRMLS_DC); void (*free_mem)(void *packet, zend_bool alloca); } mysqlnd_packet_methods; extern mysqlnd_packet_methods packet_methods[]; typedef struct st_mysqlnd_packet_header { size_t size; zend_uchar packet_no; mysqlnd_packet_methods *m; } mysqlnd_packet_header; /* Server greets the client */ typedef struct st_php_mysql_packet_greet { mysqlnd_packet_header header; mysqlnd_1b protocol_version; char *server_version; mysqlnd_4b thread_id; zend_uchar scramble_buf[SCRAMBLE_LENGTH]; /* 1 byte pad */ mysqlnd_2b server_capabilities; mysqlnd_1b charset_no; mysqlnd_2b server_status; /* 13 byte pad*/ zend_bool pre41; /* If error packet, we use these */ char error[MYSQLND_ERRMSG_SIZE+1]; char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; unsigned int error_no; } php_mysql_packet_greet; /* Client authenticates */ typedef struct st_php_mysql_packet_auth { mysqlnd_packet_header header; mysqlnd_4b client_flags; uint32 max_packet_size; mysqlnd_1b charset_no; /* 23 byte pad */ char *user; /* 8 byte scramble */ char *db; /* 12 byte scramble */ /* Here the packet ends. This is user supplied data */ char *password; /* +1 for \0 because of scramble() */ unsigned char *server_scramble_buf; size_t db_len; } php_mysql_packet_auth; /* OK packet */ typedef struct st_php_mysql_packet_ok { mysqlnd_packet_header header; mysqlnd_1b field_count; /* always 0x0 */ mynd_ulonglong affected_rows; mynd_ulonglong last_insert_id; mysqlnd_2b server_status; mysqlnd_2b warning_count; char *message; size_t message_len; /* If error packet, we use these */ char error[MYSQLND_ERRMSG_SIZE+1]; char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; unsigned int error_no; } php_mysql_packet_ok; /* Command packet */ typedef struct st_php_mysql_packet_command { mysqlnd_packet_header header; enum php_mysqlnd_server_command command; const char *argument; size_t arg_len; } php_mysql_packet_command; /* EOF packet */ typedef struct st_php_mysql_packet_eof { mysqlnd_packet_header header; mysqlnd_1b field_count; /* 0xFE */ mysqlnd_2b warning_count; mysqlnd_2b server_status; /* If error packet, we use these */ char error[MYSQLND_ERRMSG_SIZE+1]; char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; unsigned int error_no; } php_mysql_packet_eof; /* EOF packet */ /* Result Set header*/ typedef struct st_php_mysql_packet_rset_header { mysqlnd_packet_header header; /* 0x00 => ok ~0 => LOAD DATA LOCAL error_no != 0 => error others => result set -> Read res_field packets up to field_count */ unsigned long field_count; /* These are filled if no SELECT query. For SELECT warning_count and server status are in the last row packet, the EOF packet. */ mysqlnd_2b warning_count; mysqlnd_2b server_status; unsigned long long affected_rows; unsigned long long last_insert_id; /* This is for both LOAD DATA or info, when no result set */ char *info_or_local_file; size_t info_or_local_file_len; /* If error packet, we use these */ mysqlnd_error_info error_info; } php_mysql_packet_rset_header; /* Result set field packet */ typedef struct st_php_mysql_packet_res_field { mysqlnd_packet_header header; MYSQLND_FIELD *metadata; /* For table definitions, empty for result sets */ zend_bool skip_parsing; } php_mysql_packet_res_field; /* Row packet */ struct st_php_mysql_packet_row { mysqlnd_packet_header header; zval **fields; mysqlnd_4b field_count; zend_bool eof; /* These are, of course, only for SELECT in the EOF packet, which is detected by this packet */ mysqlnd_2b warning_count; mysqlnd_2b server_status; zend_uchar *row_buffer; zend_bool skip_extraction; zend_bool binary_protocol; zend_bool persistent_alloc; MYSQLND_FIELD *fields_metadata; /* We need this to alloc bigger bufs in non-PS mode */ unsigned int bit_fields_count; size_t bit_fields_total_len; /* trailing \0 not counted */ /* If error packet, we use these */ mysqlnd_error_info error_info; }; /* Statistics packet */ typedef struct st_php_mysql_packet_stats { mysqlnd_packet_header header; char *message; /* message_len is not part of the packet*/ size_t message_len; } php_mysql_packet_stats; /* COM_PREPARE response packet */ typedef struct st_php_mysql_packet_prepare_response { mysqlnd_packet_header header; /* also known as field_count 0x00=OK , 0xFF=error */ unsigned char error_code; unsigned long stmt_id; unsigned int field_count; unsigned int param_count; unsigned int warning_count; /* present in case of error */ mysqlnd_error_info error_info; } php_mysql_packet_prepare_response; /* Statistics packet */ typedef struct st_php_mysql_packet_chg_user_resp { mysqlnd_packet_header header; mysqlnd_4b field_count; /* message_len is not part of the packet*/ mysqlnd_2b server_capabilities; /* If error packet, we use these */ mysqlnd_error_info error_info; } php_mysql_packet_chg_user_resp; size_t mysqlnd_stream_write(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC); void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass); unsigned long php_mysqlnd_net_field_length(zend_uchar **packet); zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length); extern char * const mysqlnd_empty_string; #endif /* MYSQLND_WIREPROTOCOL_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/php_mysqlnd.h?view=markup&rev=1.1 Index: php-src/ext/mysqlnd/php_mysqlnd.h +++ php-src/ext/mysqlnd/php_mysqlnd.h /* +----------------------------------------------------------------------+ | 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: php_mysqlnd.h,v 1.1 2007/07/25 23:47:33 jani Exp $ */ #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 */
« previous php.cvs (#45605) next »