From ae1cd8e253ff1a38a07b7d6636cdc57bfe73b1b0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Johannes=20Schl=C3=BCter?= Date: Mon, 21 Jul 2008 13:05:51 +0000 Subject: [PATCH] - Add mysqlnd support for PDO_mysql, fixes at least bug#41997,#42499, pecl#12794, pecl#12401 # Running the tests: # (Note: Doesn't work currnetly on HEAD, see: # http://news.php.net/php.qa/64378) # # PDO_MYSQL_TEST_DSN - DSN # For example: mysql:dbname=test;host=localhost;port=3306 # # PDO_MYSQL_TEST_HOST - database host # PDO_MYSQL_TEST_DB - database (schema) name # PDO_MYSQL_TEST_SOCKET - database server socket # PDO_MYSQL_TEST_ENGINE - storage engine to use # PDO_MYSQL_TEST_USER - database user # PDO_MYSQL_TEST_PASS - database user password # PDO_MYSQL_TEST_CHARSET - database charset # # NOTE: if any of PDO_MYSQL_TEST_[HOST|DB|SOCKET|ENGINE|CHARSET] is # part of PDO_MYSQL_TEST_DSN, the values must match. That is, for example, # for PDO_MYSQL_TEST_DSN = mysql:dbname=test you MUST set PDO_MYSQL_TEST_DB=test. --- ext/pdo/pdo_stmt.c | 19 +- ext/pdo/php_pdo_driver.h | 9 +- ext/pdo_mysql/CREDITS | 6 +- ext/pdo_mysql/config.m4 | 192 +++-- ext/pdo_mysql/config.w32 | 14 +- ext/pdo_mysql/mysql_driver.c | 366 +++++++-- ext/pdo_mysql/mysql_statement.c | 732 +++++++++++++----- ext/pdo_mysql/pdo_mysql.c | 226 ++++-- ext/pdo_mysql/php_pdo_mysql.h | 11 +- ext/pdo_mysql/php_pdo_mysql_int.h | 106 ++- ext/pdo_mysql/tests/README | 16 + ext/pdo_mysql/tests/bug_33689.phpt | 26 +- ext/pdo_mysql/tests/bug_37445.phpt | 4 +- ext/pdo_mysql/tests/bug_39483.phpt | Bin 821 -> 756 bytes ext/pdo_mysql/tests/bug_39858.phpt | 96 +++ ext/pdo_mysql/tests/bug_41125.phpt | 44 ++ ext/pdo_mysql/tests/bug_41698.phpt | 37 + ext/pdo_mysql/tests/bug_41997.phpt | 64 ++ ext/pdo_mysql/tests/bug_42499.phpt | 80 ++ ext/pdo_mysql/tests/bug_43371.phpt | 20 + ext/pdo_mysql/tests/bug_44454.phpt | 104 +++ ext/pdo_mysql/tests/bug_44707.phpt | 92 +++ ext/pdo_mysql/tests/bug_45120.phpt | 50 ++ ext/pdo_mysql/tests/bug_pecl_12925.phpt | 62 ++ ext/pdo_mysql/tests/bug_pecl_7976.phpt | 87 +++ ext/pdo_mysql/tests/config.inc | 53 +- ext/pdo_mysql/tests/last_insert_id.phpt | 4 +- ext/pdo_mysql/tests/mysql_pdo_test.inc | 162 ++++ .../tests/pdo_mysql___construct.phpt | 303 ++++++++ .../tests/pdo_mysql___construct_ini.phpt | 56 ++ .../tests/pdo_mysql___construct_options.phpt | 170 ++++ ...do_mysql___construct_options_libmysql.phpt | 88 +++ .../tests/pdo_mysql___construct_uri.phpt | 75 ++ .../tests/pdo_mysql_attr_autocommit.phpt | 93 +++ ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt | 219 ++++++ .../tests/pdo_mysql_attr_client_version.phpt | 36 + .../pdo_mysql_attr_connection_status.phpt | 36 + .../tests/pdo_mysql_attr_driver_name.phpt | 30 + .../tests/pdo_mysql_attr_errmode.phpt | 166 ++++ .../pdo_mysql_attr_fetch_table_names.phpt | 41 + .../tests/pdo_mysql_attr_init_command.phpt | 51 ++ .../tests/pdo_mysql_attr_max_buffer_size.phpt | 70 ++ .../tests/pdo_mysql_attr_oracle_nulls.phpt | 120 +++ .../tests/pdo_mysql_attr_prefetch.phpt | 21 + .../tests/pdo_mysql_attr_server_info.phpt | 48 ++ .../tests/pdo_mysql_attr_server_version.phpt | 64 ++ .../tests/pdo_mysql_attr_statement_class.phpt | 154 ++++ .../tests/pdo_mysql_begintransaction.phpt | 201 +++++ ext/pdo_mysql/tests/pdo_mysql_bit.phpt | 59 ++ .../tests/pdo_mysql_class_constants.phpt | 78 ++ ext/pdo_mysql/tests/pdo_mysql_commit.phpt | 84 ++ ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt | 81 ++ ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt | 173 +++++ ext/pdo_mysql/tests/pdo_mysql_exec.phpt | 179 +++++ ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt | 88 +++ .../tests/pdo_mysql_exec_load_data.phpt | 102 +++ .../tests/pdo_mysql_exec_select.phpt | 59 ++ ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt | 88 +++ .../tests/pdo_mysql_get_attribute.phpt | 101 +++ ext/pdo_mysql/tests/pdo_mysql_interface.phpt | 58 ++ .../tests/pdo_mysql_last_insert_id.phpt | 114 +++ ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt | 101 +++ ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt | 34 + .../tests/pdo_mysql_prepare_emulated.phpt | 414 ++++++++++ .../pdo_mysql_prepare_emulated_anonymous.phpt | 78 ++ ...epare_emulated_placeholder_everywhere.phpt | 75 ++ .../pdo_mysql_prepare_match_against.phpt | 48 ++ .../tests/pdo_mysql_prepare_native.phpt | 380 +++++++++ .../pdo_mysql_prepare_native_clear_error.phpt | 91 +++ .../pdo_mysql_prepare_native_column.phpt | 44 ++ ..._prepare_native_dup_named_placeholder.phpt | 136 ++++ .../pdo_mysql_prepare_native_mixed_style.phpt | 35 + ...ysql_prepare_native_named_placeholder.phpt | 85 ++ ...prepare_native_placeholder_everywhere.phpt | 85 ++ ext/pdo_mysql/tests/pdo_mysql_rollback.phpt | 90 +++ .../tests/pdo_mysql_stmt_bindcolumn.phpt | 110 +++ .../tests/pdo_mysql_stmt_bindparam.phpt | 155 ++++ .../tests/pdo_mysql_stmt_bindparam_types.phpt | 169 ++++ .../tests/pdo_mysql_stmt_bindvalue.phpt | 332 ++++++++ .../tests/pdo_mysql_stmt_blobfromsteam.phpt | 143 ++++ ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt | 91 +++ .../tests/pdo_mysql_stmt_closecursor.phpt | 172 ++++ .../pdo_mysql_stmt_closecursor_empty.phpt | 69 ++ .../tests/pdo_mysql_stmt_columncount.phpt | 65 ++ .../tests/pdo_mysql_stmt_errorcode.phpt | 61 ++ .../tests/pdo_mysql_stmt_errorinfo.phpt | 119 +++ .../pdo_mysql_stmt_fetch_non_select.phpt | 187 +++++ .../tests/pdo_mysql_stmt_fetch_serialize.phpt | 151 ++++ ...pdo_mysql_stmt_fetch_serialize_simple.phpt | 98 +++ .../tests/pdo_mysql_stmt_fetchobject.phpt | 95 +++ .../tests/pdo_mysql_stmt_getcolumnmeta.phpt | 309 ++++++++ .../tests/pdo_mysql_stmt_multiquery.phpt | 90 +++ .../tests/pdo_mysql_stmt_nextrowset.phpt | 309 ++++++++ .../tests/pdo_mysql_stmt_rowcount.phpt | 32 + .../tests/pdo_mysql_stmt_unbuffered_2050.phpt | 180 +++++ .../pdo_mysql_stmt_variable_columncount.phpt | 122 +++ ext/pdo_mysql/tests/pdo_mysql_subclass.phpt | 101 +++ ext/pdo_mysql/tests/pdo_mysql_types.phpt | 174 +++++ .../tests/pdo_mysql_types_zerofill.phpt | 117 +++ ext/pdo_mysql/tests/pecl_bug_5200.phpt | 30 + ext/pdo_mysql/tests/pecl_bug_5780.phpt | 39 + ext/pdo_mysql/tests/pecl_bug_5802.phpt | 52 ++ ext/pdo_mysql/tests/show_tables.phpt | 8 +- ext/pdo_mysql/tests/skipif.inc | 7 + ext/pdo_mysql/tests/table.inc | 9 + 105 files changed, 10847 insertions(+), 433 deletions(-) create mode 100644 ext/pdo_mysql/tests/README create mode 100644 ext/pdo_mysql/tests/bug_39858.phpt create mode 100644 ext/pdo_mysql/tests/bug_41125.phpt create mode 100644 ext/pdo_mysql/tests/bug_41698.phpt create mode 100644 ext/pdo_mysql/tests/bug_41997.phpt create mode 100644 ext/pdo_mysql/tests/bug_42499.phpt create mode 100644 ext/pdo_mysql/tests/bug_43371.phpt create mode 100644 ext/pdo_mysql/tests/bug_44454.phpt create mode 100644 ext/pdo_mysql/tests/bug_44707.phpt create mode 100644 ext/pdo_mysql/tests/bug_45120.phpt create mode 100644 ext/pdo_mysql/tests/bug_pecl_12925.phpt create mode 100644 ext/pdo_mysql/tests/bug_pecl_7976.phpt create mode 100644 ext/pdo_mysql/tests/mysql_pdo_test.inc create mode 100644 ext/pdo_mysql/tests/pdo_mysql___construct.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_bit.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_commit.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_exec.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_interface.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_rollback.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_subclass.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_types.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt create mode 100644 ext/pdo_mysql/tests/pecl_bug_5200.phpt create mode 100644 ext/pdo_mysql/tests/pecl_bug_5780.phpt create mode 100644 ext/pdo_mysql/tests/pecl_bug_5802.phpt create mode 100755 ext/pdo_mysql/tests/skipif.inc create mode 100644 ext/pdo_mysql/tests/table.inc diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index c818d4c73b..980cb8570d 100755 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -538,6 +538,20 @@ static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, int *typ stmt->methods->get_col(stmt, colno, &value, &value_len, &caller_frees TSRMLS_CC); switch (type) { + case PDO_PARAM_ZVAL: + if (value && value_len == sizeof(zval)) { + int need_copy = (new_type != PDO_PARAM_ZVAL || stmt->dbh->stringify) ? 1 : 0; + zval *zv = *(zval**)value; + ZVAL_ZVAL(dest, zv, need_copy, 1); + } else { + ZVAL_NULL(dest); + } + + if (Z_TYPE_P(dest) == IS_NULL) { + type = new_type; + } + break; + case PDO_PARAM_INT: if (value && value_len == sizeof(long)) { ZVAL_LONG(dest, *(long*)value); @@ -1879,7 +1893,10 @@ static PHP_METHOD(PDOStatement, getColumnMeta) add_assoc_string(return_value, "name", col->name, 1); add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */ add_assoc_long(return_value, "precision", col->precision); - add_assoc_long(return_value, "pdo_type", col->param_type); + if (col->param_type != PDO_PARAM_ZVAL) { + /* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */ + add_assoc_long(return_value, "pdo_type", col->param_type); + } } /* }}} */ diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 2341eaede4..0898f763ec 100755 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -45,7 +45,7 @@ PDO_API char *php_pdo_int64_to_str(pdo_int64_t i64 TSRMLS_DC); # define FALSE 0 #endif -#define PDO_DRIVER_API 20061209 +#define PDO_DRIVER_API 20080721 enum pdo_param_type { PDO_PARAM_NULL, @@ -68,7 +68,12 @@ enum pdo_param_type { PDO_PARAM_STMT, /* hierarchical result set */ /* get_col ptr should point to a zend_bool */ - PDO_PARAM_BOOL + PDO_PARAM_BOOL, + + /* get_col ptr should point to a zval* + and the driver is responsible for adding correct type information to get_column_meta() + */ + PDO_PARAM_ZVAL }; /* magic flag to denote a parameter as being input/output */ diff --git a/ext/pdo_mysql/CREDITS b/ext/pdo_mysql/CREDITS index 0bee1ee76e..0423cccd64 100755 --- a/ext/pdo_mysql/CREDITS +++ b/ext/pdo_mysql/CREDITS @@ -1,3 +1,3 @@ -mySQL driver for PDO -George Schlossnagle, Wez Furlong, Ilia Alshanetsky - +MySQL driver for PDO +George Schlossnagle, Wez Furlong, Ilia Alshanetsky, Johannes Schlueter + diff --git a/ext/pdo_mysql/config.m4 b/ext/pdo_mysql/config.m4 index 99c68bc9ba..99f9491a2a 100755 --- a/ext/pdo_mysql/config.m4 +++ b/ext/pdo_mysql/config.m4 @@ -2,68 +2,143 @@ dnl dnl $Id$ dnl -if test "$PHP_PDO" != "no"; then - -AC_DEFUN([PDO_MYSQL_LIB_CHK], [ - str="$PDO_MYSQL_DIR/$1/libmysqlclient.*" - for j in `echo $str`; do - if test -r $j; then - PDO_MYSQL_LIB_DIR=$MYSQL_DIR/$1 - break 2 - fi - done -]) - +dnl TODO Rename when moving to pdo_mysql PHP_ARG_WITH(pdo-mysql, for MySQL support for PDO, -[ --with-pdo-mysql[=DIR] PDO: MySQL support. DIR is the MySQL base directory]) +[ --with-pdo-mysql[=DIR] PDO: MySQL support. DIR is the MySQL base directoy + If mysqlnd is passed as DIR, the MySQL native + native driver will be used [/usr/local]]) + +if test -z "$PHP_ZLIB_DIR"; then + PHP_ARG_WITH(zlib-dir, for the location of libz, + [ --with-zlib-dir[=DIR] PDO_MySQL: Set the path to libz install prefix], no, no) +fi if test "$PHP_PDO_MYSQL" != "no"; then - AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL]) + PHP_MYSQLND_ENABLED=yes - for i in $PHP_PDO_MYSQL /usr/local /usr ; do - PDO_MYSQL_DIR=$i - PDO_MYSQL_CONFIG=$PDO_MYSQL_DIR/bin/mysql_config - if test -r $i/include/mysql; then - PDO_MYSQL_INC_DIR=$i/include/mysql - else - PDO_MYSQL_INC_DIR=$i/include - fi - if test -r $i/lib/mysql; then - PDO_MYSQL_LIB_DIR=$i/lib/mysql + AC_DEFUN([PDO_MYSQL_LIB_CHK], [ + str="$PDO_MYSQL_DIR/$1/libmysqlclient*" + for j in `echo $str`; do + if test -r $j; then + PDO_MYSQL_LIB_DIR=$PDO_MYSQL_DIR/$1 + break 2 + fi + done + ]) + + if test "$PHP_PDO_MYSQL" = "mysqlnd"; then + dnl enables build of mysqnd library + PHP_MYSQL_ENABLED=yes + AC_DEFINE([PDO_USE_MYSQLND], 1, [Whether pdo_mysql uses mysqlnd]) + else + AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL]) + AC_MSG_CHECKING([for mysql_config]) + + if test -f $PHP_PDO_MYSQL && test -x $PHP_PDO_MYSQL ; then + PDO_MYSQL_CONFIG=$PHP_PDO_MYSQL + elif test "$PHP_PDO_MYSQL" != "yes"; then + if test -d "$PHP_PDO_MYSQL" ; then + if test -x "$PHP_PDO_MYSQL/bin/mysql_config" ; then + PDO_MYSQL_CONFIG="$PHP_PDO_MYSQL/bin/mysql_config" + else + PDO_MYSQL_DIR="$PHP_PDO_MYSQL" + fi + else + AC_MSG_RESULT([$PHP_PDO_MYSQL is not a directory]) + AC_MSG_ERROR([can not find mysql under the "$PHP_PDO_MYSQL" that you specified]) + fi else - PDO_MYSQL_LIB_DIR=$i/lib - fi - if test -x $PDO_MYSQL_CONFIG; then - break + for i in /usr/local /usr ; do + if test -x "$i/bin/mysql_config" ; then + PDO_MYSQL_CONFIG="$i/bin/mysql_config" + break; + fi + if test -r $i/include/mysql/mysql.h || test -r $i/include/mysql.h ; then + PDO_MYSQL_DIR="$i" + break; + fi + done fi - done - if test -z "$PDO_MYSQL_DIR"; then - AC_MSG_ERROR([Cannot find MySQL header files under $PHP_MYSQL. -Note that the MySQL client library is not bundled anymore!]) - fi + if test -n "$PDO_MYSQL_CONFIG" && test -x "$PDO_MYSQL_CONFIG" ; then + AC_MSG_RESULT($PDO_MYSQL_CONFIG) + if test "x$SED" = "x"; then + AC_PATH_PROG(SED, sed) + fi - if test -x $PDO_MYSQL_CONFIG; then - PDO_MYSQL_SOCKET=`$PDO_MYSQL_CONFIG --socket` - fi + if test "$enable_maintainer_zts" = "yes"; then + PDO_MYSQL_LIBNAME=mysqlclient_r + PDO_MYSQL_LIBS=`$PDO_MYSQL_CONFIG --libs_r | $SED -e "s/'//g"` + else + PDO_MYSQL_LIBNAME=mysqlclient + PDO_MYSQL_LIBS=`$PDO_MYSQL_CONFIG --libs | $SED -e "s/'//g"` + fi + PDO_MYSQL_INCLUDE=`$PDO_MYSQL_CONFIG --cflags | $SED -e "s/'//g"` + PDO_MYSQL_SOCKET=`$PDO_MYSQL_CONFIG --socket` + elif test -z "$PDO_MYSQL_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Cannot find MySQL header files under $PDO_MYSQL_DIR]) + else + AC_MSG_RESULT([not found]) + AC_MSG_CHECKING([for mysql install under $PDO_MYSQL_DIR]) + if test -r $PDO_MYSQL_DIR/include/mysql; then + PDO_MYSQL_INC_DIR=$PDO_MYSQL_DIR/include/mysql + else + PDO_MYSQL_INC_DIR=$PDO_MYSQL_DIR/include + fi + if test -r $PDO_MYSQL_DIR/$PHP_LIBDIR/mysql; then + PDO_MYSQL_LIB_DIR=$PDO_MYSQL_DIR/$PHP_LIBDIR/mysql + else + PDO_MYSQL_LIB_DIR=$PDO_MYSQL_DIR/$PHP_LIBDIR + fi + + if test -r "$PDO_MYSQL_LIB_DIR"; then + AC_MSG_RESULT([libs under $PDO_MYSQL_LIB_DIR; seems promising]) + else + AC_MSG_RESULT([can not find it]) + AC_MSG_ERROR([Unable to find your mysql installation]) + fi + + PHP_ADD_INCLUDE($PDO_MYSQL_INC_DIR) + PDO_MYSQL_INCLUDE=-I$PDO_MYSQL_INC_DIR + fi - if test "$PHP_MYSQL_SOCK" != "no" && test "$PHP_MYSQL_SOCK" != "yes"; then - AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PHP_MYSQL_SOCK", [ ]) - else AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ]) - fi - PHP_ADD_LIBRARY_WITH_PATH(mysqlclient, $PDO_MYSQL_LIB_DIR, PDO_MYSQL_SHARED_LIBADD) - PHP_ADD_INCLUDE($PDO_MYSQL_INC_DIR) - if test -x $PDO_MYSQL_CONFIG; then - PDO_MYSQL_LIBS=`$PDO_MYSQL_CONFIG --libs` - PHP_SUBST_OLD(PDO_MYSQL_LIBS) - fi + PHP_CHECK_LIBRARY($PDO_MYSQL_LIBNAME, mysql_query, + [ + PHP_EVAL_INCLINE($PDO_MYSQL_INCLUDE) + PHP_EVAL_LIBLINE($PDO_MYSQL_LIBS, PDO_MYSQL_SHARED_LIBADD) + ],[ + if test "$PHP_ZLIB_DIR" != "no"; then + PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR, PDO_MYSQL_SHARED_LIBADD) + PHP_CHECK_LIBRARY($PDO_MYSQL_LIBNAME, mysql_query, [], [ + AC_MSG_ERROR([PDO_MYSQL configure failed. Please check config.log for more information.]) + ], [ + -L$PHP_ZLIB_DIR/$PHP_LIBDIR -L$PDO_MYSQL_LIB_DIR + ]) + PDO_MYSQL_LIBS="$PDO_MYSQL_LIBS -L$PHP_ZLIB_DIR/$PHP_LIBDIR -lz" + else + PHP_ADD_LIBRARY(z,, PDO_MYSQL_SHARED_LIBADD) + PHP_CHECK_LIBRARY($PDO_MYSQL_LIBNAME, mysql_query, [], [ + AC_MSG_ERROR([Try adding --with-zlib-dir=. Please check config.log for more information.]) + ], [ + -L$PDO_MYSQL_LIB_DIR + ]) + PDO_MYSQL_LIBS="$PDO_MYSQL_LIBS -lz" + fi - _SAVE_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS $PDO_MYSQL_LIBS" - AC_CHECK_FUNCS([mysql_commit mysql_stmt_prepare mysql_next_result mysql_sqlstate]) - LDFLAGS=$_SAVE_LDFLAGS + PHP_EVAL_INCLINE($PDO_MYSQL_INCLUDE) + PHP_EVAL_LIBLINE($PDO_MYSQL_LIBS, PDO_MYSQL_SHARED_LIBADD) + ],[ + $PDO_MYSQL_LIBS + ]) + + _SAVE_LIBS=$LIBS + LIBS="$LIBS $PDO_MYSQL_LIBS" + AC_CHECK_FUNCS([mysql_commit mysql_stmt_prepare mysql_next_result mysql_sqlstate]) + LIBS=$_SAVE_LIBS + fi ifdef([PHP_CHECK_PDO_INCLUDES], [ @@ -82,18 +157,19 @@ Note that the MySQL client library is not bundled anymore!]) AC_MSG_RESULT($pdo_inc_path) ]) - PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_inc_path) + + dnl fix after renaming to pdo_mysql + PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_inc_path -I) ifdef([PHP_ADD_EXTENSION_DEP], [ - PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo) + PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo) + if test "$PHP_MYSQL" = "mysqlnd"; then + PHP_ADD_EXTENSION_DEP(pdo_mysql, mysqlnd) + fi ]) PDO_MYSQL_MODULE_TYPE=external - PDO_MYSQL_INCLUDE=-I$PDO_MYSQL_INC_DIR - - PHP_SUBST(PDO_MYSQL_SHARED_LIBADD) + PHP_SUBST_OLD(PDO_MYSQL_MODULE_TYPE) - PHP_SUBST_OLD(PDO_MYSQL_LIBS) - PHP_SUBST_OLD(PDO_MYSQL_INCLUDE) fi -fi +dnl vim: se ts=2 sw=2 et: diff --git a/ext/pdo_mysql/config.w32 b/ext/pdo_mysql/config.w32 index 1fce9e5dbf..da085dc569 100644 --- a/ext/pdo_mysql/config.w32 +++ b/ext/pdo_mysql/config.w32 @@ -4,11 +4,17 @@ ARG_WITH("pdo-mysql", "MySQL support for PDO", "no"); if (PHP_PDO_MYSQL != "no") { - if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) && - CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL", PHP_PHP_BUILD + "\\include\\mysql;" + PHP_PDO_MYSQL)) { + if (PHP_PDO_MYSQL == "yes" || PHP_PDO_MYSQL == "mysqlnd") { + AC_DEFINE('PDO_USE_MYSQLND', 1, 'Using MySQL native driver'); + STDOUT.WriteLine("INFO: mysqlnd build"); EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c"); + ADD_EXTENSION_DEP('pdo_mysql', 'pdo'); } else { - WARNING("pdo_mysql not enabled; libraries and headers not found"); + if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) && + CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL", PHP_PHP_BUILD + "\\include\\mysql;" + PHP_PDO_MYSQL)) { + EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c"); + } else { + WARNING("pdo_mysql not enabled; libraries and headers not found"); + } } - ADD_EXTENSION_DEP('pdo_mysql', 'pdo'); } diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index 81ea1d3657..941ab4b532 100755 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Author: George Schlossnagle | | Wez Furlong | + | Johannes Schlueter | +----------------------------------------------------------------------+ */ @@ -30,18 +31,29 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#ifndef PDO_USE_MYSQLND #include +#endif #include "zend_exceptions.h" +#if PDO_USE_MYSQLND +# define pdo_mysql_init(persistent) mysqlnd_init(persistent) +#else +# define pdo_mysql_init(persistent) mysql_init(NULL) +#endif -const char *pdo_mysql_get_sqlstate(unsigned int my_errno) { +#if !HAVE_MYSQL_SQLSTATE && !PDO_USE_MYSQLND +static const char *pdo_mysql_get_sqlstate(unsigned int my_errno) { /* {{{ */ switch (my_errno) { /* import auto-generated case: code */ #include "php_pdo_mysql_sqlstate.h" default: return "HY000"; } } +/* }}} */ +#endif +/* {{{ _pdo_mysql_error */ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line TSRMLS_DC) /* {{{ */ { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; @@ -49,6 +61,8 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin pdo_mysql_error_info *einfo; pdo_mysql_stmt *S = NULL; + PDO_DBG_ENTER("_pdo_mysql_error"); + PDO_DBG_INF_FMT("file=%s line=%d", file, line); if (stmt) { S = (pdo_mysql_stmt*)stmt->driver_data; pdo_err = &stmt->error_code; @@ -58,7 +72,16 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin einfo = &H->einfo; } - einfo->errcode = mysql_errno(H->server); +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND + if (S && S->stmt) { + einfo->errcode = mysql_stmt_errno(S->stmt); + } + else +#endif + { + einfo->errcode = mysql_errno(H->server); + } + einfo->file = file; einfo->line = line; @@ -68,23 +91,29 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin } if (einfo->errcode) { - if (2014 != einfo->errcode) { - einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent); - } else { + if (einfo->errcode == 2014) { einfo->errmsg = pestrdup( "Cannot execute queries while other unbuffered queries are active. " "Consider using PDOStatement::fetchAll(). Alternatively, if your code " "is only ever going to run against mysql, you may enable query " - "buffering by setting the PDO_MYSQL_ATTR_USE_BUFFERED_QUERY attribute.", + "buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.", dbh->is_persistent); + } else if (einfo->errcode == 2057) { + einfo->errmsg = pestrdup( + "A stored procedure returning result sets of different size was called. " + "This is not supported by libmysql", + dbh->is_persistent); + + } else { + einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent); } } else { /* no error */ strcpy(*pdo_err, PDO_ERR_NONE); - return 0; + PDO_DBG_RETURN(0); } -#if HAVE_MYSQL_SQLSTATE -# if HAVE_MYSQL_STMT_PREPARE +#if HAVE_MYSQL_SQLSTATE || PDO_USE_MYSQLND +# if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND if (S && S->stmt) { strcpy(*pdo_err, mysql_stmt_sqlstate(S->stmt)); } else @@ -97,25 +126,23 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin #endif if (!dbh->methods) { -#if PHP_VERSION_ID > 50200 + PDO_DBG_INF("Throwing exception"); zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s", *pdo_err, einfo->errcode, einfo->errmsg); -#else - zend_throw_exception_ex(php_pdo_get_exception(TSRMLS_C), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s", - *pdo_err, einfo->errcode, einfo->errmsg); -#endif } -/* printf("** [%s:%d] %s %s\n", file, line, *pdo_err, einfo->errmsg); */ - return einfo->errcode; + PDO_DBG_RETURN(einfo->errcode); } /* }}} */ +/* {{{ pdo_mysql_fetch_error_func */ static int pdo_mysql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; pdo_mysql_error_info *einfo = &H->einfo; + PDO_DBG_ENTER("pdo_mysql_fetch_error_func"); + PDO_DBG_INF_FMT("dbh=%p stmt=%p", dbh, stmt); if (stmt) { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; einfo = &S->einfo; @@ -128,13 +155,17 @@ static int pdo_mysql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *in add_next_index_string(info, einfo->errmsg, 1); } - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ +/* {{{ mysql_handle_closer */ static int mysql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; + PDO_DBG_ENTER("mysql_handle_closer"); + PDO_DBG_INF_FMT("dbh=%p", dbh); if (H) { if (H->server) { mysql_close(H->server); @@ -147,20 +178,26 @@ static int mysql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ pefree(H, dbh->is_persistent); dbh->driver_data = NULL; } - return 0; + PDO_DBG_RETURN(0); } /* }}} */ +/* {{{ mysql_handle_preparer */ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; pdo_mysql_stmt *S = ecalloc(1, sizeof(pdo_mysql_stmt)); -#if HAVE_MYSQL_STMT_PREPARE +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND char *nsql = NULL; int nsql_len = 0; int ret; + int server_version; #endif + PDO_DBG_ENTER("mysql_handle_preparer"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("sql=%.*s", sql_len, sql); + S->H = H; stmt->driver_data = S; stmt->methods = &mysql_stmt_methods; @@ -169,11 +206,11 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, goto end; } - /* TODO: add runtime check to determine if the server we are talking to supports - * prepared statements; if it doesn't, we should set stmt->supports_placeholders - * to PDO_PLACEHOLDER_NONE, and have the rest of the code look at S->stmt to - * determine if we're using real prepared statements or the PDO emulated version */ -#if HAVE_MYSQL_STMT_PREPARE +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND + server_version = mysql_get_server_version(H->server); + if (server_version < 40100) { + goto fallback; + } stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); @@ -184,7 +221,7 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, } else if (ret == -1) { /* failed to parse */ strcpy(dbh->error_code, stmt->error_code); - return 0; + PDO_DBG_RETURN(0); } if (!(S->stmt = mysql_stmt_init(H->server))) { @@ -192,7 +229,7 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, if (nsql) { efree(nsql); } - return 0; + PDO_DBG_RETURN(0); } if (mysql_stmt_prepare(S->stmt, sql, sql_len)) { @@ -208,7 +245,7 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, if (nsql) { efree(nsql); } - return 0; + PDO_DBG_RETURN(0); } if (nsql) { efree(nsql); @@ -217,114 +254,199 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, S->num_params = mysql_stmt_param_count(S->stmt); if (S->num_params) { + S->params_given = 0; +#if PDO_USE_MYSQLND + S->params = NULL; +#else S->params = ecalloc(S->num_params, sizeof(MYSQL_BIND)); S->in_null = ecalloc(S->num_params, sizeof(my_bool)); S->in_length = ecalloc(S->num_params, sizeof(unsigned long)); +#endif } - dbh->alloc_own_columns = 1; S->max_length = pdo_attr_lval(driver_options, PDO_ATTR_MAX_COLUMN_LEN, 0 TSRMLS_CC); - return 1; + PDO_DBG_RETURN(1); fallback: #endif end: stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ +/* {{{ mysql_handle_doer */ static long mysql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; + PDO_DBG_ENTER("mysql_handle_doer"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("sql=%.*s", sql_len, sql); if (mysql_real_query(H->server, sql, sql_len)) { pdo_mysql_error(dbh); - return -1; + PDO_DBG_RETURN(-1); } else { my_ulonglong c = mysql_affected_rows(H->server); if (c == (my_ulonglong) -1) { pdo_mysql_error(dbh); - return (H->einfo.errcode ? -1 : 0); + PDO_DBG_RETURN(H->einfo.errcode ? -1 : 0); } else { - return c; + +#if HAVE_MYSQL_NEXT_RESULT || PDO_USE_MYSQLND + /* MULTI_QUERY support - eat up all unfetched result sets */ + MYSQL_RES* result; + while (mysql_more_results(H->server)) { + if (mysql_next_result(H->server)) { + PDO_DBG_RETURN(1); + } + result = mysql_store_result(H->server); + if (result) { + mysql_free_result(result); + } + } +#endif + PDO_DBG_RETURN((int)c); } } } +/* }}} */ +/* {{{ pdo_mysql_last_insert_id */ static char *pdo_mysql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; char *id = php_pdo_int64_to_str(mysql_insert_id(H->server) TSRMLS_CC); + PDO_DBG_ENTER("pdo_mysql_last_insert_id"); *len = strlen(id); - return id; + PDO_DBG_RETURN(id); } +/* {{{ mysql_handle_quoter */ static int mysql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; + PDO_DBG_ENTER("mysql_handle_quoter"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("unquoted=%.*s", unquotedlen, unquoted); *quoted = safe_emalloc(2, unquotedlen, 3); *quotedlen = mysql_real_escape_string(H->server, *quoted + 1, unquoted, unquotedlen); (*quoted)[0] =(*quoted)[++*quotedlen] = '\''; (*quoted)[++*quotedlen] = '\0'; - return 1; + PDO_DBG_INF_FMT("quoted=%.*s", *quotedlen, *quoted); + PDO_DBG_RETURN(1); } +/* }}} */ +/* {{{ mysql_handle_begin */ static int mysql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC) { - return 0 <= mysql_handle_doer(dbh, ZEND_STRL("START TRANSACTION") TSRMLS_CC); + PDO_DBG_ENTER("mysql_handle_quoter"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("START TRANSACTION") TSRMLS_CC)); } +/* }}} */ +/* {{{ mysql_handle_commit */ static int mysql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) { - return 0 <= mysql_handle_doer(dbh, ZEND_STRL("COMMIT") TSRMLS_CC); + PDO_DBG_ENTER("mysql_handle_commit"); + PDO_DBG_INF_FMT("dbh=%p", dbh); +#if MYSQL_VERSION_ID >= 40100 || defined(PDO_USE_MYSQLND) + PDO_DBG_RETURN(0 <= mysql_commit(((pdo_mysql_db_handle *)dbh->driver_data)->server)); +#else + PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("COMMIT") TSRMLS_CC)); +#endif } +/* {{{ mysql_handle_rollback */ static int mysql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) { - return 0 <= mysql_handle_doer(dbh, ZEND_STRL("ROLLBACK") TSRMLS_CC); + PDO_DBG_ENTER("mysql_handle_rollback"); + PDO_DBG_INF_FMT("dbh=%p", dbh); +#if MYSQL_VERSION_ID >= 40100 || defined(PDO_USE_MYSQLND) + PDO_DBG_RETURN(0 <= mysql_rollback(((pdo_mysql_db_handle *)dbh->driver_data)->server)); +#else + PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("ROLLBACK") TSRMLS_CC)); +#endif } +/* }}} */ -static int mysql_handle_autocommit(pdo_dbh_t *dbh TSRMLS_DC) +/* {{{ mysql_handle_autocommit */ +static inline int mysql_handle_autocommit(pdo_dbh_t *dbh TSRMLS_DC) { + PDO_DBG_ENTER("mysql_handle_autocommit"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("dbh->autocommit=%d", dbh->auto_commit); +#if MYSQL_VERSION_ID >= 40100 || defined(PDO_USE_MYSQLND) + PDO_DBG_RETURN(0 <= mysql_autocommit(((pdo_mysql_db_handle *)dbh->driver_data)->server, dbh->auto_commit)); +#else if (dbh->auto_commit) { - return 0 <= mysql_handle_doer(dbh, ZEND_STRL("SET AUTOCOMMIT=1") TSRMLS_CC); + PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("SET AUTOCOMMIT=1") TSRMLS_CC)); } else { - return 0 <= mysql_handle_doer(dbh, ZEND_STRL("SET AUTOCOMMIT=0") TSRMLS_CC); + PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("SET AUTOCOMMIT=0") TSRMLS_CC)); } +#endif } +/* }}} */ +/* {{{ pdo_mysql_set_attribute */ static int pdo_mysql_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) { + PDO_DBG_ENTER("pdo_mysql_set_attribute"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("attr=%l", attr); switch (attr) { - case PDO_ATTR_AUTOCOMMIT: - - convert_to_boolean(val); + case PDO_ATTR_AUTOCOMMIT: + convert_to_boolean(val); - /* ignore if the new value equals the old one */ - if (dbh->auto_commit ^ Z_BVAL_P(val)) { - dbh->auto_commit = Z_BVAL_P(val); - mysql_handle_autocommit(dbh TSRMLS_CC); - } - return 1; + /* ignore if the new value equals the old one */ + if (dbh->auto_commit ^ Z_BVAL_P(val)) { + dbh->auto_commit = Z_BVAL_P(val); + mysql_handle_autocommit(dbh TSRMLS_CC); + } + PDO_DBG_RETURN(1); + + case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY: + ((pdo_mysql_db_handle *)dbh->driver_data)->buffered = Z_BVAL_P(val); + PDO_DBG_RETURN(1); + case PDO_MYSQL_ATTR_DIRECT_QUERY: + case PDO_ATTR_EMULATE_PREPARES: + ((pdo_mysql_db_handle *)dbh->driver_data)->emulate_prepare = Z_BVAL_P(val); + PDO_DBG_RETURN(1); + case PDO_ATTR_FETCH_TABLE_NAMES: + ((pdo_mysql_db_handle *)dbh->driver_data)->fetch_table_names = Z_BVAL_P(val); + PDO_DBG_RETURN(1); +#ifndef PDO_USE_MYSQLND + case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE: + if (Z_LVAL_P(val) < 0) { + // TODO - Johannes, can we throw a warning here? + ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size = 1024*1024; + PDO_DBG_INF_FMT("Adjusting invalid buffer size to =%l", ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size); + } else { + ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size = Z_LVAL_P(val); + } + PDO_DBG_RETURN(1); + break; +#endif - case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY: - ((pdo_mysql_db_handle *)dbh->driver_data)->buffered = Z_BVAL_P(val); - return 1; - case PDO_MYSQL_ATTR_DIRECT_QUERY: - ((pdo_mysql_db_handle *)dbh->driver_data)->emulate_prepare = Z_BVAL_P(val); - return 1; - default: - return 0; + default: + PDO_DBG_RETURN(0); } } +/* }}} */ +/* {{{ pdo_mysql_get_attribute */ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC) { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; + PDO_DBG_ENTER("pdo_mysql_get_attribute"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + PDO_DBG_INF_FMT("attr=%l", attr); switch (attr) { case PDO_ATTR_CLIENT_VERSION: ZVAL_STRING(return_value, (char *)mysql_get_client_info(), 1); @@ -337,42 +459,50 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value case PDO_ATTR_CONNECTION_STATUS: ZVAL_STRING(return_value, (char *)mysql_get_host_info(H->server), 1); break; - case PDO_ATTR_SERVER_INFO: { char *tmp; +#if PDO_USE_MYSQLND + int tmp_len; + if (mysqlnd_stat(H->server, &tmp, &tmp_len) == PASS) { + ZVAL_STRINGL(return_value, tmp, tmp_len, 0); +#else if ((tmp = (char *)mysql_stat(H->server))) { ZVAL_STRING(return_value, tmp, 1); +#endif } else { pdo_mysql_error(dbh); - return -1; + PDO_DBG_RETURN(-1); } } break; - case PDO_ATTR_AUTOCOMMIT: ZVAL_LONG(return_value, dbh->auto_commit); - return 1; + break; case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY: ZVAL_LONG(return_value, H->buffered); - return 1; + break; case PDO_MYSQL_ATTR_DIRECT_QUERY: ZVAL_LONG(return_value, H->emulate_prepare); - return 1; + break; +#ifndef PDO_USE_MYSQLND case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE: ZVAL_LONG(return_value, H->max_buffer_size); - return 1; + break; +#endif default: - return 0; + PDO_DBG_RETURN(0); } - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ +/* {{{ pdo_mysql_check_liveness */ static int pdo_mysql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; @@ -381,27 +511,31 @@ static int pdo_mysql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ unsigned int my_errno; #endif + PDO_DBG_ENTER("pdo_mysql_check_liveness"); + PDO_DBG_INF_FMT("dbh=%p", dbh); + #if MYSQL_VERSION_ID > 32230 if (mysql_ping(H->server)) { - return FAILURE; + PDO_DBG_RETURN(FAILURE); } #else /* no mysql_ping() */ - handler=signal(SIGPIPE, SIG_IGN); + handler = signal(SIGPIPE, SIG_IGN); mysql_stat(H->server); switch (mysql_errno(H->server)) { case CR_SERVER_GONE_ERROR: - /* case CR_SERVER_LOST: I'm not sure this means the same as "gone" for us */ + case CR_SERVER_LOST: signal(SIGPIPE, handler); - return FAILURE; + PDO_DBG_RETURN(FAILURE); default: break; } signal(SIGPIPE, handler); #endif /* end mysql_ping() */ - return SUCCESS; + PDO_DBG_RETURN(SUCCESS); } /* }}} */ +/* {{{ mysql_methods */ static struct pdo_dbh_methods mysql_methods = { mysql_handle_closer, mysql_handle_preparer, @@ -416,8 +550,17 @@ static struct pdo_dbh_methods mysql_methods = { pdo_mysql_get_attribute, pdo_mysql_check_liveness }; +/* }}} */ +#ifndef PDO_MYSQL_UNIX_ADDR +# ifdef PHP_WIN32 +# define MYSQL_UNIX_ADDR "MySQL" +# else +# define MYSQL_UNIX_ADDR PDO_MYSQL_G(default_socket) +# endif +#endif +/* {{{ pdo_mysql_handle_factory */ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ { pdo_mysql_db_handle *H; @@ -430,7 +573,7 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ { "dbname", "", 0 }, { "host", "localhost", 0 }, { "port", "3306", 0 }, - { "unix_socket", PDO_MYSQL_UNIX_ADDR, 0 }, + { "unix_socket", MYSQL_UNIX_ADDR, 0 }, }; int connect_opts = 0 #ifdef CLIENT_MULTI_RESULTS @@ -441,48 +584,85 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ #endif ; +#if PDO_USE_MYSQLND + int dbname_len = 0; + int password_len = 0; +#endif + PDO_DBG_ENTER("pdo_mysql_handle_factory"); + PDO_DBG_INF_FMT("dbh=%p", dbh); +#ifdef CLIENT_MULTI_RESULTS + PDO_DBG_INF("multi results"); +#endif + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 5); H = pecalloc(1, sizeof(pdo_mysql_db_handle), dbh->is_persistent); - + H->einfo.errcode = 0; H->einfo.errmsg = NULL; /* allocate an environment */ - + /* handle for the server */ - if (!(H->server = mysql_init(NULL))) { + if (!(H->server = pdo_mysql_init(dbh->is_persistent))) { pdo_mysql_error(dbh); goto cleanup; } dbh->driver_data = H; - H->max_buffer_size = 1024 * 1024; + +#ifndef PDO_USE_MYSQLND + H->max_buffer_size = 1024*1024; +#endif + + H->buffered = H->emulate_prepare = 1; /* handle MySQL options */ if (driver_options) { long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC); long local_infile = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_LOCAL_INFILE, 0 TSRMLS_CC); +#ifndef PDO_USE_MYSQLND char *init_cmd = NULL, *default_file = NULL, *default_group = NULL; - +#endif H->buffered = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY, 1 TSRMLS_CC); - H->emulate_prepare = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_DIRECT_QUERY, 1 TSRMLS_CC); - H->max_buffer_size = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, H->max_buffer_size TSRMLS_CC); - if (PG(open_basedir) && PG(open_basedir)[0] != '\0') { - local_infile = 0; - } + H->emulate_prepare = pdo_attr_lval(driver_options, + PDO_MYSQL_ATTR_DIRECT_QUERY, H->emulate_prepare TSRMLS_CC); + H->emulate_prepare = pdo_attr_lval(driver_options, + PDO_ATTR_EMULATE_PREPARES, H->emulate_prepare TSRMLS_CC); + +#ifndef PDO_USE_MYSQLND + H->max_buffer_size = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, H->max_buffer_size TSRMLS_CC); +#endif if (mysql_options(H->server, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout)) { pdo_mysql_error(dbh); goto cleanup; } - + +#if PHP_MAJOR_VERSION < 6 + if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) +#else + if (PG(open_basedir) && PG(open_basedir)[0] != '\0') +#endif + { + local_infile = 0; + } + if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) { pdo_mysql_error(dbh); goto cleanup; } +#ifdef MYSQL_OPT_RECONNECT + /* since 5.0.3, the default for this option is 0 if not specified. + * we want the old behaviour */ + { + long reconnect = 1; + mysql_options(H->server, MYSQL_OPT_RECONNECT, (const char*)&reconnect); + } +#endif +#ifndef PDO_USE_MYSQLND init_cmd = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_INIT_COMMAND, NULL TSRMLS_CC); if (init_cmd) { if (mysql_options(H->server, MYSQL_INIT_COMMAND, (const char *)init_cmd)) { @@ -512,6 +692,7 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ } efree(default_group); } +#endif } dbname = vars[1].optval; @@ -522,7 +703,22 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ if (vars[2].optval && !strcmp("localhost", vars[2].optval)) { unix_socket = vars[4].optval; } + + /* TODO: - Check zval cache + ZTS */ +#ifdef PDO_USE_MYSQLND + if (dbname) { + dbname_len = strlen(dbname); + } + + if (dbh->password) { + password_len = strlen(dbh->password); + } + + if (mysqlnd_connect(H->server, host, dbh->username, dbh->password, password_len, dbname, dbname_len, + port, unix_socket, connect_opts, PDO_MYSQL_G(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL) { +#else if (mysql_real_connect(H->server, host, dbh->username, dbh->password, dbname, port, unix_socket, connect_opts) == NULL) { +#endif pdo_mysql_error(dbh); goto cleanup; } @@ -548,7 +744,7 @@ cleanup: dbh->methods = &mysql_methods; - return ret; + PDO_DBG_RETURN(ret); } /* }}} */ diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index 4c682f4107..18544a8b3f 100755 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Author: George Schlossnagle | | Wez Furlong | + | Johannes Schlueter | +----------------------------------------------------------------------+ */ @@ -31,11 +32,24 @@ #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#ifdef PDO_USE_MYSQLND +# define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_mysqlnd(stmt TSRMLS_CC) +# define pdo_free_bound_result(res) zval_dtor(res.zv) +# define pdo_mysql_stmt_close(stmt) mysqlnd_stmt_close(stmt, 0) +#else +# define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_libmysql(stmt TSRMLS_CC) +# define pdo_free_bound_result(res) efree(res.buffer) +# define pdo_mysql_stmt_close(stmt) mysql_stmt_close(stmt) +#endif + -static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) + +static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; + PDO_DBG_ENTER("pdo_mysql_stmt_dtor"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); if (S->result) { /* free the resource */ mysql_free_result(S->result); @@ -45,132 +59,247 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) pefree(S->einfo.errmsg, stmt->dbh->is_persistent); S->einfo.errmsg = NULL; } -#if HAVE_MYSQL_STMT_PREPARE +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND if (S->stmt) { - mysql_stmt_close(S->stmt); + pdo_mysql_stmt_close(S->stmt); S->stmt = NULL; } +#endif /* HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND */ + +#ifndef PDO_USE_MYSQLND if (S->params) { efree(S->params); + } + if (S->in_null) { efree(S->in_null); + } + if (S->in_length) { efree(S->in_length); } + +#endif /* PDO_USE_MYSQLND */ + +#ifdef HAVE_MYSQL_STMT_PREPARE if (S->bound_result) { int i; for (i = 0; i < stmt->column_count; i++) { - efree(S->bound_result[i].buffer); + pdo_free_bound_result(S->bound_result[i]); } efree(S->bound_result); efree(S->out_null); efree(S->out_length); } -#endif +#endif /* HAVE_MYSQL_STMT_PREPARE */ + + +#if HAVE_MYSQL_NEXT_RESULT || PDO_USE_MYSQLND + if (S->H->server) { + while (mysql_more_results(S->H->server)) { + MYSQL_RES *res; + if (mysql_next_result(S->H->server) != 0) { + break; + } + + res = mysql_store_result(S->H->server); + if (res) { + mysql_free_result(res); + } + } + } +#endif /* HAVE_MYSQL_NEXT_RESULT || PDO_USE_MYSQLND */ +#if PDO_USE_MYSQLND + if (!S->stmt && S->current_data) { + free(S->current_data); + } +#endif /* PDO_USE_MYSQLND */ + efree(S); - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ -static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) +static void pdo_mysql_stmt_set_row_count(pdo_stmt_t *stmt) /* {{{ */ { - pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; - pdo_mysql_db_handle *H = S->H; my_ulonglong row_count; -#if HAVE_MYSQL_STMT_PREPARE - int i; + pdo_mysql_stmt *S = stmt->driver_data; + row_count = mysql_stmt_affected_rows(S->stmt); + if (row_count != (my_ulonglong)-1) { + stmt->row_count = row_count; + } +} +/* }}} */ - if (S->stmt) { - /* (re)bind the parameters */ - if (mysql_stmt_bind_param(S->stmt, S->params)) { - pdo_mysql_error_stmt(stmt); - return 0; - } +#ifdef HAVE_MYSQL_STMT_PREPARE +static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ +{ + pdo_mysql_stmt *S = stmt->driver_data; + pdo_mysql_db_handle *H = S->H; - if (mysql_stmt_execute(S->stmt)) { - pdo_mysql_error_stmt(stmt); - return 0; + PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_libmysql"); + + /* (re)bind the parameters */ + if (mysql_stmt_bind_param(S->stmt, S->params) || mysql_stmt_execute(S->stmt)) { + if (S->params) { + efree(S->params); + S->params = 0; + } + pdo_mysql_error_stmt(stmt); + if (mysql_stmt_errno(S->stmt) == 2057) { + /* CR_NEW_STMT_METADATA makes the statement unusable */ + S->stmt = NULL; } + PDO_DBG_RETURN(0); + } - if (!S->result) { - /* figure out the result set format, if any */ - S->result = mysql_stmt_result_metadata(S->stmt); - if (S->result) { - int calc_max_length = H->buffered && S->max_length == 1; + if (!S->result) { + int i; + + /* figure out the result set format, if any */ + S->result = mysql_stmt_result_metadata(S->stmt); + if (S->result) { + int calc_max_length = H->buffered && S->max_length == 1; + S->fields = mysql_fetch_fields(S->result); + if (S->bound_result) { + int i; + for (i = 0; i < stmt->column_count; i++) { + efree(S->bound_result[i].buffer); + } + efree(S->bound_result); + efree(S->out_null); + efree(S->out_length); + } - S->fields = mysql_fetch_fields(S->result); - - if (S->bound_result) { - int i; - for (i = 0; i < stmt->column_count; i++) { - efree(S->bound_result[i].buffer); - } - efree(S->bound_result); - efree(S->out_null); - efree(S->out_length); + stmt->column_count = (int)mysql_num_fields(S->result); + S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND)); + S->out_null = ecalloc(stmt->column_count, sizeof(my_bool)); + S->out_length = ecalloc(stmt->column_count, sizeof(unsigned long)); + + /* summon memory to hold the row */ + for (i = 0; i < stmt->column_count; i++) { + if (calc_max_length && S->fields[i].type == FIELD_TYPE_BLOB) { + my_bool on = 1; + mysql_stmt_attr_set(S->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on); + calc_max_length = 0; } - - stmt->column_count = (int)mysql_num_fields(S->result); - S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND)); - S->out_null = ecalloc(stmt->column_count, sizeof(my_bool)); - S->out_length = ecalloc(stmt->column_count, sizeof(unsigned long)); - - /* summon memory to hold the row */ - for (i = 0; i < stmt->column_count; i++) { - if (calc_max_length && S->fields[i].type == FIELD_TYPE_BLOB) { - my_bool on = 1; - mysql_stmt_attr_set(S->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on); - calc_max_length = 0; - } - switch (S->fields[i].type) { - case FIELD_TYPE_INT24: - S->bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH; - break; - case FIELD_TYPE_LONG: - S->bound_result[i].buffer_length = MAX_INT_WIDTH; - break; - case FIELD_TYPE_LONGLONG: - S->bound_result[i].buffer_length = MAX_BIGINT_WIDTH; - break; - case FIELD_TYPE_TINY: - S->bound_result[i].buffer_length = MAX_TINYINT_WIDTH; - break; - case FIELD_TYPE_SHORT: - S->bound_result[i].buffer_length = MAX_SMALLINT_WIDTH; - break; - default: - S->bound_result[i].buffer_length = - S->fields[i].max_length? S->fields[i].max_length: - S->fields[i].length; - /* work-around for longtext and alike */ - if (S->bound_result[i].buffer_length > H->max_buffer_size) { - S->bound_result[i].buffer_length = H->max_buffer_size; - } - } - S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length); - S->bound_result[i].is_null = &S->out_null[i]; - S->bound_result[i].length = &S->out_length[i]; - S->bound_result[i].buffer_type = MYSQL_TYPE_STRING; + switch (S->fields[i].type) { + case FIELD_TYPE_INT24: + S->bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH + 1; + break; + case FIELD_TYPE_LONG: + S->bound_result[i].buffer_length = MAX_INT_WIDTH + 1; + break; + case FIELD_TYPE_LONGLONG: + S->bound_result[i].buffer_length = MAX_BIGINT_WIDTH + 1; + break; + case FIELD_TYPE_TINY: + S->bound_result[i].buffer_length = MAX_TINYINT_WIDTH + 1; + break; + case FIELD_TYPE_SHORT: + S->bound_result[i].buffer_length = MAX_SMALLINT_WIDTH + 1; + break; + default: + S->bound_result[i].buffer_length = + S->fields[i].max_length? S->fields[i].max_length: + S->fields[i].length; + /* work-around for longtext and alike */ + if (S->bound_result[i].buffer_length > H->max_buffer_size) { + S->bound_result[i].buffer_length = H->max_buffer_size; + } } - if (mysql_stmt_bind_result(S->stmt, S->bound_result)) { - pdo_mysql_error_stmt(stmt); - return 0; + /* there are cases where the length reported by mysql is too short. + * eg: when describing a table that contains an enum column. Since + * we have no way of knowing the true length either, we'll bump up + * our buffer size to a reasonable size, just in case */ + if (S->fields[i].max_length == 0 && S->bound_result[i].buffer_length < 128 && MYSQL_TYPE_VAR_STRING) { + S->bound_result[i].buffer_length = 128; } - /* if buffered, pre-fetch all the data */ - if (H->buffered) { - mysql_stmt_store_result(S->stmt); - } + S->out_length[i] = 0; + + S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length); + S->bound_result[i].is_null = &S->out_null[i]; + S->bound_result[i].length = &S->out_length[i]; + S->bound_result[i].buffer_type = MYSQL_TYPE_STRING; + } + + if (mysql_stmt_bind_result(S->stmt, S->bound_result)) { + pdo_mysql_error_stmt(stmt); + PDO_DBG_RETURN(0); + } + + /* if buffered, pre-fetch all the data */ + if (H->buffered) { + mysql_stmt_store_result(S->stmt); } } + } + + pdo_mysql_stmt_set_row_count(stmt); + PDO_DBG_RETURN(1); +} +/* }}} */ +#endif - row_count = mysql_stmt_affected_rows(S->stmt); - if (row_count != (my_ulonglong)-1) { - stmt->row_count = row_count; +#ifdef PDO_USE_MYSQLND +static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ +{ + pdo_mysql_stmt *S = stmt->driver_data; + pdo_mysql_db_handle *H = S->H; + unsigned int i; + + PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_mysqlnd"); + + if (mysql_stmt_execute(S->stmt)) { + pdo_mysql_error_stmt(stmt); + PDO_DBG_RETURN(0); + } + + if (S->result) { + /* TODO: add a test to check if we really have zvals here... */ + mysql_free_result(S->result); + S->result = NULL; + } + + /* for SHOW/DESCRIBE and others the column/field count is not available before execute */ + stmt->column_count = S->stmt->field_count; + for (i = 0; i < stmt->column_count; i++) { + mysqlnd_stmt_bind_one_result(S->stmt, i); + } + + S->result = mysqlnd_stmt_result_metadata(S->stmt); + if (S->result) { + S->fields = mysql_fetch_fields(S->result); + /* if buffered, pre-fetch all the data */ + if (H->buffered) { + if (mysql_stmt_store_result(S->stmt)) { + PDO_DBG_RETURN(0); + } } - return 1; + } + + pdo_mysql_stmt_set_row_count(stmt); + PDO_DBG_RETURN(1); +} +/* }}} */ +#endif + +static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ +{ + pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; + pdo_mysql_db_handle *H = S->H; + my_ulonglong row_count; + PDO_DBG_ENTER("pdo_mysql_stmt_execute"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); + +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND + if (S->stmt) { + PDO_DBG_RETURN(pdo_mysql_stmt_execute_prepared(stmt)); } #endif + /* ensure that we free any previous unfetched results */ if (S->result) { mysql_free_result(S->result); @@ -179,7 +308,7 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) if (mysql_real_query(H->server, stmt->active_query_string, stmt->active_query_stringlen) != 0) { pdo_mysql_error_stmt(stmt); - return 0; + PDO_DBG_RETURN(0); } row_count = mysql_affected_rows(H->server); @@ -193,36 +322,89 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) } if (NULL == S->result) { pdo_mysql_error_stmt(stmt); - return 0; + PDO_DBG_RETURN(0); } stmt->row_count = mysql_num_rows(S->result); + stmt->column_count = (int) mysql_num_fields(S->result); + S->fields = mysql_fetch_fields(S->result); - if (!stmt->executed) { - stmt->column_count = (int) mysql_num_fields(S->result); - S->fields = mysql_fetch_fields(S->result); - } } else { /* this was a DML or DDL query (INSERT, UPDATE, DELETE, ... */ stmt->row_count = row_count; } - return 1; + PDO_DBG_RETURN(1); } +/* {{{ */ -static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) +static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ { -#if HAVE_MYSQL_NEXT_RESULT +#if HAVE_MYSQL_NEXT_RESULT || PDO_USE_MYSQLND pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; pdo_mysql_db_handle *H = S->H; my_ulonglong row_count; int ret; + PDO_DBG_ENTER("pdo_mysql_stmt_next_rowset"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); - /* ensure that we free any previous unfetched results */ +#if PDO_USE_MYSQLND + if (!H->emulate_prepare) { + if (!mysqlnd_stmt_more_results(S->stmt)) { + PDO_DBG_RETURN(0); + } + if (mysqlnd_stmt_next_result(S->stmt)) { + PDO_DBG_RETURN(0); + } + + if (!mysqlnd_stmt_more_results(S->stmt)) { + /* + MySQL gives us n + 1 result sets for + CALL proc() and n result sets returned by the proc itself. + Result set n + 1 is about the procedure call itself. + As the PDO emulation does not return it, we skip it as well + */ + PDO_DBG_RETURN(0); + } + + /* TODO - this code is stolen from execute() - see above */ + if (S->result) { + mysql_free_result(S->result); + S->result = NULL; + } + { + /* for SHOW/DESCRIBE and others the column/field count is not available before execute */ + unsigned int i; + + stmt->column_count = S->stmt->field_count; + for (i = 0; i < stmt->column_count; i++) { + mysqlnd_stmt_bind_one_result(S->stmt, i); + } + } + + S->result = mysqlnd_stmt_result_metadata(S->stmt); + if (S->result) { + S->fields = mysql_fetch_fields(S->result); + + /* if buffered, pre-fetch all the data */ + if (H->buffered) { + if (mysql_stmt_store_result(S->stmt)) + PDO_DBG_RETURN(1); + } + } + row_count = mysql_stmt_affected_rows(S->stmt); + if (row_count != (my_ulonglong)-1) { + stmt->row_count = row_count; + } + PDO_DBG_RETURN(1); + } +#endif + +/* ensure that we free any previous unfetched results */ #if HAVE_MYSQL_STMT_PREPARE if (S->stmt) { + stmt->column_count = (int)mysql_num_fields(S->result); mysql_stmt_free_result(S->stmt); - S->stmt = NULL; } #endif if (S->result) { @@ -234,10 +416,10 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) if (ret > 0) { pdo_mysql_error_stmt(stmt); - return 0; + PDO_DBG_RETURN(0); } else if (ret < 0) { /* No more results */ - return 0; + PDO_DBG_RETURN(0); } else { if (!H->buffered) { S->result = mysql_use_result(H->server); @@ -246,204 +428,354 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) S->result = mysql_store_result(H->server); if ((my_ulonglong)-1 == (row_count = mysql_affected_rows(H->server))) { pdo_mysql_error_stmt(stmt); - return 0; + PDO_DBG_RETURN(0); } } if (NULL == S->result) { - return 0; + PDO_DBG_RETURN(0); } stmt->row_count = row_count; stmt->column_count = (int) mysql_num_fields(S->result); S->fields = mysql_fetch_fields(S->result); - return 1; + PDO_DBG_RETURN(1); } #else strcpy(stmt->error_code, "HYC00"); - return 0; -#endif + PDO_DBG_RETURN(0); +#endif /* HAVE_MYSQL_STMT_PREPARE */ } +/* }}} */ + + +static const char * const pdo_param_event_names[] = +{ + "PDO_PARAM_EVT_ALLOC", + "PDO_PARAM_EVT_FREE", + "PDO_PARAM_EVT_EXEC_PRE", + "PDO_PARAM_EVT_EXEC_POST", + "PDO_PARAM_EVT_FETCH_PRE", + "PDO_PARAM_EVT_FETCH_POST", + "PDO_PARAM_EVT_NORMALIZE", +}; static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type TSRMLS_DC) + enum pdo_param_event event_type TSRMLS_DC) /* {{{ */ { -#if HAVE_MYSQL_STMT_PREPARE - pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; - MYSQL_BIND *b; +#ifndef PDO_USE_MYSQLND + PDO_MYSQL_PARAM_BIND *b; +#endif +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND + pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; + PDO_DBG_ENTER("pdo_mysql_stmt_param_hook"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); + PDO_DBG_INF_FMT("event = %s", pdo_param_event_names[event_type]); if (S->stmt && param->is_param) { switch (event_type) { case PDO_PARAM_EVT_ALLOC: /* sanity check parameter number range */ if (param->paramno < 0 || param->paramno >= S->num_params) { strcpy(stmt->error_code, "HY093"); - return 0; + PDO_DBG_RETURN(0); } + S->params_given++; + +#ifndef PDO_USE_MYSQLND b = &S->params[param->paramno]; param->driver_data = b; b->is_null = &S->in_null[param->paramno]; b->length = &S->in_length[param->paramno]; - return 1; + /* recall how many parameters have been provided */ +#endif + PDO_DBG_RETURN(1); case PDO_PARAM_EVT_EXEC_PRE: - b = (MYSQL_BIND*)param->driver_data; + if (S->params_given < S->num_params) { + /* too few parameter bound */ + PDO_DBG_ERR("too few parameters bound"); + strcpy(stmt->error_code, "HY093"); + PDO_DBG_RETURN(0); + } - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || +#if PDO_USE_MYSQLND + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || Z_TYPE_P(param->parameter) == IS_NULL) { - *b->is_null = 1; - b->buffer_type = MYSQL_TYPE_STRING; - b->buffer = NULL; - b->buffer_length = 0; - *b->length = 0; - return 1; + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_NULL); + PDO_DBG_RETURN(1); } +#else + b = (PDO_MYSQL_PARAM_BIND*)param->driver_data; + *b->is_null = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(param->parameter) == IS_NULL) { + *b->is_null = 1; + b->buffer_type = MYSQL_TYPE_STRING; + b->buffer = NULL; + b->buffer_length = 0; + *b->length = 0; + PDO_DBG_RETURN(1); + } +#endif /* PDO_USE_MYSQLND */ switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_LOB: case PDO_PARAM_STMT: - return 0; + PDO_DBG_RETURN(0); + case PDO_PARAM_LOB: + PDO_DBG_INF("PDO_PARAM_LOB"); + if (Z_TYPE_P(param->parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, ¶m->parameter); + if (stm) { + SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + Z_TYPE_P(param->parameter) = IS_STRING; + Z_STRLEN_P(param->parameter) = php_stream_copy_to_mem(stm, + &Z_STRVAL_P(param->parameter), PHP_STREAM_COPY_ALL, 0); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource" TSRMLS_CC); + return 0; + } + } + /* fall through */ + default: ; } - + +#if PDO_USE_MYSQLND + /* Is it really correct to check the zval's type? - But well, that's what the old code below does, too */ + PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE_P(param->parameter)); + switch (Z_TYPE_P(param->parameter)) { + case IS_STRING: + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_VAR_STRING); + break; + case IS_LONG: +#if SIZEOF_LONG==8 + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_LONGLONG); +#elif SIZEOF_LONG==4 + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_LONG); +#endif /* SIZEOF_LONG */ + break; + case IS_DOUBLE: + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_DOUBLE); + break; + default: + PDO_DBG_RETURN(0); + } + + PDO_DBG_RETURN(1); +#else + PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE_P(param->parameter)); switch (Z_TYPE_P(param->parameter)) { case IS_STRING: b->buffer_type = MYSQL_TYPE_STRING; b->buffer = Z_STRVAL_P(param->parameter); b->buffer_length = Z_STRLEN_P(param->parameter); *b->length = Z_STRLEN_P(param->parameter); - return 1; + PDO_DBG_RETURN(1); case IS_LONG: b->buffer_type = MYSQL_TYPE_LONG; b->buffer = &Z_LVAL_P(param->parameter); - return 1; + PDO_DBG_RETURN(1); case IS_DOUBLE: b->buffer_type = MYSQL_TYPE_DOUBLE; b->buffer = &Z_DVAL_P(param->parameter); - return 1; + PDO_DBG_RETURN(1); default: - return 0; + PDO_DBG_RETURN(0); } +#endif /* PDO_USE_MYSQLND */ + case PDO_PARAM_EVT_FREE: + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* do nothing */ + break; } } -#endif - return 1; +#endif /* HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND */ + PDO_DBG_RETURN(1); } +/* }}} */ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, long offset TSRMLS_DC) + enum pdo_fetch_orientation ori, long offset TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; -#if HAVE_MYSQL_STMT_PREPARE +#if PDO_USE_MYSQLND + zend_bool fetched_anything; + + PDO_DBG_ENTER("pdo_mysql_stmt_fetch"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); + if (S->stmt) { + if (FAIL == mysqlnd_stmt_fetch(S->stmt, &fetched_anything) || fetched_anything == FALSE) { + PDO_DBG_RETURN(0); + } + + PDO_DBG_RETURN(1); + } +#else +# if HAVE_MYSQL_STMT_PREPARE int ret; if (S->stmt) { ret = mysql_stmt_fetch(S->stmt); -#ifdef MYSQL_DATA_TRUNCATED +# ifdef MYSQL_DATA_TRUNCATED if (ret == MYSQL_DATA_TRUNCATED) { ret = 0; } -#endif +# endif if (ret) { if (ret != MYSQL_NO_DATA) { pdo_mysql_error_stmt(stmt); } - return 0; + PDO_DBG_RETURN(0); } - return 1; + PDO_DBG_RETURN(1); } -#endif - +# endif /* HAVE_MYSQL_STMT_PREPARE */ +#endif /* PDO_USE_MYSQLND */ + if (!S->result) { strcpy(stmt->error_code, "HY000"); - return 0; + PDO_DBG_RETURN(0); + } +#if PDO_USE_MYSQLND + if (!S->stmt && S->current_data) { + free(S->current_data); } +#endif /* PDO_USE_MYSQLND */ + if ((S->current_data = mysql_fetch_row(S->result)) == NULL) { if (mysql_errno(S->H->server)) { pdo_mysql_error_stmt(stmt); } - return 0; + PDO_DBG_RETURN(0); } + S->current_lengths = mysql_fetch_lengths(S->result); - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ -static int pdo_mysql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) +static int pdo_mysql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; struct pdo_column_data *cols = stmt->columns; unsigned int i; + PDO_DBG_ENTER("pdo_mysql_stmt_describe"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); if (!S->result) { - return 0; + PDO_DBG_RETURN(0); } if (colno >= stmt->column_count) { /* error invalid column */ - return 0; + PDO_DBG_RETURN(0); } /* fetch all on demand, this seems easiest ** if we've been here before bail out */ if (cols[0].name) { - return 1; + PDO_DBG_RETURN(1); } for (i=0; i < stmt->column_count; i++) { int namelen; - namelen = strlen(S->fields[i].name); + + if (S->H->fetch_table_names) { + namelen = spprintf(&cols[i].name, 0, "%s.%s", S->fields[i].table, S->fields[i].name); + cols[i].namelen = namelen; + } else { + namelen = strlen(S->fields[i].name); + cols[i].namelen = namelen; + cols[i].name = estrndup(S->fields[i].name, namelen); + } + cols[i].precision = S->fields[i].decimals; cols[i].maxlen = S->fields[i].length; - cols[i].namelen = namelen; - cols[i].name = estrndup(S->fields[i].name, namelen); - cols[i].param_type = PDO_PARAM_STR; + +#ifdef PDO_USE_MYSQLND + if (S->stmt) { + cols[i].param_type = PDO_PARAM_ZVAL; + } else +#endif + { + cols[i].param_type = PDO_PARAM_STR; + } } - return 1; + PDO_DBG_RETURN(1); } +/* }}} */ -static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) +static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; +#if PDO_USE_MYSQLND + struct pdo_column_data *cols = stmt->columns; +#endif + PDO_DBG_ENTER("pdo_mysql_stmt_get_col"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); + if (!S->result) { + PDO_DBG_RETURN(0); + } -#if HAVE_MYSQL_STMT_PREPARE + /* With mysqlnd data is stored inside mysqlnd, not S->current_data */ +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND if (!S->stmt) { #endif if (S->current_data == NULL || !S->result) { - return 0; + PDO_DBG_RETURN(0); } -#if HAVE_MYSQL_STMT_PREPARE +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND } #endif if (colno >= stmt->column_count) { /* error invalid column */ - return 0; + PDO_DBG_RETURN(0); } -#if HAVE_MYSQL_STMT_PREPARE +#if PDO_USE_MYSQLND + if (S->stmt) { + Z_ADDREF_P(S->stmt->result_bind[colno].zv); + *ptr = (char*)&S->stmt->result_bind[colno].zv; + *len = sizeof(zval); + PDO_DBG_RETURN(1); + } +#elif HAVE_MYSQL_STMT_PREPARE if (S->stmt) { if (S->out_null[colno]) { *ptr = NULL; *len = 0; - return 1; + PDO_DBG_RETURN(1); } *ptr = S->bound_result[colno].buffer; + if (S->out_length[colno] > S->bound_result[colno].buffer_length) { + /* mysql lied about the column width */ + strcpy(stmt->error_code, "01004"); /* truncated */ + S->out_length[colno] = S->bound_result[colno].buffer_length; + *len = S->out_length[colno]; + PDO_DBG_RETURN(0); + } *len = S->out_length[colno]; - return 1; + PDO_DBG_RETURN(1); } -#endif +#endif /* PDO_USE_MYSQLND else HAVE_MYSQL_STMT_PREPARE */ *ptr = S->current_data[colno]; *len = S->current_lengths[colno]; - return 1; -} + PDO_DBG_RETURN(1); +} /* }}} */ -static char *type_to_name_native(int type) +static char *type_to_name_native(int type) /* }}} */ { #define PDO_MYSQL_NATIVE_TYPE_NAME(x) case FIELD_TYPE_##x: return #x; @@ -486,21 +818,24 @@ static char *type_to_name_native(int type) default: return NULL; } -} +#undef PDO_MYSQL_NATIVE_TYPE_NAME +} /* }}} */ -static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC) +static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; - MYSQL_FIELD *F; + const MYSQL_FIELD *F; zval *flags; char *str; + PDO_DBG_ENTER("pdo_mysql_stmt_col_meta"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); if (!S->result) { - return FAILURE; + PDO_DBG_RETURN(FAILURE); } - if (colno >= stmt->column_count || colno < 0) { + if (colno >= stmt->column_count) { /* error invalid column */ - return FAILURE; + PDO_DBG_RETURN(FAILURE); } array_init(return_value); @@ -532,32 +867,63 @@ static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, long colno, zval *return_va add_assoc_string(return_value, "native_type", str, 1); } +#ifdef PDO_USE_MYSQLND + switch (F->type) { + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: +#if SIZEOF_LONG==8 + case MYSQL_TYPE_LONGLONG: +#endif + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + } +#endif + add_assoc_zval(return_value, "flags", flags); - return SUCCESS; -} + add_assoc_string(return_value, "table",(F->table?F->table:""), 1); + PDO_DBG_RETURN(SUCCESS); +} /* }}} */ -static int pdo_mysql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +static int pdo_mysql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; -#if HAVE_MYSQL_STMT_PREPARE + + PDO_DBG_ENTER("pdo_mysql_stmt_cursor_closer"); + PDO_DBG_INF_FMT("stmt=%p", S->stmt); + if (S->result) { + mysql_free_result(S->result); + S->result = NULL; + } +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND if (S->stmt) { int retval; - if (!S->H->buffered) { - retval = mysql_stmt_close(S->stmt); - S->stmt = NULL; - } else { - retval = mysql_stmt_free_result(S->stmt); - } - S->stmt = NULL; - return retval ? 0 : 1; + retval = mysql_stmt_free_result(S->stmt); + PDO_DBG_RETURN(retval ? 0 : 1); } #endif - if (S->result) { - mysql_free_result(S->result); - S->result = NULL; + +#if HAVE_MYSQL_NEXT_RESULT || PDO_USE_MYSQLND + while (mysql_more_results(S->H->server)) { + MYSQL_RES *res; + if (mysql_next_result(S->H->server) != 0) { + break; + } + res = mysql_store_result(S->H->server); + if (res) { + mysql_free_result(res); + } } - return 1; +#endif + PDO_DBG_RETURN(1); } +/* }}} */ struct pdo_stmt_methods mysql_stmt_methods = { pdo_mysql_stmt_dtor, diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index bff9f10ac7..d13f45d71c 100755 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: George Schlossnagle | + | Johannes Schlueter | +----------------------------------------------------------------------+ */ @@ -30,83 +31,220 @@ #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" -/* {{{ pdo_mysql_functions[] */ -const zend_function_entry pdo_mysql_functions[] = { - {NULL, NULL, NULL} -}; -/* }}} */ +#ifdef COMPILE_DL_PDO_MYSQL +ZEND_GET_MODULE(pdo_mysql) +#endif -/* {{{ pdo_mysql_functions[] */ -#if ZEND_MODULE_API_NO >= 20050922 -static const zend_module_dep pdo_mysql_deps[] = { - ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} -}; +#if PDO_USE_MYSQLND +ZEND_DECLARE_MODULE_GLOBALS(pdo_mysql); + +#ifndef PHP_WIN32 +# ifndef PDO_MYSQL_UNIX_ADDR +# define PDO_MYSQL_UNIX_ADDR "/tmp/mysql.sock" +# endif #endif -/* }}} */ -/* {{{ pdo_mysql_module_entry */ -zend_module_entry pdo_mysql_module_entry = { -#if ZEND_MODULE_API_NO >= 20050922 - STANDARD_MODULE_HEADER_EX, NULL, - pdo_mysql_deps, -#else - STANDARD_MODULE_HEADER, + +/* {{{ PHP_INI_BEGIN +*/ +PHP_INI_BEGIN() +#ifndef PHP_WIN32 + STD_PHP_INI_ENTRY("pdo_mysql.default_socket", PDO_MYSQL_UNIX_ADDR, PHP_INI_SYSTEM, OnUpdateString, default_socket, zend_pdo_mysql_globals, pdo_mysql_globals) #endif - "pdo_mysql", - pdo_mysql_functions, - PHP_MINIT(pdo_mysql), - PHP_MSHUTDOWN(pdo_mysql), - NULL, - NULL, - PHP_MINFO(pdo_mysql), - "0.9", - STANDARD_MODULE_PROPERTIES -}; +#if PDO_DBG_ENABLED + STD_PHP_INI_ENTRY("pdo_mysql.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_pdo_mysql_globals, pdo_mysql_globals) +#endif + STD_PHP_INI_ENTRY("pdo_mysql.cache_size", "2000", PHP_INI_SYSTEM, OnUpdateLong, cache_size, zend_pdo_mysql_globals, pdo_mysql_globals) +PHP_INI_END() /* }}} */ - -#ifdef COMPILE_DL_PDO_MYSQL -ZEND_GET_MODULE(pdo_mysql) #endif /* true global environment */ - +#ifdef PDO_USE_MYSQLND +static MYSQLND_ZVAL_PCACHE *mysql_mysqlnd_zval_cache; +#endif + + /* {{{ PHP_MINIT_FUNCTION */ -PHP_MINIT_FUNCTION(pdo_mysql) +static PHP_MINIT_FUNCTION(pdo_mysql) { - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_USE_BUFFERED_QUERY", (long)PDO_MYSQL_ATTR_USE_BUFFERED_QUERY); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_LOCAL_INFILE", (long)PDO_MYSQL_ATTR_LOCAL_INFILE); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_INIT_COMMAND", (long)PDO_MYSQL_ATTR_INIT_COMMAND); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_FILE", (long)PDO_MYSQL_ATTR_READ_DEFAULT_FILE); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_GROUP", (long)PDO_MYSQL_ATTR_READ_DEFAULT_GROUP); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_MAX_BUFFER_SIZE", (long)PDO_MYSQL_ATTR_MAX_BUFFER_SIZE); - REGISTER_PDO_CONST_LONG("MYSQL_ATTR_DIRECT_QUERY", (long)PDO_MYSQL_ATTR_DIRECT_QUERY); +#if PDO_USE_MYSQLND + REGISTER_INI_ENTRIES(); +#endif + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_USE_BUFFERED_QUERY", (long)PDO_MYSQL_ATTR_USE_BUFFERED_QUERY); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_LOCAL_INFILE", (long)PDO_MYSQL_ATTR_LOCAL_INFILE); +#ifndef PDO_USE_MYSQLND + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_MAX_BUFFER_SIZE", (long)PDO_MYSQL_ATTR_MAX_BUFFER_SIZE); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_INIT_COMMAND", (long)PDO_MYSQL_ATTR_INIT_COMMAND); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_FILE", (long)PDO_MYSQL_ATTR_READ_DEFAULT_FILE); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_GROUP", (long)PDO_MYSQL_ATTR_READ_DEFAULT_GROUP); +#endif + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_DIRECT_QUERY", (long)PDO_MYSQL_ATTR_DIRECT_QUERY); + +#ifdef PDO_USE_MYSQLND + mysql_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(PDO_MYSQL_G(cache_size)); +#endif + return php_pdo_register_driver(&pdo_mysql_driver); } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ -PHP_MSHUTDOWN_FUNCTION(pdo_mysql) +static PHP_MSHUTDOWN_FUNCTION(pdo_mysql) { php_pdo_unregister_driver(&pdo_mysql_driver); +#if PDO_USE_MYSQLND + UNREGISTER_INI_ENTRIES(); +#endif + return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ -PHP_MINFO_FUNCTION(pdo_mysql) +static PHP_MINFO_FUNCTION(pdo_mysql) { php_info_print_table_start(); +#ifdef PDO_USE_MYSQLND + php_info_print_table_header(2, "PDO Driver for MySQL, mysql native driver version", mysql_get_client_info()); + + { + zval values; + + php_info_print_table_header(2, "Persistent cache", mysql_mysqlnd_zval_cache? "enabled":"disabled"); + + if (mysql_mysqlnd_zval_cache) { + /* Now report cache status */ + mysqlnd_palloc_stats(mysql_mysqlnd_zval_cache, &values); + mysqlnd_minfo_print_hash(&values); + zval_dtor(&values); + } + } +#else php_info_print_table_header(2, "PDO Driver for MySQL, client library version", mysql_get_client_info()); - php_info_print_table_row(2, "MYSQL_SOCKET", PDO_MYSQL_UNIX_ADDR); +#endif php_info_print_table_end(); + +#ifdef PDO_USE_MYSQLND + DISPLAY_INI_ENTRIES(); +#endif +} +/* }}} */ + + +#if PDO_USE_MYSQLND +/* {{{ PHP_RINIT_FUNCTION + */ +static PHP_RINIT_FUNCTION(pdo_mysql) +{ + PDO_MYSQL_G(mysqlnd_thd_zval_cache) = mysqlnd_palloc_rinit(mysql_mysqlnd_zval_cache); + +#if PDO_DBG_ENABLED + if (PDO_MYSQL_G(debug)) { + MYSQLND_DEBUG *dbg = mysqlnd_debug_init(TSRMLS_C); + if (!dbg) { + return FAILURE; + } + dbg->m->set_mode(dbg, PDO_MYSQL_G(debug)); + PDO_MYSQL_G(dbg) = dbg; + } +#endif + + return SUCCESS; } /* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +static PHP_RSHUTDOWN_FUNCTION(pdo_mysql) +{ + mysqlnd_palloc_rshutdown(PDO_MYSQL_G(mysqlnd_thd_zval_cache)); + +#if PDO_DBG_ENABLED + MYSQLND_DEBUG *dbg = PDO_MYSQL_G(dbg); + PDO_DBG_ENTER("RSHUTDOWN"); + if (dbg) { + dbg->m->close(dbg); + dbg->m->free_handle(dbg); + PDO_MYSQL_G(dbg) = NULL; + } +#endif + return SUCCESS; +} +/* }}} */ + + +/* {{{ PHP_GINIT_FUNCTION + */ +static PHP_GINIT_FUNCTION(pdo_mysql) +{ + pdo_mysql_globals->mysqlnd_thd_zval_cache = NULL; /* zval cache */ + pdo_mysql_globals->cache_size = 0; +#ifndef PHP_WIN32 + pdo_mysql_globals->default_socket = NULL; +#endif +#if PDO_DBG_ENABLED + pdo_mysql_globals->debug = NULL; /* The actual string */ + pdo_mysql_globals->dbg = NULL; /* The DBG object*/ +#endif +} +/* }}} */ +#endif + + +/* {{{ pdo_mysql_functions[] */ +const zend_function_entry pdo_mysql_functions[] = { + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ pdo_mysql_deps[] */ +#if ZEND_MODULE_API_NO >= 20050922 +static const zend_module_dep pdo_mysql_deps[] = { + ZEND_MOD_REQUIRED("pdo") +#ifdef PDO_USE_MYSQLND + ZEND_MOD_REQUIRED("mysqlnd") +#endif + {NULL, NULL, NULL} +}; +#endif +/* }}} */ + +/* {{{ pdo_mysql_module_entry */ +zend_module_entry pdo_mysql_module_entry = { + STANDARD_MODULE_HEADER_EX, NULL, + pdo_mysql_deps, + "pdo_mysql", + pdo_mysql_functions, + PHP_MINIT(pdo_mysql), + PHP_MSHUTDOWN(pdo_mysql), +#if PDO_USE_MYSQLND + PHP_RINIT(pdo_mysql), + PHP_RSHUTDOWN(pdo_mysql), +#else + NULL, + NULL, +#endif + PHP_MINFO(pdo_mysql), + "1.0.2", +#if PDO_USE_MYSQLND + PHP_MODULE_GLOBALS(pdo_mysql), + PHP_GINIT(pdo_mysql), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +#else + STANDARD_MODULE_PROPERTIES +#endif +}; +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/ext/pdo_mysql/php_pdo_mysql.h b/ext/pdo_mysql/php_pdo_mysql.h index 8a9922f10f..3da32cff34 100755 --- a/ext/pdo_mysql/php_pdo_mysql.h +++ b/ext/pdo_mysql/php_pdo_mysql.h @@ -24,15 +24,16 @@ extern zend_module_entry pdo_mysql_module_entry; #define phpext_pdo_mysql_ptr &pdo_mysql_module_entry +#ifdef PHP_WIN32 +#define PHP_PDO_MYSQL_API __declspec(dllexport) +#else +#define PHP_PDO_MYSQL_API +#endif + #ifdef ZTS #include "TSRM.h" #endif -PHP_MINIT_FUNCTION(pdo_mysql); -PHP_MSHUTDOWN_FUNCTION(pdo_mysql); -PHP_RINIT_FUNCTION(pdo_mysql); -PHP_RSHUTDOWN_FUNCTION(pdo_mysql); -PHP_MINFO_FUNCTION(pdo_mysql); #endif /* PHP_PDO_MYSQL_H */ diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h index 65bcbd2a2b..3ed96c9029 100755 --- a/ext/pdo_mysql/php_pdo_mysql_int.h +++ b/ext/pdo_mysql/php_pdo_mysql_int.h @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Author: George Schlossnagle | | Wez Furlong | + | Johannes Schlueter | +----------------------------------------------------------------------+ */ @@ -22,7 +23,66 @@ #ifndef PHP_PDO_MYSQL_INT_H #define PHP_PDO_MYSQL_INT_H -#include +#if defined(PDO_USE_MYSQLND) +# include "ext/mysqlnd/mysqlnd.h" +# include "ext/mysql/mysql_mysqlnd.h" +# include "ext/mysqlnd/mysqlnd_libmysql_compat.h" +# define PDO_MYSQL_PARAM_BIND MYSQLND_PARAM_BIND +#else +# include +# define PDO_MYSQL_PARAM_BIND MYSQL_BIND +#endif + +#if defined(PDO_USE_MYSQLND) && PHP_DEBUG && !defined(PHP_WIN32) +#define PDO_DBG_ENABLED 1 + +#define PDO_DBG_INF(msg) do { if (dbg_skip_trace == FALSE) PDO_MYSQL_G(dbg)->m->log(PDO_MYSQL_G(dbg), __LINE__, __FILE__, -1, "info : ", (msg)); } while (0) +#define PDO_DBG_ERR(msg) do { if (dbg_skip_trace == FALSE) PDO_MYSQL_G(dbg)->m->log(PDO_MYSQL_G(dbg), __LINE__, __FILE__, -1, "error: ", (msg)); } while (0) +#define PDO_DBG_INF_FMT(...) do { if (dbg_skip_trace == FALSE) PDO_MYSQL_G(dbg)->m->log_va(PDO_MYSQL_G(dbg), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0) +#define PDO_DBG_ERR_FMT(...) do { if (dbg_skip_trace == FALSE) PDO_MYSQL_G(dbg)->m->log_va(PDO_MYSQL_G(dbg), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0) +#define PDO_DBG_ENTER(func_name) zend_bool dbg_skip_trace = TRUE; if (PDO_MYSQL_G(dbg)) dbg_skip_trace = !PDO_MYSQL_G(dbg)->m->func_enter(PDO_MYSQL_G(dbg), __LINE__, __FILE__, func_name, strlen(func_name)); +#define PDO_DBG_RETURN(value) do { if (PDO_MYSQL_G(dbg)) PDO_MYSQL_G(dbg)->m->func_leave(PDO_MYSQL_G(dbg), __LINE__, __FILE__); return (value); } while (0) +#define PDO_DBG_VOID_RETURN do { if (PDO_MYSQL_G(dbg)) PDO_MYSQL_G(dbg)->m->func_leave(PDO_MYSQL_G(dbg), __LINE__, __FILE__); return; } while (0) + +#else +#define PDO_DBG_ENABLED 0 + +static inline void PDO_DBG_INF(char *msg) {} +static inline void PDO_DBG_ERR(char *msg) {} +static inline void PDO_DBG_INF_FMT(char *format, ...) {} +static inline void PDO_DBG_ERR_FMT(char *format, ...) {} +static inline void PDO_DBG_ENTER(char *func_name) {} +#define PDO_DBG_RETURN(value) return (value) +#define PDO_DBG_VOID_RETURN return; + +#endif + +#if defined(PDO_USE_MYSQLND) +#include "ext/mysqlnd/mysqlnd_debug.h" +#endif + +#ifdef PDO_USE_MYSQLND +ZEND_BEGIN_MODULE_GLOBALS(pdo_mysql) + MYSQLND_THD_ZVAL_PCACHE *mysqlnd_thd_zval_cache; + long cache_size; +#ifndef PHP_WIN32 + char *default_socket; +#endif +#if PDO_DBG_ENABLED + char *debug; /* The actual string */ + MYSQLND_DEBUG *dbg; /* The DBG object */ +#endif +ZEND_END_MODULE_GLOBALS(pdo_mysql) + +ZEND_EXTERN_MODULE_GLOBALS(pdo_mysql); +#endif + +#ifdef ZTS +#define PDO_MYSQL_G(v) TSRMG(pdo_mysql_globals_id, zend_pdo_mysql_globals *, v) +#else +#define PDO_MYSQL_G(v) (pdo_mysql_globals.v) +#endif + typedef struct { const char *file; @@ -38,8 +98,11 @@ typedef struct { unsigned attached:1; unsigned buffered:1; unsigned emulate_prepare:1; - unsigned _reserved:31; + unsigned fetch_table_names:1; + unsigned _reserved:31; +#if !PDO_USE_MYSQLND unsigned long max_buffer_size; +#endif pdo_mysql_error_info einfo; } pdo_mysql_db_handle; @@ -51,22 +114,31 @@ typedef struct { typedef struct { pdo_mysql_db_handle *H; MYSQL_RES *result; - MYSQL_FIELD *fields; + const MYSQL_FIELD *fields; MYSQL_ROW current_data; +#if PDO_USE_MYSQLND + unsigned long *current_lengths; +#else long *current_lengths; +#endif pdo_mysql_error_info einfo; -#if HAVE_MYSQL_STMT_PREPARE - MYSQL_STMT *stmt; - +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSQLND +#if PDO_USE_MYSQLND + MYSQLND_STMT *stmt; +#else + MYSQL_STMT *stmt; +#endif int num_params; - MYSQL_BIND *params; + PDO_MYSQL_PARAM_BIND *params; +#ifndef PDO_USE_MYSQLND my_bool *in_null; - unsigned long *in_length; - - MYSQL_BIND *bound_result; + unsigned long *in_length; +#endif + PDO_MYSQL_PARAM_BIND *bound_result; my_bool *out_null; - unsigned long *out_length; - unsigned max_length:1; + unsigned long *out_length; + unsigned int params_given; + unsigned max_length:1; #endif } pdo_mysql_stmt; @@ -81,19 +153,13 @@ extern struct pdo_stmt_methods mysql_stmt_methods; enum { PDO_MYSQL_ATTR_USE_BUFFERED_QUERY = PDO_ATTR_DRIVER_SPECIFIC, PDO_MYSQL_ATTR_LOCAL_INFILE, +#ifndef PDO_USE_MYSQLND PDO_MYSQL_ATTR_INIT_COMMAND, PDO_MYSQL_ATTR_READ_DEFAULT_FILE, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, +#endif PDO_MYSQL_ATTR_DIRECT_QUERY, }; -#ifndef PDO_MYSQL_UNIX_ADDR -# ifdef PHP_WIN32 -# define PDO_MYSQL_UNIX_ADDR "MySQL" -# else -# define PDO_MYSQL_UNIX_ADDR "/tmp/mysql.sock" -# endif -#endif - #endif diff --git a/ext/pdo_mysql/tests/README b/ext/pdo_mysql/tests/README new file mode 100644 index 0000000000..920f5ef474 --- /dev/null +++ b/ext/pdo_mysql/tests/README @@ -0,0 +1,16 @@ +You must set the following environment variables to run the tests: + + PDO_MYSQL_TEST_DSN - DSN + For example: mysql:dbname=test;host=localhost;port=3306 + + PDO_MYSQL_TEST_HOST - database host + PDO_MYSQL_TEST_DB - database (schema) name + PDO_MYSQL_TEST_SOCKET - database server socket + PDO_MYSQL_TEST_ENGINE - storage engine to use + PDO_MYSQL_TEST_USER - database user + PDO_MYSQL_TEST_PASS - database user password + PDO_MYSQL_TEST_CHARSET - database charset + + NOTE: if any of PDO_MYSQL_TEST_[HOST|DB|SOCKET|ENGINE|CHARSET] is + part of PDO_MYSQL_TEST_DSN, the values must match. That is, for example, + for PDO_MYSQL_TEST_DSN = mysql:dbname=test you MUST set PDO_MYSQL_TEST_DB=test. diff --git a/ext/pdo_mysql/tests/bug_33689.phpt b/ext/pdo_mysql/tests/bug_33689.phpt index 37d0d032db..88392ef32f 100644 --- a/ext/pdo_mysql/tests/bug_33689.phpt +++ b/ext/pdo_mysql/tests/bug_33689.phpt @@ -3,16 +3,15 @@ PDO MySQL Bug #33689 (query() execute() and fetch() return false on valid select --SKIPIF-- ---INI-- -precision=14 --FILE-- exec('CREATE TABLE test (bar INT NOT NULL)'); $db->exec('INSERT INTO test VALUES(1)'); @@ -25,12 +24,20 @@ foreach ($db->query('SELECT * from test') as $row) { $stmt = $db->prepare('SELECT * from test'); print_r($stmt->getColumnMeta(0)); $stmt->execute(); -print_r($stmt->getColumnMeta(0)); +$tmp = $stmt->getColumnMeta(0); + +// libmysql and mysqlnd will show the pdo_type entry at a different position in the hash +if (!isset($tmp['pdo_type']) || (isset($tmp['pdo_type']) && $tmp['pdo_type'] != 2)) + printf("Expecting pdo_type = 2 got %s\n", $tmp['pdo_type']); +else + unset($tmp['pdo_type']); + +print_r($tmp); ?> --EXPECTF-- object(PDOStatement)#%d (1) { - [u"queryString"]=> - unicode(18) "SELECT * from test" + ["queryString"]=> + string(18) "SELECT * from test" } Array ( @@ -49,5 +56,4 @@ Array [name] => bar [len] => 11 [precision] => 0 - [pdo_type] => 2 ) diff --git a/ext/pdo_mysql/tests/bug_37445.phpt b/ext/pdo_mysql/tests/bug_37445.phpt index c4d760083d..524a3d0807 100644 --- a/ext/pdo_mysql/tests/bug_37445.phpt +++ b/ext/pdo_mysql/tests/bug_37445.phpt @@ -5,7 +5,6 @@ PDO MySQL Bug #37445 (Premature stmt object destruction) if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded'); require dirname(__FILE__) . '/config.inc'; require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; -$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); PDOTest::skip(); ?> --FILE-- @@ -17,5 +16,6 @@ $db->setAttribute(PDO :: ATTR_EMULATE_PREPARES, true); $stmt = $db->prepare("SELECT 1"); $stmt->bindParam(':a', 'b'); +?> --EXPECTF-- -Fatal error: Cannot pass parameter 2 by reference in %s/bug_37445.php on line %d \ No newline at end of file +Fatal error: Cannot pass parameter 2 by reference in %sbug_37445.php on line %d diff --git a/ext/pdo_mysql/tests/bug_39483.phpt b/ext/pdo_mysql/tests/bug_39483.phpt index f8a6135df52db6c6d2841401554f4a8de64c7fc2..c20f4a360275ab082fbd0b2ad3b530d9dcf0f221 100644 GIT binary patch delta 12 TcmdnW_JwtW0psRE#v_aXAS48U delta 16 Ycmeyux|MB%0psN9jE_Ul05xOquery('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 50000) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + +function bug_39858($db) { + + $db->exec("DROP PROCEDURE IF EXISTS p"); + $db->exec(" + CREATE PROCEDURE p() + NOT DETERMINISTIC + CONTAINS SQL + SQL SECURITY DEFINER + COMMENT '' + BEGIN + SELECT 2 * 2; + END;"); + + $stmt = $db->prepare("CALL p()"); + $stmt->execute(); + do { + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } while ($stmt->nextRowset()); + + $stmt = $db->prepare("CALL p()"); + $stmt->execute(); + do { + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } while ($stmt->nextRowset()); + $stmt->closeCursor(); + +} + +printf("Emulated Prepared Statements...\n"); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_39858($db); + +printf("Native Prepared Statements...\n"); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_39858($db); + +print "done!"; +?> +--EXPECTF-- +Emulated Prepared Statements... +array(1) { + [0]=> + array(1) { + ["2 * 2"]=> + string(1) "4" + } +} +array(1) { + [0]=> + array(1) { + ["2 * 2"]=> + string(1) "4" + } +} +Native Prepared Statements... +array(1) { + [0]=> + array(1) { + ["2 * 2"]=> + string(1) "4" + } +} +array(1) { + [0]=> + array(1) { + ["2 * 2"]=> + string(1) "4" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_41125.phpt b/ext/pdo_mysql/tests/bug_41125.phpt new file mode 100644 index 0000000000..1ed3a0cf0f --- /dev/null +++ b/ext/pdo_mysql/tests/bug_41125.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #41125 (http://bugs.php.net/bug.php?id=41125) +--SKIPIF-- +query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +die("skip $version"); +if ($version < 40100) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +$sql = "CREATE TABLE IF NOT EXISTS test(id INT); INSERT INTO test(id) VALUES (1); SELECT * FROM test; INSERT INTO test(id) VALUES (2); SELECT * FROM test;"; +// NOTE: This will fail, it is OK to fail - you must not mix DML/DDL and SELECT +// The PDO API does not support multiple queries properly! +// Read http://blog.ulf-wendel.de/?p=192 +// Compare MySQL C-API documentation +$stmt = $db->query($sql); +do { + var_dump($stmt->fetchAll()); +} while ($stmt->nextRowset()); + +print "done!"; +?> +--EXPECTF-- +Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_41698.phpt b/ext/pdo_mysql/tests/bug_41698.phpt new file mode 100644 index 0000000000..b2dee60d74 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_41698.phpt @@ -0,0 +1,37 @@ +--TEST-- +PDO MySQL Bug #41698 (float parameters truncated to integer in prepared statements) +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE test(floatval DECIMAL(8,6))'); +$db->exec('INSERT INTO test VALUES(2.34)'); +$value=4.56; +$stmt = $db->prepare('INSERT INTO test VALUES(?)'); +$stmt->execute(array($value)); +var_dump($db->query('SELECT * from test')->fetchAll(PDO::FETCH_ASSOC)); +?> +--EXPECT-- +array(2) { + [0]=> + array(1) { + ["floatval"]=> + string(8) "2.340000" + } + [1]=> + array(1) { + ["floatval"]=> + string(8) "4.560000" + } +} \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_41997.phpt b/ext/pdo_mysql/tests/bug_41997.phpt new file mode 100644 index 0000000000..60bdfee2ba --- /dev/null +++ b/ext/pdo_mysql/tests/bug_41997.phpt @@ -0,0 +1,64 @@ +--TEST-- +PDO MySQL Bug #41997 (stored procedure call returning single rowset blocks future queries) +--SKIPIF-- +query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 50000) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +exec('DROP PROCEDURE IF EXISTS p'); +$db->exec('CREATE PROCEDURE p() BEGIN SELECT 1 AS "one"; END'); + +$stmt = $db->query("CALL p()"); +do { + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); +} while ($stmt->nextRowset()); +var_dump($stmt->errorInfo()); + +$stmt = $db->query('SELECT 2 AS "two"'); +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); +var_dump($stmt->errorInfo()); +print "done!"; +?> +--EXPECT-- +array(1) { + [0]=> + array(1) { + ["one"]=> + string(1) "1" + } +} +array(1) { + [0]=> + string(5) "00000" +} +array(1) { + [0]=> + array(1) { + ["two"]=> + string(1) "2" + } +} +array(1) { + [0]=> + string(5) "00000" +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_42499.phpt b/ext/pdo_mysql/tests/bug_42499.phpt new file mode 100644 index 0000000000..6b05c17aad --- /dev/null +++ b/ext/pdo_mysql/tests/bug_42499.phpt @@ -0,0 +1,80 @@ +--TEST-- +PDO MySQL Bug #42499 (http://bugs.php.net/bug.php?id=42499) +--SKIPIF-- +query('SELECT VERSION() as _version'); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 41000) + die(sprintf("skip Need MySQL Server 4.1.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id CHAR(1)); INSERT INTO test(id) VALUES ("a")'); + + $stmt = $db->query('SELECT id AS _id FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // You must not use exec() to run statements that create a result set! + $db->exec('SELECT id FROM test'); + // This will bail at you because you have not fetched the SELECT results: this is not a bug! + $db->exec('INSERT INTO test(id) VALUES ("b")'); + +} + +print "Emulated Prepared Statements...\n"; +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); +bug_42499($db); + +print "Native Prepared Statements...\n"; +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); +bug_42499($db); + +$db = MySQLPDOTest::factory(); +$db->exec('DROP TABLE IF EXISTS test'); + +print "done!"; +?> +--EXPECTF-- +Emulated Prepared Statements... +array(1) { + [0]=> + array(1) { + ["_id"]=> + string(1) "a" + } +} + +Warning: PDO::exec(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +Native Prepared Statements... +array(1) { + [0]=> + array(1) { + ["_id"]=> + string(1) "a" + } +} + +Warning: PDO::exec(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_43371.phpt b/ext/pdo_mysql/tests/bug_43371.phpt new file mode 100644 index 0000000000..c0c65a2eca --- /dev/null +++ b/ext/pdo_mysql/tests/bug_43371.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #43371 (http://bugs.php.net/bug.php?id=43371) +--SKIPIF-- + +--FILE-- + true)); + +print "done!"; +?> +--EXPECT-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_44454.phpt b/ext/pdo_mysql/tests/bug_44454.phpt new file mode 100644 index 0000000000..a7c636a73f --- /dev/null +++ b/ext/pdo_mysql/tests/bug_44454.phpt @@ -0,0 +1,104 @@ +--TEST-- +Bug #44454 (http://bugs.php.net/bug.php?id=44454) +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(a INT, b INT, UNIQUE KEY idx_ab (a, b))'); + $db->exec('INSERT INTO test(a, b) VALUES (1, 1)'); + + $stmt = $db->query('SELECT a, b FROM test'); + printf("... SELECT has returned %d row...\n", $stmt->rowCount()); + while ($row = $stmt->fetch()) { + try { + printf("... INSERT should fail...\n"); + $db->exec('INSERT INTO test(a, b) VALUES (1, 1)'); + } catch (Exception $e) { + printf("... STMT - %s\n", var_export($stmt->errorCode(), true)); + printf("... PDO - %s\n", var_export($db->errorInfo(), true)); + } + } + + } catch (Exception $e) { + printf("... While error %s\n", $e->getMessage()); ; + } + + $stmt = $db->query('SELECT a, b FROM test'); + printf("... SELECT has returned %d row...\n", $stmt->rowCount()); + foreach ($stmt as $row) { + try { + printf("... INSERT should fail...\n"); + $db->exec('INSERT INTO test(a, b) VALUES (1, 1)'); + } catch (Exception $e) { + printf("... STMT - %s\n", var_export($stmt->errorCode(), true)); + printf("... PDO - %s\n", var_export($db->errorInfo(), true)); + } + } + +} + +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +print "Native Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_44454($db); + +print "\nEmulated Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_44454($db); + +print "done!"; +?> +--XFAIL-- +For some reason the exception gets thrown at the wrong place +--EXPECT-- +Native Prepared Statements +... SELECT has returned 1 row... +... INSERT should fail... +... STMT - '00000' +... PDO - array ( + 0 => '23000', + 1 => 1062, + 2 => 'Duplicate entry \'1-1\' for key 1', +) +... SELECT has returned 1 row... +... INSERT should fail... +... STMT - '00000' +... PDO - array ( + 0 => '23000', + 1 => 1062, + 2 => 'Duplicate entry \'1-1\' for key 1', +) + +Emulated Prepared Statements +... SELECT has returned 1 row... +... INSERT should fail... +... STMT - '00000' +... PDO - array ( + 0 => '23000', + 1 => 1062, + 2 => 'Duplicate entry \'1-1\' for key 1', +) +... SELECT has returned 1 row... +... INSERT should fail... +... STMT - '00000' +... PDO - array ( + 0 => '23000', + 1 => 1062, + 2 => 'Duplicate entry \'1-1\' for key 1', +) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_44707.phpt b/ext/pdo_mysql/tests/bug_44707.phpt new file mode 100644 index 0000000000..c7e944f2a2 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_44707.phpt @@ -0,0 +1,92 @@ +--TEST-- +Bug #44707 (http://bugs.php.net/bug.php?id=44707) = ! driver bug +--SKIPIF-- +query('SELECT VERSION() as _version'); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 41000) + die(sprintf("skip Will work different with MySQL Server < 4.1.0, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id INT, mybool TINYINT)'); + + $id = 1; + $mybool = false; + var_dump($mybool); + + $stmt = $db->prepare('INSERT INTO test(id, mybool) VALUES (?, ?)'); + $stmt->bindParam(1, $id); + // From MySQL 4.1 on boolean and TINYINT don't match! INSERT will fail. + // Versions prior to 4.1 have a weak test and will accept this. + $stmt->bindParam(2, $mybool, PDO::PARAM_BOOL); + var_dump($mybool); + + $stmt->execute(); + var_dump($mybool); + + $stmt = $db->query('SELECT * FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $db->prepare('INSERT INTO test(id, mybool) VALUES (?, ?)'); + $stmt->bindParam(1, $id); + // INT and integer work well together + $stmt->bindParam(2, $mybool, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $db->query('SELECT * FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + +} + + +/* +// This is beyond the control of the driver... - the driver never gets in touch with bound values +print "Emulated Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_44707($db); +*/ + +print "Native Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_44707($db); + +print "done!"; +?> +--EXPECT-- +Native Prepared Statements +bool(false) +bool(false) +bool(false) +array(0) { +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["mybool"]=> + string(1) "0" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_45120.phpt b/ext/pdo_mysql/tests/bug_45120.phpt new file mode 100644 index 0000000000..5f18342bf6 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_45120.phpt @@ -0,0 +1,50 @@ +--TEST-- +Bug #45120 (http://bugs.php.net/bug.php?id=45120) +--SKIPIF-- + +--FILE-- +prepare("SELECT 1 AS 'one'"); + if (true !== $stmt->execute()) + printf("[001] Execute has failed: %s\n", var_export($stmt->errorInfo(), true)); + + $res = $stmt->fetch(PDO::FETCH_ASSOC); + if ($res['one'] != 1) + printf("[002] Wrong results: %s\n", var_export($res, true)); + + if (true !== $stmt->execute()) + printf("[003] Execute has failed: %s\n", var_export($stmt->errorInfo(), true)); + + $res = $stmt->fetch(PDO::FETCH_ASSOC); + if ($res['one'] != 1) + printf("[004] Wrong results: %s\n", var_export($res, true)); + +} + +print "Emulated Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_45120($db); + +print "Native Prepared Statements\n"; +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_45120($db); + +print "done!"; +?> +--XFAIL-- +This is an open PDO bug. It is not a PDO_MYSQL bug +--EXPECT-- +Emulated Prepared Statements +Native Prepared Statements +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_pecl_12925.phpt b/ext/pdo_mysql/tests/bug_pecl_12925.phpt new file mode 100644 index 0000000000..1e7cea5e48 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_pecl_12925.phpt @@ -0,0 +1,62 @@ +--TEST-- +PDO MySQL PECL bug #1295 (http://pecl.php.net/bugs/bug.php?id=12925) +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id CHAR(1))'); + $db->exec('INSERT INTO test(id) VALUES ("a")'); + $stmt = $db->prepare('UPDATE test SET id = "b"'); + $stmt->execute(); + $stmt = $db->prepare('UPDATE test SET id = "c"'); + $stmt->execute(); + $stmt = $db->prepare('SELECT id FROM test'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt->closeCursor(); + +} + +printf("Emulated...\n"); +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_pecl_1295($db); + +printf("Native...\n"); +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_pecl_1295($db); + +$db->exec('DROP TABLE IF EXISTS test'); +print "done!"; +?> +--EXPECT-- +Emulated... +array(1) { + [0]=> + array(1) { + ["id"]=> + string(1) "c" + } +} +Native... +array(1) { + [0]=> + array(1) { + ["id"]=> + string(1) "c" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/bug_pecl_7976.phpt b/ext/pdo_mysql/tests/bug_pecl_7976.phpt new file mode 100644 index 0000000000..29ca98ded4 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_pecl_7976.phpt @@ -0,0 +1,87 @@ +--TEST-- +PDO MySQL Bug #42499 (http://bugs.php.net/bug.php?id=42499) +--SKIPIF-- +query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 50000) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +exec('DROP PROCEDURE IF EXISTS p'); + $db->exec('CREATE PROCEDURE p() BEGIN SELECT "1" AS _one; END;'); + + $stmt = $db->query('CALL p()'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt->closeCursor(); + + $stmt = $db->query('CALL p()'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt->closeCursor(); + +} + +printf("Emulated...\n"); +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +bug_pecl_7976($db); + +printf("Native...\n"); +$db = MySQLPDOTest::factory(); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); +bug_pecl_7976($db); + +$db->exec('DROP PROCEDURE IF EXISTS p'); +print "done!"; +?> +--XFAIL-- +Works with mysqlnd. It is not supported by libmysql. For libmysql is good enough to see no crash. +--EXPECT-- +Emulated... +array(1) { + [0]=> + array(1) { + ["_one"]=> + string(1) "1" + } +} +array(1) { + [0]=> + array(1) { + ["_one"]=> + string(1) "1" + } +} +Native... +array(1) { + [0]=> + array(1) { + ["_one"]=> + string(1) "1" + } +} +array(1) { + [0]=> + array(1) { + ["_one"]=> + string(1) "1" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/config.inc b/ext/pdo_mysql/tests/config.inc index 855f89bc1e..2530442c0e 100644 --- a/ext/pdo_mysql/tests/config.inc +++ b/ext/pdo_mysql/tests/config.inc @@ -1,19 +1,52 @@ $v) { putenv("$k=$v"); } + +/* MySQL specific settings */ +define('PDO_MYSQL_TEST_ENGINE', (false !== getenv('PDO_MYSQL_TEST_ENGINE')) ? getenv('PDO_MYSQL_TEST_ENGINE') : 'MyISAM'); +define('PDO_MYSQL_TEST_HOST', (false !== getenv('PDO_MYSQL_TEST_HOST')) ? getenv('PDO_MYSQL_TEST_HOST') : 'localhost'); +define('PDO_MYSQL_TEST_PORT', (false !== getenv('PDO_MYSQL_TEST_PORT')) ? getenv('PDO_MYSQL_TEST_PORT') : NULL); +define('PDO_MYSQL_TEST_DB', (false !== getenv('PDO_MYSQL_TEST_DB')) ? getenv('PDO_MYSQL_TEST_DB') : 'test'); +define('PDO_MYSQL_TEST_SOCKET', (false !== getenv('PDO_MYSQL_TEST_SOCKET')) ? getenv('PDO_MYSQL_TEST_SOCKET') : NULL); +define('PDO_MYSQL_TEST_DSN', (false !== getenv('PDO_MYSQL_TEST_DSN')) ? getenv('PDO_MYSQL_TEST_DSN') : $config['ENV']['PDOTEST_DSN']); +define('PDO_MYSQL_TEST_USER', (false !== getenv('PDO_MYSQL_TEST_USER')) ? getenv('PDO_MYSQL_TEST_USER') : $config['ENV']['PDOTEST_USER']); +define('PDO_MYSQL_TEST_PASS', (false !== getenv('PDO_MYSQL_TEST_PASS')) ? getenv('PDO_MYSQL_TEST_PASS') : $config['ENV']['PDOTEST_PASS']); +define('PDO_MYSQL_TEST_CHARSET', (false !== getenv('PDO_MYSQL_TEST_CHARSET')) ? getenv('PDO_MYSQL_TEST_CHARSET') : NULL); + +if (!function_exists('sys_get_temp_dir')) { + function sys_get_temp_dir() { + + if (!empty($_ENV['TMP'])) + return realpath( $_ENV['TMP'] ); + if (!empty($_ENV['TMPDIR'])) + return realpath( $_ENV['TMPDIR'] ); + if (!empty($_ENV['TEMP'])) + return realpath( $_ENV['TEMP'] ); + + $temp_file = tempnam(md5(uniqid(rand(), TRUE)), ''); + if ($temp_file) { + $temp_dir = realpath(dirname($temp_file)); + unlink($temp_file); + return $temp_dir; + } + return FALSE; + } +} +?> \ No newline at end of file diff --git a/ext/pdo_mysql/tests/last_insert_id.phpt b/ext/pdo_mysql/tests/last_insert_id.phpt index d20f1a71af..c819efe1e6 100644 --- a/ext/pdo_mysql/tests/last_insert_id.phpt +++ b/ext/pdo_mysql/tests/last_insert_id.phpt @@ -4,7 +4,7 @@ PDO MySQL auto_increment / last insert id --FILE-- @@ -32,4 +32,4 @@ PDOStatement Object ( [queryString] => INSERT INTO test (num) VALUES (451) ) -24 \ No newline at end of file +24 diff --git a/ext/pdo_mysql/tests/mysql_pdo_test.inc b/ext/pdo_mysql/tests/mysql_pdo_test.inc new file mode 100644 index 0000000000..bf5f49db56 --- /dev/null +++ b/ext/pdo_mysql/tests/mysql_pdo_test.inc @@ -0,0 +1,162 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + + return $db; + } + + static function createTestTable($db, $engine = null) { + if (!$engine) + $engine = PDO_MYSQL_TEST_ENGINE; + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=' . $engine); + $db->exec('INSERT INTO test(id, label) VALUES (1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), (6, "f")'); + } + + static function getTableEngine() { + return PDO_MYSQL_TEST_ENGINE; + } + + + static function getDSN($new_options = null, $addition = '') { + if (!$new_options) + return PDO_MYSQL_TEST_DSN . $addition; + + $old_options = array(); + $dsn = substr(PDO_MYSQL_TEST_DSN, + strpos(PDO_MYSQL_TEST_DSN, ':') + 1, + strlen(PDO_MYSQL_TEST_DSN)); + + // no real parser - any excotic setting can fool us + $parts = explode(';', $dsn); + foreach ($parts as $k => $v) { + $tmp = explode('=', $v); + if (count($tmp) == 2) + $old_options[$tmp[0]] = $tmp[1]; + } + + $options = $old_options; + foreach ($new_options as $k => $v) + $options[$k] = $v; + + $dsn = 'mysql:'; + foreach ($options as $k => $v) + $dsn .= sprintf('%s=%s;', $k, $v); + + if ($addition) + $dsn .= $addition; + else + $dsn = substr($dsn, 0, strlen($dsn) -1); + + return $dsn; + } + + static function getClientVersion($db) { + return self::extractVersion($db->getAttribute(PDO::ATTR_CLIENT_VERSION)); + } + + static function getServerVersion($db) { + return self::extractVersion($db->getAttribute(PDO::ATTR_SERVER_VERSION)); + } + + static function extractVersion($version_string) { + /* + TODO: + We're a bit in trouble: PDO_MYSQL returns version strings. + That's wrong according to the manual. According to the manual + integers should be returned. However, this code needs to work + with stinky PDO_MYSQL and hopefully better PDO_MYSQLND. + */ + + // already an int value? + if (is_int($version_string)) + return $version_string; + + // string but int value? + $tmp = (int)$version_string; + if (((string)$tmp) === $version_string) + return $tmp; + + // stinky string which we need to parse + $parts = explode('.', $version_string); + if (count($parts) != 3) + return -1; + + $version = (int)$parts[0] * 10000; + $version+= (int)$parts[1] * 100; + $version+= (int)$parts[2]; + + return $version; + } + + static function getTempDir() { + + if (!function_exists('sys_get_temp_dir')) { + + if (!empty($_ENV['TMP'])) + return realpath( $_ENV['TMP'] ); + if (!empty($_ENV['TMPDIR'])) + return realpath( $_ENV['TMPDIR'] ); + if (!empty($_ENV['TEMP'])) + return realpath( $_ENV['TEMP'] ); + + $temp_file = tempnam(md5(uniqid(rand(), TRUE)), ''); + if ($temp_file) { + $temp_dir = realpath(dirname($temp_file)); + unlink($temp_file); + return $temp_dir; + } + return FALSE; + } else { + return sys_get_temp_dir(); + } + + } + + static function detect_transactional_mysql_engine($db) { + foreach ($db->query("show variables like 'have%'") as $row) { + if ($row[1] == 'YES' && ($row[0] == 'have_innodb' || $row[0] == 'have_bdb')) { + return str_replace("have_", "", $row[0]); + } + } + return false; + } + + static function isPDOMySQLnd() { + ob_start(); + phpinfo(); + $tmp = ob_get_contents(); + ob_end_clean(); + return (true == stristr($tmp, 'PDO Driver for MySQL, mysql native driver version')); + } + +} +?> diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct.phpt new file mode 100644 index 0000000000..b6191d1825 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql___construct.phpt @@ -0,0 +1,303 @@ +--TEST-- +MySQL PDO->__construct() - Generic + DSN +--SKIPIF-- + +--FILE-- +getMessage(), + (isset($db) && is_object($db)) ? $db->errorCode() : 'n/a', + (isset($db) && is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + return ''; + } + + try { + + if (NULL !== ($db = @new PDO())) + printf("[001] Too few parameters\n"); + + print tryandcatch(2, '$db = new PDO(chr(0));'); + print tryandcatch(3, '$db = new PDO("a" . chr(0) . "b");'); + print tryandcatch(4, '$db = new PDO("MYSQL");'); + print tryandcatch(5, '$db = new PDO("mysql");'); + print tryandcatch(6, '$db = new PDO("mysql ");'); + print tryandcatch(7, '$db = new PDO("fantasyandfriends :");'); + + $dsn = PDO_MYSQL_TEST_DSN; + // MySQL Server might accept anonymous connections, don't + // print anything + tryandcatch(8, '$db = new PDO("' . $dsn . '");'); + + $user = 'dontcreatesuchauser'; + $pass = 'withthispassword'; + print tryandcatch(9, '$db = new PDO("' . $dsn . '", "' . $user . '", "' . $pass . '");'); + + // should fail + $dsn = 'mysql:'; + print tryandcatch(10, '$db = new PDO("' . $dsn . '", "' . $user . '", "' . $pass . '");'); + + $dsn = PDO_MYSQL_TEST_DSN; + $user = PDO_MYSQL_TEST_USER; + $pass = PDO_MYSQL_TEST_PASS; + // should work... + $db = new PDO($dsn, $user, $pass); + + $dsn = 'mysql:invalid=foo'; + print tryandcatch(11, '$db = new PDO("' . $dsn . '", "' . $user . '", "' . $pass . '");'); + + $dsn = 'mysql:' . str_repeat('howmuch=canpdoeat;', 1000); + print tryandcatch(12, '$db = new PDO("' . $dsn . '", "' . $user . '", "' . $pass . '");'); + + $dsn = 'mysql:' . str_repeat('abcdefghij', 1024 * 10) . '=somevalue'; + print tryandcatch(13, '$db = new PDO("' . $dsn . '", "' . $user . '", "' . $pass . '");'); + + if (PDO_MYSQL_TEST_HOST) { + $host = PDO_MYSQL_TEST_HOST; + $invalid_host = $host . 'invalid'; + + // last host specification should be the one used + $dsn = MySQLPDOTest::getDSN(array('host' => $host), 'host=' . $invalid_host); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, 'HY000') && !stristr($tmp, '2005') && !stristr($tmp, '2002')) + printf("[014] Cannot find proper error codes: %s\n", $tmp); + } + + $dsn = MySQLPDOTest::getDSN(array('host' => $invalid_host), 'host=' . $host); + try { $db = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { + printf("[015] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + $invalid_host = '-' . chr(0); + + $dsn = MySQLPDOTest::getDSN(array('host' => $invalid_host)); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, 'HY000') && !stristr($tmp, '2005') && !stristr($tmp, '2002')) + printf("[016] Cannot find proper error codes: %s\n", $tmp); + } + + // parsing should not get confused by chr(0) + $dsn = MySQLPDOTest::getDSN(array('host' => $invalid_host), 'host=' . $host); + try { $db = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { + printf("[017] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + } + + // what about long values for a valid option ... + $dsn = MySQLPDOTest::getDSN(array('host' => str_repeat('0123456789', 1024 * 100))); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, 'HY000') && !stristr($tmp, '2005') && !stristr($tmp, '2002')) + printf("[018] Cannot find proper error codes: %s\n", $tmp); + } + + if (PDO_MYSQL_TEST_PORT && (PDO_MYSQL_TEST_SOCKET == '')) { + // Playing with the port makes only sense if no socket gets used + + $port = PDO_MYSQL_TEST_PORT; + // let's hope we don't hit a MySQL running on that port... + $invalid_port = $port * 2; + + $dsn = MySQLPDOTest::getDSN(array('port' => $port), 'port=' . $invalid_port); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, 'HY000') && !stristr($tmp, '2005')) + printf("[019] Cannot find proper error codes: %s\n", $tmp); + } + + $dsn = MySQLPDOTest::getDSN(array('port' => $invalid_port), 'port=' . $port); + try { $db = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { + printf("[020] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + $invalid_port = 'abc'; + $dsn = MySQLPDOTest::getDSN(array('port' => $port), 'port=' . $invalid_port); + try { + $db = @new PDO($dsn, $user, $pass); + // atoi('abc') = 0, 0 -> fallback to default 3306 -> may or may not fail! + } catch (PDOException $e) { + } + + } + + if (PDO_MYSQL_TEST_DB) { + $db = PDO_MYSQL_TEST_DB; + $invalid_db = 'letshopeitdoesnotexist'; + + $dsn = MySQLPDOTest::getDSN(array('dbname' => $db), 'dbname=' . $invalid_db); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, '42000') && !stristr($tmp, '1049')) + printf("[022] Cannot find proper error codes: %s\n", $tmp); + } + + $dsn = MySQLPDOTest::getDSN(array('dbname' => $invalid_db), 'dbname=' . $db); + try { $db = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { + printf("[023] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + } + + if (PDO_MYSQL_TEST_SOCKET && (stristr(PDO_MYSQL_TEST_DSN, PDO_MYSQL_TEST_SOCKET) !== false)) { + $socket = PDO_MYSQL_TEST_SOCKET; + $invalid_socket = '/lets/hope/it/does/not/exist'; + + $dsn = MySQLPDOTest::getDSN(array('unix_socket' => $socket), 'unix_socket=' . $invalid_socket); + try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) { + $tmp = $e->getMessage(); + if (!stristr($tmp, 'HY000') && !stristr($tmp, '2002')) + printf("[024] Cannot find proper error codes: %s\n", $tmp); + } + + $dsn = MySQLPDOTest::getDSN(array('unix_socket' => $invalid_socket), 'unix_socket=' . $socket); + try { $db = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { + printf("[025] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + } + + $have_charset_support = false; + $dsn = MySQLPDOTest::getDSN(); + try { + $db = new PDO($dsn, $user, $pass); + $stmt = $db->query('SELECT VERSION() as _version'); + $version = $stmt->fetch(PDO::FETCH_ASSOC); + + $tmp = explode('.', $version['_version']); + if ((count($tmp) == 3) && + (($tmp[0] >= 4 && $tmp[1] >= 1) || ($tmp[0] >= 5))) { + // MySQL Server 4.1 - charset support available + $have_charset_support = true; + } + + } catch (PDOException $e) { + printf("[026] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + + if (PDO_MYSQL_TEST_CHARSET) { + $charset = PDO_MYSQL_TEST_CHARSET; + $invalid_charset = 'invalid'; + + if ($have_charset_support) { + $dsn = MySQLPDOTest::getDSN(); + $db = new PDO($dsn, $user, $pass); + $stmt = $db->query(sprintf('SHOW CHARACTER SET LIKE "%s"', $charset)); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $have_charset = (empty($tmp)) ? false : true; + + if ($have_charset) { + $dsn = MySQLPDOTest::getDSN(array('charset' => $charset), 'charset=' . $invalid_charset); + try { + $db = @new PDO($dsn, $user, $pass); + /* NOTE: MySQL does a fallback to the charset suggested during the handshake - no error - no bug! */ + assert(false); + printf("%s\n", $dsn); + } catch (PDOException $e) { + $tmp = $e->getMessage(); + /* TODO: add proper codes */ + if (!stristr($tmp, 'sqlstatecode') || !stristr($tmp, 'mysqlinternalerrcode')) + printf("[027] TODO - Cannot find proper error codes: %s\n", $tmp); + } + + $dsn = MySQLPDOTest::getDSN(array('charset' => $invalid_charset), 'charset=' . $charset); + try { + $db = @new PDO($dsn, $user, $pass); + /* Strictly speaking we should test more: character_set_client, character_set_results, and character_set_connection */ + $stmt = $db->query('SELECT @@character_set_connection AS _charset'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['_charset'] != $charset) + printf("[028] Character sets has not been set, @@character_set_connection reports '%s', expecting '%s'", + $tmp['_charset'], $charset); + } catch (PDOException $e) { + printf("[029] DSN=%s, %s\n", $dsn, $e->getMessage()); + } + } else { + printf("[030] You're trying to run the tests with charset '%s' which seems not supported by the server!", $charset); + } + + } + + } + + if ($have_charset_support) { + // In case the PDO_MYSQL_TEST_CHARSET interferes with any defaults + // we do another test to verify that the charset has been set. + $dsn = MySQLPDOTest::getDSN(); + $db = new PDO($dsn, $user, $pass); + $stmt = $db->query('SHOW CHARACTER SET LIKE "latin1"'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $have_latin1 =(empty($tmp)) ? false : true; + $stmt = $db->query('SHOW CHARACTER SET LIKE "latin2"'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $have_latin2 =(empty($tmp)) ? false : true; + + if ($have_latin1 && $have_latin2) { + // very likely we do have both of them... + try { + $dsn = MySQLPDOTest::getDSN(array('charset' => 'latin1')); + $db = new PDO($dsn, $user, $pass); + $stmt = $db->query('SELECT @@character_set_connection AS _charset'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['_charset'] != 'latin1') + printf("[031] DSN = %s, Character sets has not been set, @@character_set_connection reports '%s', expecting '%s'", + $dsn, $tmp['_charset'], 'latin1'); + + } catch (PDOException $e) { + printf("[032] %s\n", $e->getMessage()); + } + + try { + $dsn = MySQLPDOTest::getDSN(array('charset' => 'latin2')); + $db = new PDO($dsn, $user, $pass); + $stmt = $db->query('SELECT @@character_set_connection AS _charset'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['_charset'] != 'latin2') + printf("[033] DSN = %s, character sets has not been set, @@character_set_connection reports '%s', expecting '%s'", + $dsn, $tmp['_charset'], 'latin2'); + + } catch (PDOException $e) { + printf("[034] %s\n", $e->getMessage()); + } + + } + } + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + print "done!"; +--EXPECTF-- +[002] invalid data source name, [n/a] n/a +[003] invalid data source name, [n/a] n/a +[004] invalid data source name, [n/a] n/a +[005] invalid data source name, [n/a] n/a +[006] invalid data source name, [n/a] n/a +[007] could not find driver, [n/a] n/a +[009] SQLSTATE[28000] [1045] Access denied for user 'dont%s'@'%s' (using password: YES), [n/a] n/a +[010] SQLSTATE[28000] [1045] Access denied for user 'dont%s'@'%s' (using password: YES), [n/a] n/a +[017] DSN=%s, SQLSTATE[%s] [%d] %s + +Warning: assert(): Assertion failed in %s on line %d +mysql:%s +[033] DSN = mysql:%s, character sets has not been set, @@character_set_connection reports 'latin1', expecting 'latin2'done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt new file mode 100644 index 0000000000..5bd824b812 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt @@ -0,0 +1,56 @@ +--TEST-- +MySQL PDO->__construct() - URI +--SKIPIF-- + +--INI-- +pdo.dsn.mysql="mysql:dbname=phptest;socket=/tmp/mysql.sock" +--FILE-- + $dsn) + if ('pdo.dsn.mysql' == $name) { + printf("pdo.dsn.mysql=%s\n", $dsn); + $found = true; + break; + } + + if (!$found) { + $dsn = ini_get('pdo.dsn.mysql'); + $found = ($dsn !== false); + } + + if (!$found) + printf("pdo.dsn.mysql cannot be accessed through ini_get_all()/ini_get()\n"); + + if (MySQLPDOTest::getDSN() == $dsn) { + // we are lucky, we can run the test + try { + + $user = PDO_MYSQL_TEST_USER; + $pass = PDO_MYSQL_TEST_PASS; + $db = new PDO('mysql', $user, $pass); + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + } + + print "done!"; +--EXPECTF-- +pdo.dsn.mysql cannot be accessed through ini_get_all()/ini_get() +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt new file mode 100644 index 0000000000..8bd714d698 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt @@ -0,0 +1,170 @@ +--TEST-- +MySQL PDO->__construct(), options +--SKIPIF-- + +--FILE-- + $value)); + if (!is_object($db) || ($value !== ($tmp = @$db->getAttribute($option)))) + printf("[%03d] Execting '%s'/%s got '%s'/%s' for options '%s'\n", + $offset, + $value, gettype($value), + $tmp, gettype($tmp), + $option_desc); + } catch (PDOException $e) { + printf("[%03d] %s\n", $offset, $e->getMessage()); + } + + } + + try { + + $dsn = MySQLPDOTest::getDSN(); + $user = PDO_MYSQL_TEST_USER; + $pass = PDO_MYSQL_TEST_PASS; + + $valid_options = array( + /* pdo_dbh.c */ + PDO::ATTR_PERSISTENT => 'PDO::ATTR_PERSISTENT', + PDO::ATTR_AUTOCOMMIT => 'PDO::ATTR_AUTOCOMMIT', + /* mysql_driver.c */ + /* TODO Possible bug PDO::ATTR_TIMEOUT != MYSQLI_OPT_CONNECT_TIMEOUT*/ + PDO::ATTR_TIMEOUT => 'PDO::ATTR_TIMEOUT', + PDO::ATTR_EMULATE_PREPARES => 'PDO::ATTR_EMULATE_PREPARES', + + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => 'PDO::MYSQL_ATTR_USE_BUFFERED_QUERY', + PDO::MYSQL_ATTR_LOCAL_INFILE => 'PDO::MYSQL_ATTR_LOCAL_INFILE', + PDO::MYSQL_ATTR_DIRECT_QUERY => 'PDO::MYSQL_ATTR_DIRECT_QUERY', + ); + + $defaults = array( + PDO::ATTR_PERSISTENT => false, + PDO::ATTR_AUTOCOMMIT => 1, + /* TODO - why is this a valid option if getAttribute() does not support it?! */ + PDO::ATTR_TIMEOUT => false, + PDO::ATTR_EMULATE_PREPARES => 1, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => 1, + /* TODO getAttribute() does not handle it */ + PDO::MYSQL_ATTR_LOCAL_INFILE => false, + /* TODO getAttribute() does not handle it */ + PDO::MYSQL_ATTR_DIRECT_QUERY => 1, + ); + + if (NULL !== ($db = @new PDO($dsn, $user, $pass, 'wrong type'))) + printf("[001] Expecting NULL got %s/%s\n", gettype($db), $db); + + if (!is_object($db = new PDO($dsn, $user, $pass, array()))) + printf("[002] Expecting object got %s/%s¸\n", gettype($db), $db); + + do { + $invalid = mt_rand(-1000, 1000); + } while (isset($valid_options[$invalid])); + if (is_object($db = new PDO($dsn, $user, $pass, array($invalid => true)))) + printf("[003] [TODO][CHANGEREQUEST] Please, lets not ignore invalid options and bail out!\n"); + + $db = new PDO($dsn, $user, $pass); + foreach ($valid_options as $option => $name) { + /* TODO getAttribute() is pretty poor in supporting the options, suppress errors */ + $tmp = @$db->getAttribute($option); + if ($tmp !== $defaults[$option]) + printf("[003a] Expecting default value for '%s' of '%s'/%s, getAttribute() reports setting '%s'/%s\n", + $name, $defaults[$option], gettype($defaults[$option]), + $tmp, gettype($tmp)); + } + + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_AUTOCOMMIT => true)); + if (!is_object($db) || !$db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[004] Autocommit should be on\n"); + + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_AUTOCOMMIT => false)); + if (!is_object($db) || $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[005] Autocommit should be off\n"); + + /* TODO: no way to check ATTR_TIMEOUT settings */ + if (!is_object($db = new PDO($dsn, $user, $pass, array(PDO::ATTR_TIMEOUT => 10)))) + printf("[006] ATTR_TIMEOUT should be accepted\n"); + + if (!is_object($db = new PDO($dsn, $user, $pass, array(PDO::ATTR_TIMEOUT => PHP_INT_MAX)))) + printf("[007] ATTR_TIMEOUT should be accepted\n"); + + if (!is_object($db = new PDO($dsn, $user, $pass, array(PDO::ATTR_TIMEOUT => -PHP_INT_MAX)))) + printf("[008] ATTR_TIMEOUT should be accepted\n"); + + /* TODO: Its ugly that PDO::ATTR_EMULATE_PREPARES == PDO::MYSQL_ATTR_DIRECT_QUERY */ + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_EMULATE_PREPARES => true)); + if (!is_object($db)) + printf("[009] ATTR_EMULATE_PREPARES should be accepted and on\n"); + if (!$db->getAttribute(PDO::ATTR_EMULATE_PREPARES)) + printf("[010] [TODO][CHANGEREQUEST] ATTR_EMULATE_PREPARES should be on\n"); + if (!$db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[011] As PDO::MYSQL_ATTR_DIRECT_QUERY == PDO::ATTR_EMULATE_PREPARES + and PDO::ATTR_EMULATE_PREPARES overrules the other, PDO::MYSQL_ATTR_DIRECT_QUERY should be on\n"); + + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_EMULATE_PREPARES => false)); + if (!is_object($db)) + printf("[012] ATTR_EMULATE_PREPARES should be accepted and on\n"); + if ($db->getAttribute(PDO::ATTR_EMULATE_PREPARES)) + printf("[013] [TODO][CHANGEREQUEST] ATTR_EMULATE_PREPARES should be off\n"); + if ($db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[014] As PDO::MYSQL_ATTR_DIRECT_QUERY == PDO::ATTR_EMULATE_PREPARES + and PDO::ATTR_EMULATE_PREPARES overrules the other, PDO::MYSQL_ATTR_DIRECT_QUERY should be off\n"); + + // PDO::ATTR_EMULATE_PREPARES overrules PDO::MYSQL_ATTR_DIRECT_QUERY + // TODO: is it clever that a generic setting overrules a specific setting? + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_EMULATE_PREPARES => true, PDO::MYSQL_ATTR_DIRECT_QUERY => false)); + if (!$db->getAttribute(PDO::ATTR_EMULATE_PREPARES)) + printf("[015] PDO::ATTR_EMULATE_PREPARES should be on\n"); + if (!$db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[016] PDO::MYSQL_ATTR_DIRECT_QUERY should be on\n"); + + $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_DIRECT_QUERY => true)); + if ($db->getAttribute(PDO::ATTR_EMULATE_PREPARES)) + printf("[017] PDO::ATTR_EMULATE_PREPARES should be off\n"); + if ($db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[018] PDO::MYSQL_ATTR_DIRECT_QUERY should be off\n"); + + set_option_and_check(19, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1, 'PDO::MYSQL_ATTR_USE_BUFFERED_QUERY'); + set_option_and_check(20, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 0, 'PDO::MYSQL_ATTR_USE_BUFFERED_QUERY'); + + set_option_and_check(21, PDO::MYSQL_ATTR_LOCAL_INFILE, true, 'PDO::MYSQL_ATTR_LOCAL_INFILE'); + set_option_and_check(22, PDO::MYSQL_ATTR_LOCAL_INFILE, false, 'PDO::MYSQL_ATTR_LOCAL_INFILE'); + + set_option_and_check(33, PDO::MYSQL_ATTR_DIRECT_QUERY, 1, 'PDO::MYSQL_ATTR_DIRECT_QUERY'); + set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, 0, 'PDO::MYSQL_ATTR_DIRECT_QUERY'); + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + print "done!"; +--EXPECTF-- +[003] [TODO][CHANGEREQUEST] Please, lets not ignore invalid options and bail out! +[003a] Expecting default value for 'PDO::ATTR_EMULATE_PREPARES' of '1'/integer, getAttribute() reports setting ''/boolean + +Warning: PDO::getAttribute(): SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute in %s on line %d +[010] [TODO][CHANGEREQUEST] ATTR_EMULATE_PREPARES should be on + +Warning: PDO::getAttribute(): SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute in %s on line %d + +Warning: PDO::getAttribute(): SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute in %s on line %d +[015] PDO::ATTR_EMULATE_PREPARES should be on +[016] PDO::MYSQL_ATTR_DIRECT_QUERY should be on + +Warning: PDO::getAttribute(): SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute in %s on line %d +[018] PDO::MYSQL_ATTR_DIRECT_QUERY should be off +[021] Execting '1'/boolean got ''/boolean' for options 'PDO::MYSQL_ATTR_LOCAL_INFILE' +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt new file mode 100644 index 0000000000..8a8e347a63 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt @@ -0,0 +1,88 @@ +--TEST-- +MySQL PDO->__construct(), libmysql only options +--SKIPIF-- + +--FILE-- + $value)); + if (!is_object($db) || (!$ignore_diff && ($value !== ($tmp = @$db->getAttribute($option))))) + printf("[%03d] Execting '%s'/%s got '%s'/%s' for options '%s'\n", + $offset, + $value, gettype($value), + $tmp, gettype($tmp), + $option_desc); + } catch (PDOException $e) { + printf("[%03d] %s\n", $offset, $e->getMessage()); + } + + } + + try { + + $dsn = MySQLPDOTest::getDSN(); + $user = PDO_MYSQL_TEST_USER; + $pass = PDO_MYSQL_TEST_PASS; + + $valid_options = array(); + $valid_options[PDO::MYSQL_ATTR_MAX_BUFFER_SIZE] = 'PDO::MYSQL_ATTR_MAX_BUFFER_SIZE'; + $valid_options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'PDO::MYSQL_ATTR_INIT_COMMAND'; + $valid_options[PDO::MYSQL_ATTR_READ_DEFAULT_FILE] = 'PDO::MYSQL_ATTR_READ_DEFAULT_FILE'; + $valid_options[PDO::MYSQL_ATTR_READ_DEFAULT_GROUP] = 'PDO::MYSQL_ATTR_READ_DEFAULT_GROUP'; + + $defaults[PDO::MYSQL_ATTR_MAX_BUFFER_SIZE] = 1048576; + /* TODO getAttribute() does not handle it */ + $defaults[PDO::MYSQL_ATTR_INIT_COMMAND] = ''; + $defaults[PDO::MYSQL_ATTR_READ_DEFAULT_FILE] = false; + $defaults[PDO::MYSQL_ATTR_READ_DEFAULT_GROUP] = false; + + $db = new PDO($dsn, $user, $pass); + foreach ($valid_options as $option => $name) { + /* TODO getAttribute() is pretty poor in supporting the options, suppress errors */ + $tmp = @$db->getAttribute($option); + if ($tmp !== $defaults[$option]) + printf("[001] Expecting default value for '%s' of '%s'/%s, getAttribute() reports setting '%s'/%s\n", + $name, $defaults[$option], gettype($defaults[$option]), + $tmp, gettype($tmp)); + } + + set_option_and_check(23, PDO::MYSQL_ATTR_INIT_COMMAND, 'SET @a=1', 'PDO::MYSQL_ATTR_INIT_COMMAND'); + set_option_and_check(24, PDO::MYSQL_ATTR_INIT_COMMAND, '', 'PDO::MYSQL_ATTR_INIT_COMMAND'); + set_option_and_check(25, PDO::MYSQL_ATTR_INIT_COMMAND, 'INSERT INTO nonexistent(invalid) VALUES (1)', 'PDO::MYSQL_ATTR_INIT_COMMAND'); + + set_option_and_check(26, PDO::MYSQL_ATTR_READ_DEFAULT_FILE, true, 'PDO::MYSQL_ATTR_READ_DEFAULT_FILE'); + set_option_and_check(27, PDO::MYSQL_ATTR_READ_DEFAULT_FILE, false, 'PDO::MYSQL_ATTR_READ_DEFAULT_FILE'); + + set_option_and_check(30, PDO::MYSQL_ATTR_MAX_BUFFER_SIZE, -1, 'PDO::MYSQL_ATTR_MAX_BUFFER_SIZE', true); + set_option_and_check(31, PDO::MYSQL_ATTR_MAX_BUFFER_SIZE, PHP_INT_MAX, 'PDO::MYSQL_ATTR_MAX_BUFFER_SIZE'); + set_option_and_check(32, PDO::MYSQL_ATTR_MAX_BUFFER_SIZE, 1, 'PDO::MYSQL_ATTR_MAX_BUFFER_SIZE'); + + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + print "done!"; +--EXPECTF-- +[001] Expecting default value for 'PDO::MYSQL_ATTR_INIT_COMMAND' of ''/string, getAttribute() reports setting ''/boolean +[023] Execting 'SET @a=1'/string got ''/boolean' for options 'PDO::MYSQL_ATTR_INIT_COMMAND' +[024] SQLSTATE[42000] [1065] Query was empty +[025] SQLSTATE[42S02] [1146] Table '%snonexistent' doesn't exist +[026] Execting '1'/boolean got ''/boolean' for options 'PDO::MYSQL_ATTR_READ_DEFAULT_FILE' +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt new file mode 100644 index 0000000000..87e1a1ee06 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt @@ -0,0 +1,75 @@ +--TEST-- +MySQL PDO->__construct() - URI +--SKIPIF-- + +--FILE-- +getMessage()); + } + unlink($file); + } + + if ($fp = @fopen($file, 'w')) { + fwrite($fp, sprintf('mysql:dbname=letshopeinvalid;%s%s', + chr(0), $dsn)); + fclose($fp); + clearstatcache(); + assert(file_exists($file)); + try { + $db = new PDO($uri, $user, $pass); + } catch (PDOException $e) { + printf("[003] URI=%s, DSN=%s, File=%s (%d bytes, '%s'), chr(0) test, %s\n", + $uri, $dsn, + $file, filesize($file), file_get_contents($file), + $e->getMessage()); + } + unlink($file); + } + + } + + /* TODO: safe mode */ + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + print "done!"; +--EXPECTF-- +Warning: PDO::__construct(%s +[002] URI=uri:file:%spdomuri.tst, DSN=mysql%sdbname=%s, File=%spdomuri.tst (%d bytes, 'mysql%sdbname=%s'), invalid data source URI + +Warning: PDO::__construct(%s +[003] URI=uri:file:%spdomuri.tst, DSN=mysql%sdbname=%s, File=%spdomuri.tst (%d bytes, 'mysql%sdbname=letshopeinvalid%s'), chr(0) test, invalid data source URI +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt new file mode 100644 index 0000000000..b48952875e --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt @@ -0,0 +1,93 @@ +--TEST-- +PDO::ATTR_AUTOCOMMIT +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_AUTOCOMMIT))) + printf("[001] Expecting int/1 got %s\n", var_export($tmp, true)); + + // lets see if the server agrees to that + $row = $db->query('SELECT @@autocommit AS _autocommit')->fetch(PDO::FETCH_ASSOC); + if (!$row['_autocommit']) + printf("[002] Server autocommit mode should be on, got '%s'\n", var_export($row['_autocommit'])); + + // on -> off + if (!$db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0)) + printf("[003] Cannot turn off autocommit\n"); + + $row = $db->query('SELECT @@autocommit AS _autocommit')->fetch(PDO::FETCH_ASSOC); + if ($row['_autocommit']) + printf("[004] Server autocommit mode should be off, got '%s'\n", var_export($row['_autocommit'])); + + // PDO thinks autocommit is off, but its manually turned on... + if (!$db->query('SET autocommit = 1')) + printf("[005] Cannot turn on server autocommit mode, %s\n", var_export($db->errorInfo(), true)); + + if (0 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT))) + printf("[006] Expecting int/0 got %s\n", var_export($tmp, true)); + + // off -> on + if (!$db->query('SET autocommit = 0')) + printf("[007] Cannot turn off server autocommit mode, %s\n", var_export($db->errorInfo(), true)); + + if (!$db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1)) + printf("[008] Cannot turn on autocommit\n"); + + $row = $db->query('SELECT @@autocommit AS _autocommit')->fetch(PDO::FETCH_ASSOC); + if (!$row['_autocommit']) + printf("[009] Server autocommit mode should be on, got '%s'\n", var_export($row['_autocommit'])); + + if (1 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT))) + printf("[010] Expecting int/1 got %s\n", var_export($tmp, true)); + + if (MySQLPDOTest::detect_transactional_mysql_engine($db)) { + // nice, we have a transactional engine to play with + + MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db)); + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + $num = $row['_num']; + + $db->query('INSERT INTO test(id, label) VALUES (100, "z")'); + $num++; + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[011] Insert has failed, test will fail\n"); + + // autocommit is on, no rollback possible + $db->query('ROLLBACK'); + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[012] ROLLBACK should not have undone anything\n"); + + if (!$db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0)) + printf("[013] Cannot turn off autocommit\n"); + + $db->query('DELETE FROM test WHERE id = 100'); + $db->query('ROLLBACK'); + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[014] ROLLBACK should have undone the DELETE\n"); + + $db->query('DELETE FROM test WHERE id = 100'); + $db->query('COMMIT'); + $num--; + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[015] DELETE should have been committed\n"); + + } + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt new file mode 100644 index 0000000000..c7a2037276 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt @@ -0,0 +1,219 @@ +--TEST-- +PDO::ATTR_CASE +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_CASE); + $known = array( + PDO::CASE_LOWER => 'PDO::CASE_LOWER', + PDO::CASE_UPPER => 'PDO::CASE_UPPER', + PDO::CASE_NATURAL => 'PDO::CASE_NATURAL' + ); + if (!isset($known[$default])) + printf("[001] getAttribute(PDO::ATTR_CASE) returns unknown value '%s'\n", + var_export($default, true)); + else + var_dump($known[$default]); + + // lets see what the default is... + if (!is_object($stmt = $db->query('SELECT id, id AS "ID_UPPER", label FROM test ORDER BY id ASC LIMIT 2'))) + printf("[002] %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + var_dump($stmt->fetchAll(PDO::FETCH_BOTH)); + + if (true !== $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER)) + printf("[003] Cannot set PDO::ATTR_CASE = PDO::CASE_LOWER, %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + if (($tmp = $db->getAttribute(PDO::ATTR_CASE)) !== PDO::CASE_LOWER) + printf("[004] getAttribute(PDO::ATTR_CASE) returns wrong value '%s'\n", + var_export($tmp, true)); + + if (true === $db->exec('ALTER TABLE test ADD MiXeD CHAR(1)')) + printf("[005] Cannot add column %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + if (false === $db->exec('ALTER TABLE test ADD MYUPPER CHAR(1)')) + printf("[006] Cannot add column %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + if (!is_object($stmt = $db->query('SELECT id, id AS "ID_UPPER", label, MiXeD, MYUPPER FROM test ORDER BY id ASC LIMIT 2'))) + printf("[007] %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + var_dump($stmt->fetchAll(PDO::FETCH_BOTH)); + + if (true !== $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER)) + printf("[008] Cannot set PDO::ATTR_CASE = PDO::CASE_UPPER %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + if (($tmp = $db->getAttribute(PDO::ATTR_CASE)) !== PDO::CASE_UPPER) + printf("[009] getAttribute(PDO::ATTR_CASE) returns wrong value '%s'\n", + var_export($tmp, true)); + + if (!is_object($stmt = $db->query('SELECT id, label, MiXeD, MYUPPER, MYUPPER AS "lower" FROM test ORDER BY id ASC LIMIT 1'))) + printf("[010] %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + var_dump($stmt->fetchAll(PDO::FETCH_BOTH)); + + if (true !== $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL)) + printf("[011] Cannot set PDO::ATTR_CASE = PDO::CASE_NATURAL %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + if (($tmp = $db->getAttribute(PDO::ATTR_CASE)) !== PDO::CASE_NATURAL) + printf("[012] getAttribute(PDO::ATTR_CASE) returns wrong value '%s'\n", + var_export($tmp, true)); + + if (!is_object($stmt = $db->query('SELECT id, label, MiXeD, MYUPPER, id AS "ID" FROM test ORDER BY id ASC LIMIT 1'))) + printf("[013] %s - %s\n", + var_export($db->errorInfo(), true), var_export($db->errorCode(), true)); + + var_dump($stmt->fetchAll(PDO::FETCH_BOTH)); + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +string(15) "PDO::CASE_LOWER" +array(2) { + [0]=> + array(6) { + ["id"]=> + string(1) "1" + [0]=> + string(1) "1" + ["id_upper"]=> + string(1) "1" + [1]=> + string(1) "1" + ["label"]=> + string(1) "a" + [2]=> + string(1) "a" + } + [1]=> + array(6) { + ["id"]=> + string(1) "2" + [0]=> + string(1) "2" + ["id_upper"]=> + string(1) "2" + [1]=> + string(1) "2" + ["label"]=> + string(1) "b" + [2]=> + string(1) "b" + } +} +array(2) { + [0]=> + array(10) { + ["id"]=> + string(1) "1" + [0]=> + string(1) "1" + ["id_upper"]=> + string(1) "1" + [1]=> + string(1) "1" + ["label"]=> + string(1) "a" + [2]=> + string(1) "a" + ["mixed"]=> + NULL + [3]=> + NULL + ["myupper"]=> + NULL + [4]=> + NULL + } + [1]=> + array(10) { + ["id"]=> + string(1) "2" + [0]=> + string(1) "2" + ["id_upper"]=> + string(1) "2" + [1]=> + string(1) "2" + ["label"]=> + string(1) "b" + [2]=> + string(1) "b" + ["mixed"]=> + NULL + [3]=> + NULL + ["myupper"]=> + NULL + [4]=> + NULL + } +} +array(1) { + [0]=> + array(10) { + ["ID"]=> + string(1) "1" + [0]=> + string(1) "1" + ["LABEL"]=> + string(1) "a" + [1]=> + string(1) "a" + ["MIXED"]=> + NULL + [2]=> + NULL + ["MYUPPER"]=> + NULL + [3]=> + NULL + ["LOWER"]=> + NULL + [4]=> + NULL + } +} +array(1) { + [0]=> + array(10) { + ["id"]=> + string(1) "1" + [0]=> + string(1) "1" + ["label"]=> + string(1) "a" + [1]=> + string(1) "a" + ["MiXeD"]=> + NULL + [2]=> + NULL + ["MYUPPER"]=> + NULL + [3]=> + NULL + ["ID"]=> + string(1) "1" + [4]=> + string(1) "1" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt new file mode 100644 index 0000000000..e3f21fbaf1 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt @@ -0,0 +1,36 @@ +--TEST-- +PDO::ATTR_CLIENT_VERSION +--SKIPIF-- + +--FILE-- +errorCode()) || ('00000' == $db->errorCode())); + + $version = $db->getAttribute(PDO::ATTR_CLIENT_VERSION); + + // No more constraints - mysqlnd and libmysql return different strings at least + // with mysqli. Return type check is already performed in the generic test. + // According to the manual we should get an int but as of today we do get a string... + if ('' == $version) + printf("[001] Client version must not be empty\n"); + + + // Read-only + if (false !== $db->setAttribute(PDO::ATTR_CLIENT_VERSION, '1.0')) + printf("[002] Wonderful, I can change the client version!\n"); + + $new_version = $db->getAttribute(PDO::ATTR_CLIENT_VERSION); + if ($new_version !== $version) + printf("[003] Did we change it from '%s' to '%s'?\n", $version, $new_version); + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt new file mode 100644 index 0000000000..038df3cdbc --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt @@ -0,0 +1,36 @@ +--TEST-- +PDO::ATTR_CONNECTION_STATUS +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_CONNECTION_STATUS); + if (ini_get('unicode.semantics')) { + if (!is_unicode($status)) + printf("[001] Expecting unicode, got '%s'\n", var_export($status, true)); + } else { + if (!is_string($status)) + printf("[002] Expecting string, got '%s'\n", var_export($status, true)); + } + + if ('' == $status) + printf("[003] Connection status string must not be empty\n"); + + if (false !== $db->setAttribute(PDO::ATTR_CONNECTION_STATUS, 'my own connection status')) + printf("[004] Changing read only attribute\n"); + + $status2 = $db->getAttribute(PDO::ATTR_CONNECTION_STATUS); + if ($status !== $status2) + printf("[005] Connection status should not have changed\n"); + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt new file mode 100644 index 0000000000..0c156e4d1b --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt @@ -0,0 +1,30 @@ +--TEST-- +PDO::ATTR_DRIVER_NAME +--SKIPIF-- + +--FILE-- +errorCode()) || ('00000' == $db->errorCode())); + + $name = $db->getAttribute(PDO::ATTR_DRIVER_NAME); + var_dump($name); + + if (false !== $db->setAttribute(PDO::ATTR_DRIVER_NAME, 'mydriver')) + printf("[001] Wonderful, I can create new PDO drivers!\n"); + + $new_name = $db->getAttribute(PDO::ATTR_DRIVER_NAME); + if ($name != $new_name) + printf("[002] Did we change it from '%s' to '%s'?\n", $name, $new_name); + + print "done!"; +--EXPECTF-- +string(5) "mysql" +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt new file mode 100644 index 0000000000..b03708900f --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt @@ -0,0 +1,166 @@ +--TEST-- +PDO::ATTR_ERRMODE +--SKIPIF-- + +--INI-- +error_reporting=E_ALL +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, $tmp)) + printf("[001] Maybe PDO could indicate that this is not a proper way of setting the ERRMODE...\n"); + + $tmp = new stdClass(); + $ret = @$db->setAttribute(PDO::ATTR_ERRMODE, $tmp); + if (false != $ret) + printf("[002] Maybe PDO could indicate that this is not a proper way of setting the ERRMODE...%s\n", + var_export($ret, true)); + + $ret = @$db->setAttribute(PDO::ATTR_ERRMODE, 'pdo'); + if (false != $ret) + printf("[003] Maybe PDO could indicate that this is not a proper way of setting the ERRMODE...%s\n", + var_export($ret, true)); + + if (false != @$db->setAttribute(PDO::ATTR_ERRMODE, $invalid)) + printf("[004] Invalid ERRMODE should be rejected\n"); + + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + // no message for any PDO call but... + $db->query('THIS IS NOT VALID SQL'); + // ... still messages for everything else + $code = $db->errorCode(); + $info = $db->errorInfo(); + + if ($code != '42000') + printf("[005] Expecting SQL code 42000 got '%s'\n", $code); + if ($code !== $info[0]) + printf("[006] Code and info should be identical, got errorCode() = %s, errorInfo()[0] = %s\n", + $code, $info[0]); + if ('' == $info[1]) + printf("[007] Driver specific error code not set\n"); + if ('' == $info[2]) + printf("[008] Driver specific error message not set\n"); + + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $db->query('THIS IS NOT VALID SQL'); + + $code = $db->errorCode(); + $info = $db->errorInfo(); + + if ($code != '42000') + printf("[009] Expecting SQL code 42000 got '%s'\n", $code); + if ($code !== $info[0]) + printf("[010] Code and info should be identical, got errorCode() = %s, errorInfo()[0] = %s\n", + $code, $info[0]); + if ('' == $info[1]) + printf("[011] Driver specific error code not set\n"); + if ('' == $info[2]) + printf("[012] Driver specific error message not set\n"); + + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + try { + $line = __LINE__ + 1; + $db->query('THIS IS NOT VALID SQL'); + } catch (PDOException $e) { + + $code = $db->errorCode(); + $info = $db->errorInfo(); + + if ($code != '42000') + printf("[013] Expecting SQL code 42000 got '%s'\n", $code); + if ($code !== $info[0]) + printf("[014] Code and info should be identical, got errorCode() = %s, errorInfo()[0] = %s\n", + $code, $info[0]); + if ('' == $info[1]) + printf("[015] Driver specific error code not set\n"); + if ('' == $info[2]) + printf("[016] Driver specific error message not set\n"); + + if ($e->getCode() !== $code) + printf("[017] Exception code '%s' differs from errorCode '%s'\n", + $e->getCode(), $code); + + $msg = $e->getMessage(); + foreach ($info as $k => $v) { + if (false === stristr($msg, (string)$v)) { + printf("[018] Cannot find all parts of the error info ('%s') in the exception message '%s'\n", + $v, $msg); + } + } + + if ($e->getLine() !== $line) + printf("[019] Exception has been thrown in line %d, exception object reports line %d\n", + $line, $e->getLine()); + + if ($e->getFile() !== __FILE__) + printf("[020] Exception has been thrown in file '%s', exception object reports file '%s'\n", + __FILE__, $e->getFile()); + + } + + function my_handler($e) { + global $db, $line; + + $code = $db->errorCode(); + $info = $db->errorInfo(); + + if ($code != '42000') + printf("[021] Expecting SQL code 42000 got '%s'\n", $code); + if ($code !== $info[0]) + printf("[022] Code and info should be identical, got errorCode() = %s, errorInfo()[0] = %s\n", + $code, $info[0]); + if ('' == $info[1]) + printf("[023] Driver specific error code not set\n"); + if ('' == $info[2]) + printf("[024] Driver specific error message not set\n"); + + if ($e->getCode() !== $code) + printf("[025] Exception code '%s' differs from errorCode '%s'\n", + $e->getCode(), $code); + + $msg = $e->getMessage(); + foreach ($info as $k => $v) { + if (false === stristr($msg, (string)$v)) { + printf("[026] Cannot find all parts of the error info ('%s') in the exception message '%s'\n", + $v, $msg); + } + } + + if ($e->getLine() !== $line) + printf("[027] Exception has been thrown in line %d, exception object reports line %d\n", + $line, $e->getLine()); + + if ($e->getFile() !== __FILE__) + printf("[028] Exception has been thrown in file '%s', exception object reports file '%s'\n", + __FILE__, $e->getFile()); + + if (get_class($e) != 'PDOException') + printf("[029] Expecting PDO exception got exception of type '%s'\n", get_class($e)); + + print "\nend of execution"; + } + set_exception_handler('my_handler'); + $line = __LINE__ + 1; + $db->query('THIS IS NOT VALID SQL'); + + print "done!\n"; +--EXPECTF-- +[003] Maybe PDO could indicate that this is not a proper way of setting the ERRMODE...true + +Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: %d You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%s' at line %d in %s on line %d + +end of execution \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt new file mode 100644 index 0000000000..3b2b0929e4 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt @@ -0,0 +1,41 @@ +--TEST-- +PDO::ATTR_FETCH_TABLE_NAMES +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, 1); + $stmt = $db->query('SELECT label FROM test LIMIT 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt->closeCursor(); + + $db->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, 0); + $stmt = $db->query('SELECT label FROM test LIMIT 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt->closeCursor(); + + print "done!"; +--EXPECTF-- +array(1) { + [0]=> + array(1) { + ["test.label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "a" + } +} +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt new file mode 100644 index 0000000000..6b13465416 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt @@ -0,0 +1,51 @@ +--TEST-- +PDO::MYSQL_ATTR_INIT_COMMAND +--SKIPIF-- + +--INI-- +error_reporting=E_ALL +--FILE-- +exec(sprintf('DROP TABLE IF EXISTS %s', $table)); + + $create = sprintf('CREATE TABLE %s(id INT)', $table); + var_dump($create); + $db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => $create)); + + var_dump($db->errorInfo()); + + $db->exec(sprintf('INSERT INTO %s(id) VALUES (1)', $table)); + $stmt = $db->query(sprintf('SELECT id FROM %s', $table)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->exec(sprintf('DROP TABLE IF EXISTS %s', $table)); + print "done!\n"; +--EXPECTF-- +string(58) "CREATE TABLE test_%s(id INT)" +array(1) { + [0]=> + string(5) "00000" +} +array(1) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt new file mode 100644 index 0000000000..9ba8dee85f --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt @@ -0,0 +1,70 @@ +--TEST-- +MySQL PDO->__construct(), PDO::MYSQL_ATTR_MAX_BUFFER_SIZE +--SKIPIF-- + +--FILE-- + $buffer_size, + /* buffer is only relevant with native PS */ + PDO::MYSQL_ATTR_DIRECT_QUERY => 0, + PDO::ATTR_EMULATE_PREPARES => 0, + )); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, val LONGBLOB) ENGINE = %s', PDO_MYSQL_TEST_ENGINE)); + + // 10 * (10 * 1024) = 10 * (10 * 1k) = 100k + $db->exec('INSERT INTO test(id, val) VALUES (1, REPEAT("01234567890", 10240))'); + + $stmt = $db->prepare('SELECT id, val FROM test'); + $stmt->execute(); + + $id = $val = NULL; + $stmt->bindColumn(1, $id); + $stmt->bindColumn(2, $val); + while ($row = $stmt->fetch(PDO::FETCH_BOUND)) { + printf("[%03d] id = %d, val = %s... (length: %d)\n", + $offset, $id, substr($val, 0, 10), strlen($val)); + } + $db->exec('DROP TABLE IF EXISTS test'); + + } catch (PDOException $e) { + printf("[%03d] %s, [%s] %s\n", + $offset, + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + } + + try_buffer_size(1, -1); + try_buffer_size(2, 1000); + try_buffer_size(3, NULL); + try_buffer_size(4, 2000); + + print "done!"; +--EXPECTF-- +[001] id = 1, val = 0123456789... (length: %d) +[002] id = 1, val = 0123456789... (length: 1000) +[003] id = 1, val = 0123456789... (length: %d) +[004] id = 1, val = 0123456789... (length: 2000) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt new file mode 100644 index 0000000000..9e938ef6a9 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt @@ -0,0 +1,120 @@ +--TEST-- +PDO::ATTR_ORACLE_NULLS +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ORACLE_NULLS, $tmp)) + printf("[001] Maybe PDO could indicate that this is not a proper way of setting ATTR_ORACLE_NULLS...\n"); + + $tmp = new stdClass(); + if (false !== @$db->setAttribute(PDO::ATTR_ORACLE_NULLS, $tmp)); + printf("[002] Maybe PDO could indicate that this is not a proper way of setting ATTR_ORACLE_NULLS...\n"); + + if (false !== @$db->setAttribute(PDO::ATTR_ORACLE_NULLS, 'pdo')) + printf("[003] Maybe PDO could indicate that this is not a proper way of setting ATTR_ORACLE_NULLS...\n"); + + $db->setAttribute(PDO::ATTR_ORACLE_NULLS, 1); + $stmt = $db->query('SELECT NULL AS z, "" AS a, " " AS b, TRIM(" ") as c, " d" AS d, "' . chr(0) . ' e" AS e'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->setAttribute(PDO::ATTR_ORACLE_NULLS, 0); + $stmt = $db->query('SELECT NULL AS z, "" AS a, " " AS b, TRIM(" ") as c, " d" AS d, "' . chr(0) . ' e" AS e'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->setAttribute(PDO::ATTR_ORACLE_NULLS, 1); + $stmt = $db->query('SELECT VERSION() as _version'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ((int)substr($row['_version'], 0, 1) >= 5) + $have_procedures = true; + else + $have_procedures = false; + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + + if ($have_procedures && (false !== $db->exec('DROP PROCEDURE IF EXISTS p')) && + (false !== $db->exec('CREATE PROCEDURE p() BEGIN SELECT NULL as z, "" AS a, " " AS b, TRIM(" ") as c, " d" AS d, " e" AS e; END;'))) { + // requires MySQL 5+ + $stmt = $db->prepare('CALL p()'); + $stmt->execute(); + $expected = array( + array( + "z" => NULL, + "a" => NULL, + "b" => " ", + "c" => NULL, + "d" => " d", + "e" => " e", + ), + ); + do { + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if ($tmp != $expected) { + printf("[004] Expecting %s got %s\n", + var_export($expected, true), var_export($tmp, true)); + } + } while ($stmt->nextRowset()); + + $stmt->execute(); + do { + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if ($tmp != $expected) { + printf("[005] Expecting %s got %s\n", + var_export($expected, true), var_export($tmp, true)); + } + } while ($stmt->nextRowset()); + + } + + if ($have_procedures) + @$db->exec('DROP PROCEDURE IF EXISTS p'); + + print "done!"; +--EXPECTF-- +[002] Maybe PDO could indicate that this is not a proper way of setting ATTR_ORACLE_NULLS... +[003] Maybe PDO could indicate that this is not a proper way of setting ATTR_ORACLE_NULLS... +array(1) { + [0]=> + array(6) { + ["z"]=> + NULL + ["a"]=> + NULL + ["b"]=> + string(1) " " + ["c"]=> + NULL + ["d"]=> + string(2) " d" + ["e"]=> + string(3) "%se" + } +} +array(1) { + [0]=> + array(6) { + ["z"]=> + NULL + ["a"]=> + string(0) "" + ["b"]=> + string(1) " " + ["c"]=> + string(0) "" + ["d"]=> + string(2) " d" + ["e"]=> + string(3) "%se" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt new file mode 100644 index 0000000000..456a796131 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt @@ -0,0 +1,21 @@ +--TEST-- +PDO::ATTR_PREFETCH +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_PREFETCH)); + var_dump($db->setAttribute(PDO::ATTR_PREFETCH, true)); + print "done!"; +--EXPECTF-- +Warning: PDO::getAttribute(): SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute in %s on line %d +bool(false) +bool(false) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt new file mode 100644 index 0000000000..558ab232eb --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt @@ -0,0 +1,48 @@ +--TEST-- +PDO::ATTR_SERVER_INFO +--SKIPIF-- + +--FILE-- +errorCode()) || ('00000' == $db->errorCode())); + + $info = $db->getAttribute(PDO::ATTR_SERVER_INFO); + if ('' == $info) + printf("[001] Server info must not be empty\n"); + + // Read-only? + if (false !== $db->setAttribute(PDO::ATTR_SERVER_INFO, 'new uptime: 0s')) + printf("[002] Wonderful, I can change the client version!\n"); + + $new_info = $db->getAttribute(PDO::ATTR_SERVER_INFO); + if ($new_info !== $info) + printf("[003] Did we change it from '%s' to '%s'?\n", $info, $info); + + // lets hope we always run this in the same second as we did run the server info request... + if (!$stmt = $db->query('SHOW STATUS LIKE "%uptime%"')) + printf("[004] Cannot run SHOW STATUS, [%s]\n", $db->errorCode()); + else { + if (!$row = $stmt->fetch(PDO::FETCH_NUM)) + printf("[005] Unable to fetch uptime, [%s]\n", $db->errorCode()); + else + $uptime = $row[1]; + $stmt->closeCursor(); + } + + if (!preg_match('/Uptime/i', $info)) + printf("[006] Can't find uptime in server info '%s'\n", $info); + + if (isset($uptime) && !preg_match(sprintf('/Uptime: %d/i', $uptime), $info)) + printf("[007] SHOW STATUS and server info have reported a different uptime, please check. Server info: '%s', SHOW STATUS: '%s'\n", $info, $uptime); + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt new file mode 100644 index 0000000000..07bb8c39f5 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt @@ -0,0 +1,64 @@ +--TEST-- +PDO::ATTR_SERVER_VERSION +--SKIPIF-- + +--FILE-- +errorCode()) || ('00000' == $db->errorCode())); + + $version = $db->getAttribute(PDO::ATTR_SERVER_VERSION); + if ('' == $version) + printf("[001] Server version must not be empty\n"); + + // Ideally the server version would be an integer - as documented but BC break! + // If its a version string it should be of the format \d+\.\d+\.\d+.* + + if (is_string($version)) { + // Its not an int like documented but a string - maybe for BC reasons... + if (!preg_match('/(\d+)\.(\d+)\.(\d+)(.*)/', $version, $matches)) + printf("[002] Client version string seems wrong, got '%s'\n", $version); + else { + // Difficult to define any meaningful constraints + // A possible better check would be calling mysqli_get_server_version() and + // comparing what we get. However, mysqli_get_server_version() needs a mysqli handle + // for which in turn one needs to parse the PDO test environment variables + // for connection parameter... + if ($matches[1] < 3) + printf("[003] Strange major version: '%s'. Should be more than 3\n", $matches[1]); + if ($matches[2] < 0) + printf("[004] Minor version should be at least 0, got '%s'\n", $matches[2]); + if ($matches[3] < 0) + printf("[005] Sub version should be at least 0, got '%s'\n", $matches[2]); + } + } else if (is_int($version)) { + // Lets accept also int if it follows the rules from the original MYSQL C API + $major = floor($version / 10000); + $minor = floor(($version - ($main * 10000)) / 100); + $sub = $version - ($main * 10000) - ($minor * 100); + if ($major < 3) + printf("[006] Strange major version: '%s'. Should be more than 3\n", $major); + if ($minor < 0) + printf("[007] Minor version should be at least 0, got '%s'\n", $minor); + if ($sub < 0) + printf("[008] Sub version should be at least 0, got '%s'\n", $sub); + } + + // Read-only? + if (false !== $db->setAttribute(PDO::ATTR_CLIENT_VERSION, '1.0')) + printf("[009] Wonderful, I can change the client version!\n"); + + $new_version = $db->getAttribute(PDO::ATTR_SERVER_VERSION); + if ($new_version !== $version) + printf("[010] Did we change it from '%s' to '%s'?\n", $version, $new_version); + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt new file mode 100644 index 0000000000..d077f6ebfe --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt @@ -0,0 +1,154 @@ +--TEST-- +PDO::ATTR_STATEMENT_CLASS +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_STATEMENT_CLASS); + var_dump($default); + + if (false !== ($tmp = @$db->setAttribute(PDO::ATTR_STATEMENT_CLASS))) + printf("[001] Expecting boolean/false got %s\n", var_export($tmp, true)); + + if (false !== ($tmp = @$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'foo'))) + printf("[002] Expecting boolean/false got %s\n", var_export($tmp, true)); + + if (false !== ($tmp = @$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('classname')))) + printf("[003] Expecting boolean/false got %s\n", var_export($tmp, true)); + + // unknown class + if (false !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('classname', array())))) + printf("[004] Expecting boolean/false got %s\n", var_export($tmp, true)); + + // class not derived from PDOStatement + class myclass { + function __construct() { + printf("myclass\n"); + } + } + if (false !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myclass', array())))) + printf("[005] Expecting boolean/false got %s\n", var_export($tmp, true)); + + // public constructor not allowed + class mystatement extends PDOStatement { + public function __construct() { + printf("mystatement\n"); + } + } + + if (false !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement', array())))) + printf("[006] Expecting boolean/false got %s\n", var_export($tmp, true)); + + // ... but a public destructor is allowed + class mystatement2 extends PDOStatement { + public function __destruct() { + printf("mystatement\n"); + } + } + + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement2', array())))) + printf("[007] Expecting boolean/true got %s\n", var_export($tmp, true)); + + // private constructor + class mystatement3 extends PDOStatement { + private function __construct($msg) { + printf("mystatement3\n"); + var_dump($msg); + } + } + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement3', array('param1'))))) + printf("[008] Expecting boolean/true got %s\n", var_export($tmp, true)); + + // private constructor + class mystatement4 extends PDOStatement { + private function __construct($msg) { + printf("%s\n", get_class($this)); + var_dump($msg); + } + } + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement4', array('param1'))))) + printf("[008] Expecting boolean/true got %s\n", var_export($tmp, true)); + + var_dump($db->getAttribute(PDO::ATTR_STATEMENT_CLASS)); + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 2'); + + class mystatement5 extends mystatement4 { + public function fetchAll($fetch_style = 1, $column_index = 1, $ctor_args = array()) { + return "no data :)"; + } + } + + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement5', array('mystatement5'))))) + printf("[009] Expecting boolean/true got %s\n", var_export($tmp, true)); + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 2'); + var_dump($stmt->fetchAll()); + + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement')))) + printf("[010] Expecting boolean/true got %s\n", var_export($tmp, true)); + + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 1'); + var_dump($stmt->fetchAll()); + + // Yes, this is a fatal error and I want it to fail. + abstract class mystatement6 extends mystatement5 { + } + if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement6', array('mystatement6'))))) + printf("[011] Expecting boolean/true got %s\n", var_export($tmp, true)); + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 1'); + + print "done!"; +--EXPECTF-- +array(1) { + [0]=> + string(12) "PDOStatement" +} + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); the classname must be a string specifying an existing class in %s on line %d + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: user-supplied statement class must be derived from PDOStatement in %s on line %d + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: user-supplied statement class cannot have a public constructor in %s on line %d + +Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d +array(2) { + [0]=> + string(12) "mystatement4" + [1]=> + array(1) { + [0]=> + string(6) "param1" + } +} +mystatement4 +string(6) "param1" +mystatement5 +string(12) "mystatement5" +string(10) "no data :)" +array(1) { + [0]=> + array(4) { + ["id"]=> + string(1) "1" + [0]=> + string(1) "1" + ["label"]=> + string(1) "a" + [1]=> + string(1) "a" + } +} + +Fatal error: Cannot instantiate abstract class mystatement6 in %s on line %d \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt b/ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt new file mode 100644 index 0000000000..209bf3d89d --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt @@ -0,0 +1,201 @@ +--TEST-- +PDO->beginTransaction() +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[001] Autocommit should be on by default\n"); + + if (false == $db->beginTransaction()) + printf("[002] Cannot start a transaction, [%s] [%s]\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[003] Autocommit should be on by default, beginTransaction() shall not impact it\n"); + + if (0 == $db->exec('DELETE FROM test')) + printf("[004] No rows deleted, can't be true.\n"); + + /* This is the PDO way to close a connection */ + $db = null; + $db = MySQLPDOTest::factory(); + + /* Autocommit was off - by definition. Commit was not issued. DELETE should have been rolled back. */ + if (!($stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC'))) + printf("[005] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row); + + if (!$db->beginTransaction()) + printf("[006] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->exec(sprintf('DELETE FROM test WHERE id = %d', $row['id']))) + printf("[007] DELETE should have indicated 1 deleted row, [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (!$db->commit()) + printf("[008] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[009] Autocommit should be on after commit()\n"); + + if (!($stmt = $db->query(sprintf('SELECT id, label FROM test WHERE id = %d', $row['id'])))) + printf("[010] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + var_dump($stmt->fetch(PDO::FETCH_ASSOC)); + + if (!$db->beginTransaction()) + printf("[011] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $db->exec(sprintf('INSERT INTO test(id, label) VALUES (%d, "z")', $row['id'])); + + if (!($stmt = $db->query(sprintf('SELECT id, label FROM test WHERE id = %d', $row['id'])))) + printf("[012] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $new_row1 = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($new_row1); + + if (!$db->commit()) + printf("[013] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (!($stmt = $db->query(sprintf('SELECT id, label FROM test WHERE id = %d', $row['id'])))) + printf("[014] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $new_row2 = $stmt->fetch(PDO::FETCH_ASSOC); + if ($new_row1 != $new_row2) { + printf("[015] Results must not differ!\n"); + var_dump($new_row1); + var_dump($new_row2); + } + + if (!$db->beginTransaction()) + printf("[016] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->exec(sprintf('DELETE FROM test WHERE id = %d', $row['id']))) + printf("[017] DELETE should have indicated 1 deleted row, [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (!$db->rollback()) + printf("[018] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[019] Autocommit should be on after rollback\n"); + + if (!($stmt = $db->query(sprintf('SELECT id, label FROM test WHERE id = %d', $row['id'])))) + printf("[020] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $new_row2 = $stmt->fetch(PDO::FETCH_ASSOC); + if ($new_row1 != $new_row2) { + printf("[021] Results must not differ!\n"); + var_dump($new_row1); + var_dump($new_row2); + } + + // now, lets check the server variables + if (!($stmt = $db->query('SELECT @@autocommit as auto_commit'))) + printf("[022] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['auto_commit'] != 1) + printf("[023] MySQL Server should indicate autocommit mode, expecting 1, got '%s', [%d] %s\n", + $tmp['auto_commit'], $stmt->errorCode(), $stmt->errorInfo()); + + if (!$db->beginTransaction()) + printf("[024] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (!($stmt = $db->query('SELECT @@autocommit as auto_commit'))) + printf("[025] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['auto_commit'] != 0) + printf("[026] Autocommit mode of the MySQL Server should be off, got '%s', [%d] %s\n", + $tmp['auto_commit'], $stmt->errorCode(), implode(' ', $stmt->errorInfo())); + + $db->commit(); + // Now we should be back to autocommit - we've issues a commit + if ($tmp['auto_commit'] != 1) + printf("[027] MySQL Server should indicate autocommit mode, expecting 1, got '%s', [%d] %s\n", + $tmp['auto_commit'], $stmt->errorCode(), $stmt->errorInfo()); + + // Turn off autocommit using a server variable + $db->exec('SET @@autocommit = 0'); + if (1 === $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[028] I'm confused, how can autocommit be on? Didn't I say I want to manually control transactions?\n"); + + if (!$db->beginTransaction()) + printf("[029] Cannot start a transaction, [%d] %s\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + try { + if (false !== $db->beginTransaction()) { + printf("[030] No false and no exception - that's wrong.\n"); + } + } catch (PDOException $e) { + assert($e->getMessage() != ''); + } + + // TODO: What about an engine that does not support transactions? + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, 'MyISAM'); + + if (false == $db->beginTransaction()) + printf("[031] Cannot start a transaction, [%s] [%s]\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT)) + printf("[032] Autocommit should be on my default, beginTransaction() should not change that\n"); + + if (0 == $db->exec('DELETE FROM test')) + printf("[033] No rows deleted, can't be true.\n"); + + if (!$db->commit()) + printf("[034] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + if (false == $db->beginTransaction()) + printf("[035] Cannot start a transaction, [%s] [%s]\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + if (0 == $db->exec('INSERT INTO test(id, label) VALUES (1, "a")')) + printf("[036] Cannot insert data, [%s] [%s]\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + // Should cause a Server warning but no error + if (!$db->rollback()) + printf("[037] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + var_dump($db->errorCode()); + + if (1 != $db->exec('DELETE FROM test')) + printf("[038] No rows deleted, can't be true.\n"); + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" +} +bool(false) +array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "z" +} +[026] Autocommit mode of the MySQL Server should be off, got '1', [0] 00000 +[028] I'm confused, how can autocommit be on? Didn't I say I want to manually control transactions? +string(5) "00000" +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_bit.phpt b/ext/pdo_mysql/tests/pdo_mysql_bit.phpt new file mode 100644 index 0000000000..18506acef9 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_bit.phpt @@ -0,0 +1,59 @@ +--TEST-- +MySQL PDO->exec(), BIT columns - remove after fix! +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine()); + @$db->exec($sql); + if ($db->errorCode() != 0) { + // not all MySQL Server versions and/or engines might support the type + return true; + } + + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)'); + $stmt->bindValue(1, $offset); + $stmt->bindValue(2, $value); + if (!$stmt->execute()) { + printf("[%03d + 1] INSERT failed, %s\n", $offset, var_export($stmt->errorInfo(), true)); + return false; + } + $stmt = $db->query('SELECT id, label FROM test'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row); + var_dump($value); + + + return true; + } + + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + test_type($db, 20, 'BIT(8)', 1); + + echo "done!\n"; +?> +--EXPECTF-- +array(2) { + ["id"]=> + string(2) "20" + ["label"]=> + string(1) "1" +} +int(1) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt b/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt new file mode 100644 index 0000000000..cfecaf74c9 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt @@ -0,0 +1,78 @@ +--TEST-- +PDO MySQL specific class constants +--SKIPIF-- + +--FILE-- + true, + 'MYSQL_ATTR_LOCAL_INFILE' => true, + 'MYSQL_ATTR_DIRECT_QUERY' => true, + ); + + if (!MySQLPDOTest::isPDOMySQLnd()) { + $expected['MYSQL_ATTR_MAX_BUFFER_SIZE'] = true; + $expected['MYSQL_ATTR_INIT_COMMAND'] = true; + $expected['MYSQL_ATTR_READ_DEFAULT_FILE'] = true; + $expected['MYSQL_ATTR_READ_DEFAULT_GROUP'] = true; + + } + + /* + TODO + + MYSQLI_OPT_CONNECT_TIMEOUT != PDO::ATTR_TIMEOUT (integer) + Sets the timeout value in seconds for communications with the database. + ^ Potential BUG, PDO::ATTR_TIMEOUT is used in pdo_mysql_handle_factory + + MYSQLI_SET_CHARSET_NAME -> DSN/charset= + ^ Undocumented and pitfall for ext/mysqli users + + Assorted mysqlnd settings missing + */ + $ref = new ReflectionClass('PDO'); + $constants = $ref->getConstants(); + $values = array(); + + foreach ($constants as $name => $value) + if (substr($name, 0, 11) == 'MYSQL_ATTR_') { + if (!isset($values[$value])) + $values[$value] = array($name); + else + $values[$value][] = $name; + + if (isset($expected[$name])) { + unset($expected[$name]); + unset($constants[$name]); + } + + } else { + unset($constants[$name]); + } + + if (!empty($constants)) { + printf("[001] Dumping list of unexpected constants\n"); + var_dump($constants); + } + + if (!empty($expected)) { + printf("[002] Dumping list of missing constants\n"); + var_dump($expected); + } + + if (!empty($values)) { + foreach ($values as $value => $constants) { + if (count($constants) > 1) { + printf("[003] Several constants share the same value '%s'\n", $value); + var_dump($constants); + } + } + } + + print "done!"; +--EXPECT-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_commit.phpt b/ext/pdo_mysql/tests/pdo_mysql_commit.phpt new file mode 100644 index 0000000000..056c0e0231 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_commit.phpt @@ -0,0 +1,84 @@ +--TEST-- +MySQL PDO->commit() +--SKIPIF-- + +--FILE-- +beginTransaction())) { + printf("[001] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + } + + // DDL will issue an implicit commit + $db->exec(sprintf('DROP TABLE IF EXISTS test_commit')); + $db->exec(sprintf('CREATE TABLE test_commit(id INT) ENGINE=%s', MySQLPDOTest::detect_transactional_mysql_engine($db))); + if (true !== ($tmp = $db->commit())) { + printf("[002] No commit allowed? [%s] %s\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + // pdo_transaction_transitions should check this as well... + // ... just to be sure the most basic stuff really works we check it again... + if (1 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT))) + printf("[003] According to the manual we should be back to autocommit mode, got %s/%s\n", + gettype($tmp), var_export($tmp, true)); + + if (true !== ($tmp = $db->beginTransaction())) + printf("[004] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + + $db->exec('INSERT INTO test(id, label) VALUES (100, "z")'); + + if (true !== ($tmp = $db->commit())) + printf("[005] No commit allowed? [%s] %s\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + // a weak test without unicode etc. - lets leave that to dedicated tests + $stmt = $db->query('SELECT id, label FROM test WHERE id = 100'); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (!isset($rows[0]['label']) || ($rows[0]['label'] != 'z')) { + printf("[006] Record data is strange, dumping rows\n"); + var_dump($rows); + } + + // Ok, lets check MyISAM resp. any other non-transactional engine + // pdo_mysql_begin_transaction has more on this, quick check only + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, 'MyISAM'); + + if (true !== ($tmp = $db->beginTransaction())) + printf("[007] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + + $db->exec('INSERT INTO test(id, label) VALUES (100, "z")'); + if (true !== ($tmp = $db->commit())) + printf("[008] No commit allowed? [%s] %s\n", + $db->errorCode(), implode(' ', $db->errorInfo())); + + // a weak test without unicode etc. - lets leave that to dedicated tests + $stmt = $db->query('SELECT id, label FROM test WHERE id = 100'); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (!isset($rows[0]['label']) || ($rows[0]['label'] != 'z')) { + printf("[009] Record data is strange, dumping rows\n"); + var_dump($rows); + } + + } catch (PDOException $e) { + printf("[002] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec(sprintf('DROP TABLE IF EXISTS test_commit')); + print "done!"; +--EXPECT-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt b/ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt new file mode 100644 index 0000000000..35056a74d8 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt @@ -0,0 +1,81 @@ +--TEST-- +MySQL PDO->errorCode() +--SKIPIF-- + +--FILE-- +errorCode(); + if (($code != $expected) && (($expected != '00000') && ($code != ''))) { + printf("[%03d] Expecting error code '%s' got code '%s'\n", + $offset, $expected, $code); + } + + } + + try { + + /* + If you create a PDOStatement object through PDO->prepare() + or PDO->query() and invoke an error on the statement handle, + PDO->errorCode() will not reflect that error. You must call + PDOStatement->errorCode() to return the error code for an + operation performed on a particular statement handle. + */ + $code = $db->errorCode(); + check_error(2, $db); + + $stmt = $db->query('SELECT id, label FROM test'); + $stmt2 = &$stmt; + check_error(3, $db); + check_error(4, $stmt); + + $db->exec('DROP TABLE IF EXISTS test'); + @$stmt->execute(); + check_error(4, $db); + check_error(5, $stmt, '42S02'); + check_error(6, $stmt2, '42S02'); + + $db->exec('DROP TABLE IF EXISTS unknown'); + @$stmt = $db->query('SELECT id, label FROM unknown'); + check_error(7, $db, '42S02'); + + MySQLPDOTest::createTestTable($db); + $stmt = $db->query('SELECT id, label FROM test'); + check_error(8, $db); + check_error(9, $stmt); + + $db2 = &$db; + @$db->query('SELECT id, label FROM unknown'); + check_error(10, $db, '42S02'); + check_error(11, $db2, '42S02'); + check_error(12, $stmt); + check_error(13, $stmt2); + + // lets hope this is an invalid attribute code + $invalid_attr = -1 * PHP_INT_MAX + 3; + $tmp = @$db->getAttribute($invalid_attr); + check_error(14, $db, 'IM001'); + check_error(15, $db2, 'IM001'); + check_error(16, $stmt); + check_error(17, $stmt2); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt b/ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt new file mode 100644 index 0000000000..8829dc448a --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt @@ -0,0 +1,173 @@ +--TEST-- +MySQL PDO->errorInfo() +--SKIPIF-- + +--FILE-- +errorInfo(); + if (count($info) != 3) + printf("[%03d] Info should have three fields, got %s\n", + $offset, var_export($info, true)); + + $code = $info[0]; + if (($code != $expected) && (($expected != '00000') && ($code != ''))) { + printf("[%03d] Expecting error code '%s' got code '%s'\n", + $offset, $expected, $code); + } + + if ($expected != '00000') { + if (!isset($info[1]) || $info[1] == '') + printf("[%03d] Driver-specific error code not set\n", $offset); + if (!isset($info[2]) || $info[2] == '') + printf("[%03d] Driver-specific error message.not set\n", $offset); + } + + } + + function pdo_mysql_errorinfo($db, $offset) { + + try { + + /* + If you create a PDOStatement object through PDO->prepare() + or PDO->query() and invoke an error on the statement handle, + PDO->errorCode() will not reflect that error. You must call + PDOStatement->errorCode() to return the error code for an + operation performed on a particular statement handle. + */ + $code = $db->errorCode(); + check_error($offset + 2, $db); + + $stmt = $db->query('SELECT id, label FROM test'); + $stmt2 = &$stmt; + check_error($offset + 3, $db); + check_error($offset + 4, $stmt); + + $db->exec('DROP TABLE IF EXISTS test'); + @$stmt->execute(); + check_error($offset + 5, $db); + check_error($offset + 6, $stmt, '42S02'); + check_error($offset + 7, $stmt2, '42S02'); + + @$stmt = $db->query('SELECT id, label FROM unknown'); + check_error($offset + 8, $db, '42S02'); + + MySQLPDOTest::createTestTable($db); + $stmt = $db->query('SELECT id, label FROM test'); + check_error($offset + 9, $db); + check_error($offset + 10, $stmt); + + $db2 = &$db; + $db->exec('DROP TABLE IF EXISTS unknown'); + @$db->query('SELECT id, label FROM unknown'); + check_error($offset + 11, $db, '42S02'); + check_error($offset + 12, $db2, '42S02'); + check_error($offset + 13, $stmt); + check_error($offset + 14, $stmt2); + + // lets hope this is an invalid attribute code + $invalid_attr = -1 * PHP_INT_MAX + 3; + $tmp = @$db->getAttribute($invalid_attr); + check_error($offset + 15, $db, 'IM001'); + check_error($offset + 16, $db2, 'IM001'); + check_error($offset + 17, $stmt); + check_error($offset + 18, $stmt2); + + } catch (PDOException $e) { + printf("[%03d] %s [%s] %s\n", + $offset + 19, $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + } + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + printf("Emulated Prepared Statements...\n"); + pdo_mysql_errorinfo($db, 0); + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + printf("Native Prepared Statements...\n"); + pdo_mysql_errorinfo($db, 20); + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +--EXPECTF-- +Emulated Prepared Statements... +[002] Info should have three fields, got array ( + 0 => '00000', +) +[003] Info should have three fields, got array ( + 0 => '00000', +) +[004] Info should have three fields, got array ( + 0 => '00000', +) +[005] Info should have three fields, got array ( + 0 => '00000', +) +[009] Info should have three fields, got array ( + 0 => '00000', +) +[010] Info should have three fields, got array ( + 0 => '00000', +) +[013] Info should have three fields, got array ( + 0 => '00000', +) +[014] Info should have three fields, got array ( + 0 => '00000', +) +[015] Info should have three fields, got array ( + 0 => 'IM001', +) +[015] Driver-specific error code not set +[015] Driver-specific error message.not set +[016] Info should have three fields, got array ( + 0 => 'IM001', +) +[016] Driver-specific error code not set +[016] Driver-specific error message.not set +[017] Info should have three fields, got array ( + 0 => '00000', +) +[018] Info should have three fields, got array ( + 0 => '00000', +) +Native Prepared Statements... +[022] Info should have three fields, got array ( + 0 => '00000', +) +[023] Info should have three fields, got array ( + 0 => '00000', +) +[024] Info should have three fields, got array ( + 0 => '00000', +) +[025] Info should have three fields, got array ( + 0 => '00000', +) +[030] Info should have three fields, got array ( + 0 => '00000', +) +[033] Info should have three fields, got array ( + 0 => '00000', +) +[034] Info should have three fields, got array ( + 0 => '00000', +) +[037] Info should have three fields, got array ( + 0 => '00000', +) +[038] Info should have three fields, got array ( + 0 => '00000', +) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_exec.phpt b/ext/pdo_mysql/tests/pdo_mysql_exec.phpt new file mode 100644 index 0000000000..5ca5f44792 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_exec.phpt @@ -0,0 +1,179 @@ +--TEST-- +MySQL PDO->exec(), affected rows +--SKIPIF-- + +--FILE-- +exec($sql); + if (!is_null($exp) && ($ret !== $exp)) { + printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n", + $offset, $exp, gettype($exp), $ret, gettype($ret), $sql, + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + } catch (PDOException $e) { + printf("[%03d] '%s' has failed, [%s] %s\n", + $offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return true; + } + + require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db)); + + /* affected rows related */ + try { + + exec_and_count(2, $db, 'DROP TABLE IF EXISTS test', 0); + exec_and_count(3, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0); + exec_and_count(4, $db, 'INSERT INTO test(id, col1) VALUES (1, "a")', 1); + exec_and_count(5, $db, 'INSERT INTO test(id, col1) VALUES (2, "b"), (3, "c")', 2); + exec_and_count(6, $db, 'UPDATE test SET id = 4 WHERE id = 3', 1); + exec_and_count(7, $db, 'INSERT INTO test(id, col1) VALUES (1, "d") ON DUPLICATE KEY UPDATE id = 3', 2); + exec_and_count(8, $db, 'UPDATE test SET id = 5 WHERE id = 5', 0); + exec_and_count(9, $db, 'INSERT INTO test(id, col1) VALUES (5, "e") ON DUPLICATE KEY UPDATE id = 6', 1); + exec_and_count(10, $db, 'REPLACE INTO test(id, col1) VALUES (5, "f")', 2); + exec_and_count(11, $db, 'REPLACE INTO test(id, col1) VALUES (6, "g")', 1); + exec_and_count(12, $db, 'DELETE FROM test WHERE id > 2', 4); + exec_and_count(13, $db, 'DROP TABLE test', 0); + exec_and_count(14, $db, 'SET @myvar = 1', 0); + + exec_and_count(15, $db, 'THIS IS NOT VALID SQL, I HOPE', false); + printf("[016] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo())); + + exec_and_count(36, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0); + exec_and_count(37, $db, 'INSERT INTO test(id, col1) VALUES (1, "a")', 1); + // Results may vary. Typically you will get 1. But the MySQL 5.1 manual states: Truncation operations do not return the number of deleted rows. + // Don't rely on any return value! + exec_and_count(38, $db, 'TRUNCATE TABLE test', NULL); + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + + /* CREATE, DROP, CALL SP and SF */ + if (MySQLPDOTest::getServerVersion($db) > 50000) { + // let's try to play with stored procedures + try { + $ignore_exception = true; + exec_and_count(18, $db, 'DROP PROCEDURE IF EXISTS p', 0); + exec_and_count(19, $db, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(255)) BEGIN SELECT VERSION() INTO ver_param; END;', 0); + // we got this far without problems. If there's an issue from now on, its a failure + $ignore_exception = false; + exec_and_count(20, $db, 'CALL p(@version)', 0); + $stmt = $db->query('SELECT @version AS p_version'); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (count($tmp) > 1 || !isset($tmp[0]['p_version'])) { + printf("[022] Data seems wrong, dumping\n"); + var_dump($tmp); + } else { + $p_version = $tmp[0]['p_version']; + } + + $stmt = $db->query('SELECT VERSION() AS _version'); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (count($tmp) > 1 || !isset($tmp[0]['_version'])) { + printf("[023] Data seems wrong, dumping\n"); + var_dump($tmp); + } else { + if ($p_version !== $tmp[0]['_version']) { + printf("[024] Found different version strings, SP returned '%s'/%s, SELECT returned '%s'/%s\n", + $p_version, gettype($p_version), + $tmp[0]['_version'], gettype($tmp[0]['_version'])); + } + } + exec_and_count(25, $db, 'DROP PROCEDURE IF EXISTS p', 0); + + } catch (PDOException $e) { + // ignore it, we might not have sufficient permissions + if (!$ignore_exception) + printf("[021] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + // stored function + try { + $ignore_exception = true; + exec_and_count(27, $db, 'DROP FUNCTION IF EXISTS f', 0); + exec_and_count(28, $db, 'CREATE FUNCTION f( ver_param VARCHAR(255)) RETURNS VARCHAR(255) DETERMINISTIC RETURN ver_param;', 0); + // we got this far without problems. If there's an issue from now on, its a failure + $ignore_exception = false; + $stmt = $db->query('SELECT f(VERSION()) AS f_version'); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (count($tmp) > 1 || !isset($tmp[0]['f_version'])) { + printf("[029] Data seems wrong, dumping\n"); + var_dump($tmp); + } else { + $f_version = $tmp[0]['f_version']; + } + $stmt = $db->query('SELECT VERSION() AS _version'); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (count($tmp) > 1 || !isset($tmp[0]['_version'])) { + printf("[030] Data seems wrong, dumping\n"); + var_dump($tmp); + } else { + if ($f_version !== $tmp[0]['_version']) { + printf("[031] Found different version strings, SF returned '%s'/%s, SELECT returned '%s'/%s\n", + $f_version, gettype($f_version), + $tmp[0]['_version'], gettype($tmp[0]['_version'])); + } + } + exec_and_count(32, $db, 'DROP FUNCTION IF EXISTS f', 0); + + } catch (PDOException $e) { + // ignore it, we might not have sufficient permissions + if (!$ignore_exception) + printf("[026] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + } + + // multi query + try { + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + $exp = 0; + + $tmp = @$db->exec(sprintf('DROP TABLE IF EXISTS test; CREATE TABLE test(id INT) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + if ($exp !== $tmp) + printf("[034] Expecting %s/%s got %s/%s, [%s] %s\n", + $exp, gettype($exp), + $tmp, gettype($tmp), + $db->errorCode(), var_export($db->errorInfo(), true)); + + // this is interesting: if we get sort of affected rows, what will happen now? + $tmp = @$db->exec('INSERT INTO test(id) VALUES (1); INSERT INTO test(id) VALUES (2)'); + printf("[035] With emulated PS it works but makes no sense given that exec() returns sort of affected rows...\n"); + + + } catch (PDOException $e) { + printf("[033] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + + @$db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +--EXPECTF-- +Warning: PDO::exec(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'THIS IS NOT VALID SQL, I HOPE' at line 1 in %s on line %d +[016] [42000] 42000 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'THIS IS NOT VALID SQL, I HOPE' at line %d +[035] With emulated PS it works but makes no sense given that exec() returns sort of affected rows... +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt b/ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt new file mode 100644 index 0000000000..00a2de1692 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt @@ -0,0 +1,88 @@ +--TEST-- +MySQL PDO->exec(), affected rows +--SKIPIF-- + +--FILE-- +exec($sql); + else + $ret = $db->exec($sql); + + if ($ret !== $exp) { + printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n", + $offset, $exp, gettype($exp), $ret, gettype($ret), $sql, + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + } catch (PDOException $e) { + printf("[%03d] '%s' has failed, [%s] %s\n", + $offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return true; + } + + require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db)); + + /* affected rows related */ + try { + + @$db->exec('DROP DATABASE IF EXISTS pdo_exec_ddl'); + @$db->exec('DROP DATABASE IF EXISTS pdo_exec_ddl2'); + if (1 === @$db->exec('CREATE DATABASE pdo_exec_ddl')) { + // yippie - we can create databases etc. + exec_and_count(3, $db, 'ALTER DATABASE pdo_exec_ddl CHARACTER SET latin1', 1); + } + + exec_and_count(4, $db, 'DROP TABLE IF EXISTS pdo_exec_ddl', 0); + exec_and_count(5, $db, 'DROP TABLE IF EXISTS pdo_exec_ddl2', 0); + if (0 === $db->exec('CREATE TABLE pdo_exec_ddl(id INT, col1 CHAR(2))')) { + exec_and_count(5, $db, 'CREATE INDEX idx1 ON pdo_exec_ddl(id)', 0); + exec_and_count(6, $db, 'DROP INDEX idx1 ON pdo_exec_ddl', 0); + exec_and_count(7, $db, 'ALTER TABLE pdo_exec_ddl DROP id', 0); + exec_and_count(8, $db, 'ALTER TABLE pdo_exec_ddl ADD id INT', 0); + exec_and_count(9, $db, 'ALTER TABLE pdo_exec_ddl ALTER id SET DEFAULT 1', 0); + exec_and_count(10, $db, 'RENAME TABLE pdo_exec_ddl TO pdo_exec_ddl2', 0); + } + + /* + 11.1.2. ALTER LOGFILE GROUP Syntax + 11.1.3. ALTER SERVER Syntax + 11.1.5. ALTER TABLESPACE Syntax + 11.1.8. CREATE LOGFILE GROUP Syntax + 11.1.9. CREATE SERVER Syntax + 11.1.11. CREATE TABLESPACE Syntax + 11.1.14. DROP LOGFILE GROUP Syntax + 11.1.15. DROP SERVER Syntax + 11.1.17. DROP TABLESPACE Syntax + */ + + // clean up + @$db->exec('DROP TABLE IF EXISTS pdo_exec_ddl'); + @$db->exec('DROP TABLE IF EXISTS pdo_exec_ddl2'); + @$db->exec('DROP DATABASE IF EXISTS pdo_exec_ddl'); + @$db->exec('DROP DATABASE IF EXISTS pdo_exec_ddl2'); + + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt b/ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt new file mode 100644 index 0000000000..ecfa502437 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt @@ -0,0 +1,102 @@ +--TEST-- +MySQL PDO->exec(), affected rows +--SKIPIF-- +query('SELECT USER() as _user'); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +$tmp = explode('@', $row['_user']); +if (count($tmp) < 2) + die("skip Cannot detect if test is run against local or remote database server"); +if (($tmp[1] !== 'localhost') && ($tmp[1] !== '127.0.0.1')) + die("skip Test cannot be run against remote database server"); + +?> +--FILE-- +exec($sql); + if ($ret !== $exp) { + printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n", + $offset, $exp, gettype($exp), $ret, gettype($ret), $sql, + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + } catch (PDOException $e) { + + if (42000 == $db->errorCode()) { + // Error: 1148 SQLSTATE: 42000 (ER_NOT_ALLOWED_COMMAND) + // Load data infile not allowed + return false; + } + + printf("[%03d] '%s' has failed, [%s] %s\n", + $offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return true; + } + + require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db)); + + /* affected rows related */ + try { + + exec_and_count(2, $db, 'DROP TABLE IF EXISTS test', 0); + exec_and_count(3, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0); + + $stmt = $db->query('SHOW VARIABLES LIKE "secure_file_priv"'); + if (($row = $stmt->fetch(PDO::FETCH_ASSOC)) && ($row['value'] != '')) { + $filename = $row['value'] . DIRECTORY_SEPARATOR . "pdo_mysql_exec_load_data.csv"; + } else { + $filename = MySQLPDOTest::getTempDir() . DIRECTORY_SEPARATOR . "pdo_mysql_exec_load_data.csv"; + } + + $fp = fopen($filename, "w"); + fwrite($fp, b"1;foo\n"); + fwrite($fp, b"2;bar"); + fclose($fp); + + $sql = sprintf("LOAD DATA INFILE %s INTO TABLE test FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'", $db->quote($filename)); + + if (exec_and_count(4, $db, $sql, 2)) { + + $stmt = $db->query('SELECT id, col1 FROM test ORDER BY id ASC'); + $expected = array(array("id" => 1, "col1" => "foo"), array("id" => 2, "col1" => "bar")); + $ret = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($expected as $offset => $exp) { + foreach ($exp as $key => $value) { + if ($ret[$offset][$key] != $value) { + printf("Results seem wrong, check manually\n"); + var_dump($ret); + var_dump($expected); + break 2; + } + } + } + } + + unlink($filename); + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt b/ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt new file mode 100644 index 0000000000..a759f72cd4 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt @@ -0,0 +1,59 @@ +--TEST-- +MySQL PDO->exec(), SELECT +--SKIPIF-- + +--FILE-- +exec($sql); + if ($ret !== $exp) { + printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n", + $offset, $exp, gettype($exp), $ret, gettype($ret), $sql, + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + } catch (PDOException $e) { + printf("[%03d] '%s' has failed, [%s] %s\n", + $offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return true; + } + + require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db)); + + /* affected rows related */ + try { + + exec_and_count(2, $db, 'DROP TABLE IF EXISTS test', 0); + exec_and_count(3, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0); + exec_and_count(4, $db, 'INSERT INTO test(id, col1) VALUES (1, "a")', 1); + // question is: will the result set be cleaned up, will it be possible to run more queries on the line? + // buffered or unbuffered does not matter! + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + exec_and_count(5, $db, 'SELECT id FROM test', 0); + exec_and_count(6, $db, 'INSERT INTO test(id, col1) VALUES (2, "b")', 1); + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + } + + @$db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +Warning: PDO::exec(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +[006] Expecting '1'/integer got ''/boolean when running 'INSERT INTO test(id, col1) VALUES (2, "b")', [HY000] HY000 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt b/ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt new file mode 100644 index 0000000000..eac6bf331f --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt @@ -0,0 +1,88 @@ +--TEST-- +MySQL PDOStatement->fetch() +--SKIPIF-- + +--FILE-- +query('SELECT 1'); + $num = $stmt->fetch(PDO::FETCH_NUM); + + $stmt = $db->query('SELECT 1'); + $assoc = $stmt->fetch(PDO::FETCH_ASSOC); + + $stmt = $db->query('SELECT 1'); + $both = $stmt->fetch(PDO::FETCH_BOTH); + + $computed_both = array_merge($num, $assoc); + if ($computed_both != $both) { + printf("[%03d] Suspicious FETCH_BOTH result, dumping\n", $offset); + var_dump($computed_both); + var_dump($both); + } + + if (!is_null($expect) && ($expect != $both)) { + printf("[%03d] Expected differes from returned data, dumping\n", $offset); + var_dump($expect); + var_dump($both); + } + + } catch (PDOException $e) { + + printf("[%03d] %s, [%s] %s\n", + $offset, + $e->getMessage(), $db->errroCode(), implode(' ', $db->errorInfo())); + + } + + } + + try { + + fetch(2, &$db, 'SELECT 1', array(0 => '1', '1' => '1')); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +[002] Suspicious FETCH_BOTH result, dumping +array(2) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" +} +array(2) { + [1]=> + string(1) "1" + [2]=> + string(1) "1" +} +[002] Expected differes from returned data, dumping +array(2) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" +} +array(2) { + [1]=> + string(1) "1" + [2]=> + string(1) "1" +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt b/ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt new file mode 100644 index 0000000000..74f17d5984 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt @@ -0,0 +1,101 @@ +--TEST-- +MySQL PDO->getAttribute() +--SKIPIF-- + +--FILE-- +setAttribute($attribute, $value)) { + printf("[%03d] Cannot set attribute '%s' to value '%s'\n", + $offset, $attribute, var_export($tmp, true)); + return false; + } + + if (gettype($value) != $value_type) { + printf("[%03d] Call to PDO::setAttribute(int attribute, mixed value) has changed the type of value from %s to %s, test will not work properly\n", + $offset, $value_type, gettype($value)); + return false; + } + + $tmp = $db->getAttribute($attribute); + if ($tmp !== $value) { + printf("[%03d] Attribute '%s' was set to '%s'/%s but getAttribute() reports '%s'/%s\n", + $offset, $attribute, var_export($value, true), gettype($value), var_export($tmp, true), gettype($tmp)); + return false; + } + + } catch (PDOException $e) { + printf("[%03d] %s, [%s] %s\n", + $offset, $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return true; + } + + set_and_get(1, $db, PDO::ATTR_AUTOCOMMIT, 1); +/* + set_and_get(2, $db, PDO::ATTR_AUTOCOMMIT, 0); + set_and_get(3, $db, PDO::ATTR_AUTOCOMMIT, -1); + $obj = new stdClass(); + set_and_get(4, $db, PDO::ATTR_AUTOCOMMIT, $obj); + + set_and_get(5, $db, PDO::MYSQL_ATTR_LOCAL_INFILE, 1); + set_and_get(6, $db, PDO::MYSQL_ATTR_LOCAL_INFILE, 0); + set_and_get(7, $db, PDO::MYSQL_ATTR_LOCAL_INFILE, -1); + $tmp = array(); + set_and_get(8, $db, PDO::MYSQL_ATTR_LOCAL_INFILE, $tmp); + + set_and_get(9, $db, PPDO::MYSQL_ATTR_INIT_COMMAND, ''); + set_and_get(10, $db, PPDO::MYSQL_ATTR_INIT_COMMAND, 'SOME SQL'); + set_and_get(11, $db, PPDO::MYSQL_ATTR_INIT_COMMAND, -1); + +*/ +/* +PDO::MYSQL_ATTR_READ_DEFAULT_FILE (integer) + + Read options from the named option file instead of from my.cnf. +PDO::MYSQL_ATTR_READ_DEFAULT_GROUP (integer) + + Read options from the named group from my.cnf or the file specified with MYSQL_READ_DEFAULT_FILE. +PDO::MYSQL_ATTR_MAX_BUFFER_SIZE (integer) + + Maximum buffer size. Defaults to 1 MiB. +PDO::MYSQL_ATTR_DIRECT_QUERY (integer) + + Perform direct queries, don't use prepared statements. +*/ +/* +TODO - read only +PDO::ATTR_CONNECTION_STATUS +PDO::ATTR_SERVER_INFO +*/ + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +[001] Call to PDO::setAttribute(int attribute, mixed value) has changed the type of value from integer to boolean, test will not work properly +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt new file mode 100644 index 0000000000..00c282bcc7 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt @@ -0,0 +1,58 @@ +--TEST-- +MySQL PDO class interface +--SKIPIF-- + +--FILE-- + true, + 'prepare' => true, + 'beginTransaction' => true, + 'commit' => true, + 'rollBack' => true, + 'setAttribute' => true, + 'exec' => true, + 'query' => true, + 'lastInsertId' => true, + 'errorCode' => true, + 'errorInfo' => true, + 'getAttribute' => true, + 'quote' => true, + '__wakeup' => true, + '__sleep' => true, + 'getAvailableDrivers' => true, + ); + $classname = get_class($db); + + $methods = get_class_methods($classname); + foreach ($methods as $k => $method) { + if (isset($expected[$method])) { + unset($expected[$method]); + unset($methods[$k]); + } + if ($method == $classname) { + unset($expected['__construct']); + unset($methods[$k]); + } + } + if (!empty($expected)) { + printf("Dumping missing class methods\n"); + var_dump($expected); + } + if (!empty($methods)) { + printf("Found more methods than expected, dumping list\n"); + var_dump($methods); + } + + print "done!"; +--EXPECT-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt b/ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt new file mode 100644 index 0000000000..552128cae1 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt @@ -0,0 +1,114 @@ +--TEST-- +MySQL PDO->lastInsertId() +--SKIPIF-- + +--FILE-- +lastInsertId())) + printf("[001] No query has been run, lastInsertId() should return '0'/string got '%s'/%s\n", + var_export($tmp, true), gettype($tmp)); + + if ('0' !== ($tmp = $db->lastInsertId('sequence_name'))) + printf("[002] MySQL does not support sequences, expecting '0'/string got '%s'/%s\n", + var_export($tmp, true), gettype($tmp)); + + $db->exec('DROP TABLE IF EXISTS test'); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[003] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + $db->exec(sprintf('CREATE TABLE test(id INT, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[004] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + $stmt = $db->query('SELECT id FROM test LIMIT 1'); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[005] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + // no auto increment column + $db->exec('INSERT INTO test(id, col1) VALUES (100, "a")'); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[006] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + $db->exec('ALTER TABLE test MODIFY id INT AUTO_INCREMENT PRIMARY KEY'); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[006] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + // duplicate key + @$db->exec('INSERT INTO test(id, col1) VALUES (100, "a")'); + if ('0' !== ($tmp = $db->lastInsertId())) + printf("[007] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + $db->exec('INSERT INTO test(id, col1) VALUES (101, "b")'); + if ('101' !== ($tmp = $db->lastInsertId())) + printf("[008] Expecting '0'/string got '%s'/%s", var_export($tmp, true), gettype($tmp)); + + $db->exec('ALTER TABLE test MODIFY col1 CHAR(10) UNIQUE'); + // replace = delete + insert -> new auto increment value + $db->exec('REPLACE INTO test(col1) VALUES ("b")'); + $next_id = (int)$db->lastInsertId(); + + if ($next_id <= 101) + printf("[009] Expecting at least 102, got %d\n",$next_id); + + $stmt = $db->query('SELECT LAST_INSERT_ID() as _last_id'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $last_id = $row['_last_id']; + if ($next_id != $last_id) { + printf("[010] LAST_INSERT_ID() = %d and lastInserId() = %d differ\n", + $last_id, $next_id); + } + + $db->exec('INSERT INTO test(col1) VALUES ("c"), ("d"), ("e")'); + $next_id = (int)$db->lastInsertId(); + if ($next_id <= $last_id) + printf("[011] Expecting at least %d, got %d\n", $last_id + 1, $next_id); + + // warnings are unhandy, lets go for exceptions for a second + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + try { + $ignore_exception = true; + $db->exec('LOCK TABLE test WRITE'); + $ignore_exception = false; + + if (MySQLPDOTest::getServerVersion($db) >= 50000) { + $stmt = $db->query('SELECT @@auto_increment_increment AS inc'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $inc = $row['inc']; + } else { + $inc = 1; + } + + $stmt = $db->query('SELECT LAST_INSERT_ID() as _last_id'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $last_id = $row['_last_id']; + + $db->exec('INSERT INTO test(col1) VALUES ("z")'); + $next_id = (int)$db->lastInsertId(); + if ($next_id < ($last_id + $inc)) + printf("[012] Expecting at least %d, got %d\n", $last_id + $inc, $next_id); + + } catch (PDOException $e) { + if (!$ignore_exception) + printf("[014] %s, [%s} %s\n", $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + @$db->exec('UNLOCK TABLE test'); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +--EXPECTF-- +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt b/ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt new file mode 100644 index 0000000000..e5ab3d5fb5 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt @@ -0,0 +1,101 @@ +--TEST-- +MySQL PDO->__construct(), PDO::ATTR_PERSISTENT +--SKIPIF-- + +--FILE-- + true)); + $db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true)); + $db1->exec('SET @pdo_persistent_connection=1'); + $stmt = $db2->query('SELECT @pdo_persistent_connection as _pers'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + if ($tmp['_pers'] !== '1') + printf("[001] Both handles should use the same connection."); + + $stmt = $db1->query('SELECT CONNECTION_ID() as _con1'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $con1 = $tmp['_con1']; + + $stmt = $db2->query('SELECT CONNECTION_ID() as _con2'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $con2 = $tmp['_con2']; + + if ($con1 !== $con2) + printf("[002] Both handles should report the same MySQL thread ID"); + + $db1 = NULL; /* should be equal to closing to my understanding */ + $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true)); + $stmt = $db1->query('SELECT CONNECTION_ID() as _con1'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $con1 = $tmp['_con1']; + + if ($con1 !== $con2) + printf("[003] Both handles should report the same MySQL thread ID"); + + $affected = $db1->exec(sprintf('KILL %d', $con1)); + // Server needs some think-time sometimes + sleep(1); + if ('00000' == $db1->errorCode()) { + // looks like KILL has worked ? Or not... TODO: why no warning with libmysql?! + @$db1->exec("SET @pdo_persistent_connection=2"); + // but now I want to see some error... + if ('HY000' != $db1->errorCode()) + printf("[004] Wrong error code %s\n", $db1->errorCode()); + + $tmp = implode(' ', $db1->errorInfo()); + if (!strstr($tmp, '2006')) + printf("[005] Wrong error info %s\n", $tmp); + } + + $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => false)); + $stmt = $db1->query('SELECT CONNECTION_ID() as _con1'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $con1 = $tmp['_con1']; + + $db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true)); + $stmt = $db2->query('SELECT CONNECTION_ID() as _con2'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + $con2 = $tmp['_con2']; + + if ($con1 == $con2) + printf("[006] Looks like the persistent and the non persistent connection are using the same link?!\n"); + + // lets go crazy and create a few pconnections... + $connections = array(); + for ($i = 0; $i <= 20; $i++) { + $connections[$i] = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true)); + } + do { + $i = mt_rand(0, 20); + if (isset($connections[$i])) + unset($connections[$i]); + } while (!empty($connections)); + + + } catch (PDOException $e) { + printf("[001] %s, [%s] %s\n", + $e->getMessage(), + (is_object($db)) ? $db->errorCode() : 'n/a', + (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a'); + } + + print "done!"; +--XFAIL-- +Expected to fail in debug mode as PDO doesn't properly clean persistent connections +--EXPECTF-- +Warning: PDO::exec(): MySQL server has gone away in %s on line %d + +Warning: PDO::exec(): Error while reading SET_OPTION's EOF packet. PID=%d in %s on line %d +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt b/ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt new file mode 100644 index 0000000000..8e978a279c --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt @@ -0,0 +1,34 @@ +--TEST-- +MySQL PDO phpinfo() output +--SKIPIF-- + +--FILE-- + 6.0.3-alpha */ + $expected = sprintf('PDO Driver for MySQL, client library version => %s', + $db->getAttribute(PDO::ATTR_CLIENT_VERSION)); + + if (false === stristr($tmp, $expected)) { + // maybe its PDO_MYSQLND + $expected = sprintf('PDO Driver for MySQL, mysql native driver version => %s', + $db->getAttribute(PDO::ATTR_CLIENT_VERSION)); + if (false === stristr($tmp, $expected)) + printf("[001] Cannot find MySQL PDO driver line in phpinfo() output\n"); + } + + print "done!"; +--EXPECT-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt new file mode 100644 index 0000000000..fa942f3858 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt @@ -0,0 +1,414 @@ +--TEST-- +MySQL PDO->prepare(), emulated PS +--SKIPIF-- + +--FILE-- +prepare($query); + else + $stmt = $db->prepare($query); + + if (is_array($error_info) && isset($error_info['prepare'])) { + $tmp = $db->errorInfo(); + + if (isset($error_info['prepare']['sqlstate']) && + ($error_info['prepare']['sqlstate'] !== $tmp[0])) { + printf("[%03d] prepare() - expecting SQLSTATE '%s' got '%s'\n", + $offset, $error_info['prepare']['sqlstate'], $tmp[0]); + return false; + } + + if (isset($error_info['prepare']['mysql']) && + ($error_info['prepare']['mysql'] !== $tmp[1])) { + printf("[%03d] prepare() - expecting MySQL Code '%s' got '%s'\n", + $offset, $error_info['prepare']['mysql'], $tmp[0]); + return false; + } + + return false; + } + + if (is_null($input_params)) + $input_params = array(); + + if (is_array($error_info) && isset($error_info['execute'])) + $ret = @$stmt->execute($input_params); + else + $ret = $stmt->execute($input_params); + + if (!is_bool($ret)) + printf("[%03d] PDO::execute() should return a boolean value, got %s/%s\n", + var_export($ret, true), $ret); + + if (is_array($error_info) && isset($error_info['execute'])) { + $tmp = $stmt->errorInfo(); + + if (isset($error_info['execute']['sqlstate']) && + ($error_info['execute']['sqlstate'] !== $tmp[0])) { + printf("[%03d] execute() - expecting SQLSTATE '%s' got '%s'\n", + $offset, $error_info['execute']['sqlstate'], $tmp[0]); + return false; + } + + if (isset($error_info['execute']['mysql']) && + ($error_info['execute']['mysql'] !== $tmp[1])) { + printf("[%03d] execute() - expecting MySQL Code '%s' got '%s'\n", + $offset, $error_info['execute']['mysql'], $tmp[0]); + return false; + } + + return false; + } + + } catch (PDOException $e) { + printf("[%03d] %s, [%s} %s\n", + $offset, $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return $stmt; + } + + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to switch to emulated prepared statements, test will fail\n"); + + // TODO - that's PDO - you can prepare empty statements! + prepex(3, $db, '', + array(), array('execute' => array('sqlstate' => '42000'))); + + // lets be fair and do the most simple SELECT first + $stmt = prepex(4, $db, 'SELECT 1 as "one"'); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)); + + prepex(5, $db, 'DROP TABLE IF EXISTS test'); + prepex(6, $db, sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + prepex(7, $db, 'INSERT INTO test(id, label) VALUES(1, ":placeholder")'); + $stmt = prepex(8, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + prepex(9, $db, 'DELETE FROM test'); + prepex(10, $db, 'INSERT INTO test(id, label) VALUES(1, ":placeholder")', + array(':placeholder' => 'first row')); + $stmt = prepex(11, $db, 'SELECT label FROM test'); + + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + prepex(12, $db, 'DELETE FROM test'); + prepex(13, $db, 'INSERT INTO test(id, label) VALUES(1, :placeholder)', + array(':placeholder' => 'first row')); + prepex(14, $db, 'INSERT INTO test(id, label) VALUES(2, :placeholder)', + array(':placeholder' => 'second row')); + $stmt = prepex(15, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Is PDO fun? + prepex(16, $db, 'SELECT label FROM test WHERE :placeholder > 1', + array(':placeholder' => 'id')); + prepex(17, $db, 'SELECT :placeholder FROM test WHERE id > 1', + array(':placeholder' => 'id')); + prepex(18, $db, 'SELECT :placeholder FROM test WHERE :placeholder > :placeholder', + array(':placeholder' => 'test')); + + for ($num_params = 2; $num_params < 100; $num_params++) { + $params = array(':placeholder' => 'a'); + for ($i = 1; $i < $num_params; $i++) { + $params[str_repeat('a', $i)] = 'some data'; + } + prepex(19, $db, 'SELECT id, label FROM test WHERE label > :placeholder', + $params, array('execute' => array('sqlstate' => 'HY093'))); + } + + prepex(20, $db, 'DELETE FROM test'); + prepex(21, $db, 'INSERT INTO test(id, label) VALUES (1, :placeholder), (2, :placeholder)', + array(':placeholder' => 'row')); + $stmt = prepex(22, $db, 'SELECT id, label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = prepex(23, $db, 'SELECT id, label FROM test WHERE :placeholder IS NOT NULL', + array(':placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[024] '1' IS NOT NULL evaluates to true, expecting two rows, got %d rows\n", $tmp); + + $stmt = prepex(25, $db, 'SELECT id, label FROM test WHERE :placeholder IS NULL', + array(':placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[026] '1' IS NOT NULL evaluates to true, expecting zero rows, got %d rows\n", $tmp); + + prepex(27, $db, 'DROP TABLE IF EXISTS test'); + prepex(28, $db, 'CREATE TABLE test(id INT, label CHAR(255)) ENGINE=MyISAM'); + if (is_object(prepex(29, $db, 'CREATE FULLTEXT INDEX idx1 ON test(label)'))) { + prepex(30, $db, 'INSERT INTO test(id, label) VALUES (1, :placeholder)', + array(':placeholder' => 'MySQL is the best database in the world!')); + prepex(31, $db, 'INSERT INTO test(id, label) VALUES (1, :placeholder)', + array(':placeholder' => 'If I have the freedom to choose, I would always go again for the MySQL Server')); + $stmt = prepex(32, $db, 'SELECT id, label FROM test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'mysql')); + /* + Lets ignore this + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[033] Expecting two rows, got %d rows\n", $tmp); + */ + } + prepex(34, $db, 'DELETE FROM test'); + prepex(35, $db, 'INSERT INTO test(id, label) VALUES (1, :placeholder), (2, :placeholder)', + array(':placeholder' => 'row')); +/* + $stmt = prepex(36, $db, 'SELECT id, label FROM "test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'row'), + array('execute' => array('sqlstate' => '42000', 'mysql' => 1064))); +*/ + $stmt = prepex(37, $db, 'SELECT id, label FROM \'test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'row'), + array('execute' => array('sqlstate' => '42000', 'mysql' => 1064))); + + $stmt = prepex(38, $db, 'SELECT id, label AS "label" FROM test WHERE label = :placeholder', + array(':placeholder' => 'row')); + + $sql = sprintf("SELECT id, label FROM test WHERE (label LIKE %s) AND (id = :placeholder)", + $db->quote('%ro%')); + $stmt = prepex(39, $db, $sql, array('placeholder' => -1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[040] Expecting zero rows, got %d rows\n", $tmp); + + + $sql = sprintf("SELECT id, label FROM test WHERE (id = :placeholder) OR (label LIKE %s)", + $db->quote('%ro%')); + $stmt = prepex(41, $db, $sql, array('placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[042] Expecting two rows, got %d rows\n", $tmp); + + $sql = "SELECT id, label FROM test WHERE id = :placeholder AND label = (SELECT label AS 'SELECT' FROM test WHERE id = :placeholder)"; + $stmt = prepex(43, $db, $sql, array('placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 1) + printf("[044] Expecting onw row, got %d rows\n", $tmp); + + // and now, the same with anonymous placeholders... + prepex(45, $db, 'DROP TABLE IF EXISTS test'); + prepex(46, $db, sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + prepex(47, $db, 'INSERT INTO test(id, label) VALUES(1, "?")'); + $stmt = prepex(48, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + prepex(49, $db, 'DELETE FROM test'); + prepex(50, $db, 'INSERT INTO test(id, label) VALUES(1, "?")', + array('first row')); + $stmt = prepex(51, $db, 'SELECT label FROM test'); + + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + prepex(52, $db, 'DELETE FROM test'); + prepex(53, $db, 'INSERT INTO test(id, label) VALUES(1, ?)', + array('first row')); + prepex(54, $db, 'INSERT INTO test(id, label) VALUES(2, ?)', + array('second row')); + $stmt = prepex(55, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Is PDO fun? + prepex(56, $db, 'SELECT label FROM test WHERE ? > 1', + array('id')); + prepex(57, $db, 'SELECT ? FROM test WHERE id > 1', + array('id')); + prepex(58, $db, 'SELECT ? FROM test WHERE ? > ?', + array('test'), array('execute' => array('sqlstate' => 'HY093'))); + + prepex(59, $db, 'SELECT ? FROM test WHERE ? > ?', + array('id', 'label', 'value')); + + for ($num_params = 2; $num_params < 100; $num_params++) { + $params = array('a'); + for ($i = 1; $i < $num_params; $i++) { + $params[] = 'some data'; + } + prepex(60, $db, 'SELECT id, label FROM test WHERE label > ?', + $params, array('execute' => array('sqlstate' => 'HY093'))); + } + + prepex(61, $db, 'DELETE FROM test'); + prepex(62, $db, 'INSERT INTO test(id, label) VALUES (1, ?), (2, ?)', + array('row', 'row')); + $stmt = prepex(63, $db, 'SELECT id, label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = prepex(64, $db, 'SELECT id, label FROM test WHERE ? IS NOT NULL', + array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[065] '1' IS NOT NULL evaluates to true, expecting two rows, got %d rows\n", $tmp); + + $stmt = prepex(66, $db, 'SELECT id, label FROM test WHERE ? IS NULL', + array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[067] '1' IS NOT NULL evaluates to true, expecting zero rows, got %d rows\n", $tmp); + + prepex(68, $db, 'DROP TABLE IF EXISTS test'); + prepex(69, $db, 'CREATE TABLE test(id INT, label CHAR(255)) ENGINE=MyISAM'); + if (is_object(prepex(70, $db, 'CREATE FULLTEXT INDEX idx1 ON test(label)'))) { + prepex(71, $db, 'INSERT INTO test(id, label) VALUES (1, ?)', + array('MySQL is the best database in the world!')); + prepex(72, $db, 'INSERT INTO test(id, label) VALUES (1, ?)', + array('If I have the freedom to choose, I would always go again for the MySQL Server')); + $stmt = prepex(73, $db, 'SELECT id, label FROM test WHERE MATCH label AGAINST (?)', + array('mysql')); + /* + Lets ignore that + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[074] Expecting two rows, got %d rows\n", $tmp); + */ + } + + prepex(74, $db, 'DELETE FROM test'); + prepex(75, $db, 'INSERT INTO test(id, label) VALUES (1, ?), (2, ?)', + array('row', 'row')); + + $stmt = prepex(76, $db, 'SELECT id, label FROM "test WHERE MATCH label AGAINST (?)', + array('row'), + array('execute' => array('sqlstate' => '42000', 'mysql' => 1064))); + + /* + TODO enable after fix + $stmt = prepex(37, $db, 'SELECT id, label FROM \'test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'row'), + array('execute' => array('sqlstate' => '42000', 'mysql' => 1064))); + */ + + $stmt = prepex(78, $db, 'SELECT id, label AS "label" FROM test WHERE label = ?', + array('row')); + + $sql = sprintf("SELECT id, label FROM test WHERE (label LIKE %s) AND (id = ?)", + $db->quote('%ro%')); + $stmt = prepex(79, $db, $sql, array(-1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[080] Expecting zero rows, got %d rows\n", $tmp); + + + $sql = sprintf("SELECT id, label FROM test WHERE (id = ?) OR (label LIKE %s)", + $db->quote('%ro%')); + $stmt = prepex(81, $db, $sql, array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[082] Expecting two rows, got %d rows\n", $tmp); + + $sql = "SELECT id, label FROM test WHERE id = ? AND label = (SELECT label AS 'SELECT' FROM test WHERE id = ?)"; + $stmt = prepex(83, $db, $sql, array(1, 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 1) + printf("[084] Expecting one row, got %d rows\n", $tmp); + + $sql = "SELECT id, label FROM test WHERE id = :placeholder AND label = (SELECT label AS 'SELECT' FROM test WHERE id = ?)"; + $stmt = prepex(85, $db, $sql, array(1, 1), array('execute' => array('sqlstate' => 'HY093'))); + if (is_object($stmt) && count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[086] Expecting no rows, got %d rows\n", $tmp); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--XFAIL-- +PDO's PS parser has some problems with invalid SQL and crashes from time to time +(check with valgrind...) +--EXPECTF-- +array(1) { + ["one"]=> + string(1) "1" +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(12) ":placeholder" + } +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(12) ":placeholder" + } +} +array(2) { + [0]=> + array(1) { + ["label"]=> + string(9) "first row" + } + [1]=> + array(1) { + ["label"]=> + string(10) "second row" + } +} +array(2) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(3) "row" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(3) "row" + } +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "?" + } +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "?" + } +} +array(2) { + [0]=> + array(1) { + ["label"]=> + string(9) "first row" + } + [1]=> + array(1) { + ["label"]=> + string(10) "second row" + } +} +array(2) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(3) "row" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(3) "row" + } +} +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt new file mode 100644 index 0000000000..1f333b9a99 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt @@ -0,0 +1,78 @@ +--TEST-- +MySQL PDO->prepare(), emulated PS, anonymous placeholder +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to switch to emulated prepared statements, test will fail\n"); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES(1, "?")'); + // you can bind as many values as you want no matter if they can be replaced or not + $stmt->execute(array('first row')); + if ('00000' !== $stmt->errorCode()) + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // now the same with native PS + printf("now the same with native PS\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to switch off emulated prepared statements, test will fail\n"); + + $db->exec('DELETE FROM test'); + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES(1, "?")'); + // you can bind as many values as you want no matter if they can be replaced or not + $stmt->execute(array('first row')); + if ('00000' !== $stmt->errorCode()) + printf("[005] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "?" + } +} +now the same with native PS +[005] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt new file mode 100644 index 0000000000..be83446c85 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt @@ -0,0 +1,75 @@ +--TEST-- +MySQL PDO->prepare(), emulated PS, anonymous placeholder +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to switch off emulated prepared statements, test will fail\n"); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + $db->exec('INSERT INTO test(id, label) VALUES (1, "row1")'); + + // So, what will happen? More placeholder but values and + // placeholders in interesting places... + $stmt = $db->prepare('SELECT ? FROM test WHERE ? > ?'); + $stmt->execute(array('test')); + if ('00000' !== $stmt->errorCode()) { + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + } + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // now the same with emulated PS + printf("now the same with emulated PS\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to switch on emulated prepared statements, test will fail\n"); + + $stmt = $db->prepare('SELECT ? FROM test WHERE ? > ?'); + $stmt->execute(array('test')); + if ('00000' !== $stmt->errorCode()) + printf("[005] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec(sprintf('DROP TABLE IF EXISTS test')); + print "done!"; +?> +--EXPECTF-- +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d +[003] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +now the same with emulated PS + +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d + +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line 33 +[005] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt new file mode 100644 index 0000000000..965d9cb161 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt @@ -0,0 +1,48 @@ +--TEST-- +MySQL PDO->prepare(MATCH ... AGAINST (:placeholder)) - similar: http://bugs.php.net/bug.php?id=41876 +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=MyISAM'); + $db->exec('CREATE FULLTEXT INDEX idx1 ON test(label)'); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE MATCH label AGAINST (:placeholder)'); + $stmt->execute(array(':placeholder' => 'row')); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE MATCH label AGAINST (:placeholder)'); + $stmt->execute(array('placeholder' => 'row')); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE MATCH label AGAINST (?)'); + $stmt->execute(array('row')); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + } catch (PDOException $e) { + + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +--EXPECTF-- +array(0) { +} +array(0) { +} +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt new file mode 100644 index 0000000000..87438011fb --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt @@ -0,0 +1,380 @@ +--TEST-- +MySQL PDO->prepare(), native PS +--SKIPIF-- + +--FILE-- +prepare($query); + else + $stmt = $db->prepare($query); + + if (is_array($error_info) && isset($error_info['prepare'])) { + $tmp = $db->errorInfo(); + + if (isset($error_info['prepare']['sqlstate']) && + ($error_info['prepare']['sqlstate'] !== $tmp[0])) { + printf("[%03d] prepare() - expecting SQLSTATE '%s' got '%s'\n", + $offset, $error_info['prepare']['sqlstate'], $tmp[0]); + return false; + } + + if (isset($error_info['prepare']['mysql']) && + ($error_info['prepare']['mysql'] !== $tmp[1])) { + printf("[%03d] prepare() - expecting MySQL Code '%s' got '%s'\n", + $offset, $error_info['prepare']['mysql'], $tmp[0]); + return false; + } + + return false; + } + + if (!is_object($stmt)) + return false; + + if (is_null($input_params)) + $input_params = array(); +// 5.0.18, 5.1.14 @ 15 +// printf("[%03d]\n", $offset); + if ($suppress_warning || (is_array($error_info) && isset($error_info['execute']))) + $ret = @$stmt->execute($input_params); + else + $ret = $stmt->execute($input_params); + + if (!is_bool($ret)) + printf("[%03d] PDO::execute() should return a boolean value, got %s/%s\n", + var_export($ret, true), $ret); + + $tmp = $stmt->errorInfo(); + if (isset($tmp[1]) && ($tmp[1] == 2030)) { + // Trying to hack around MySQL Server version dependent features + // 2030 This command is not supported in the prepared statement protocol yet + return false; + } + + if (is_array($error_info) && isset($error_info['execute'])) { + + if (isset($error_info['execute']['sqlstate']) && + ($error_info['execute']['sqlstate'] !== $tmp[0])) { + printf("[%03d] execute() - expecting SQLSTATE '%s' got '%s'\n", + $offset, $error_info['execute']['sqlstate'], $tmp[0]); + return false; + } + + if (isset($error_info['execute']['mysql']) && + ($error_info['execute']['mysql'] !== $tmp[1])) { + printf("[%03d] execute() - expecting MySQL Code '%s' got '%s'\n", + $offset, $error_info['execute']['mysql'], $tmp[0]); + return false; + } + + return false; + + } + + } catch (PDOException $e) { + printf("[%03d] %s, [%s} %s\n", + $offset, $e->getMessage(), + $db->errorCode(), implode(' ', $db->errorInfo())); + return false; + } + + return $stmt; + } + + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + // TODO - that's PDO - you can prepare empty statements! + prepex(3, $db, '', + array(), array('prepare' => array('sqlstate' => '42000'))); + + // lets be fair and do the most simple SELECT first + $stmt = prepex(4, $db, 'SELECT 1 as "one"'); + if (MySQLPDOTest::isPDOMySQLnd()) + // native types - int + $expected = array('one' => 1); + else + // always strings, like STRINGIFY flag + $expected = array('one' => '1'); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row !== $expected) { + printf("[004a] Expecting %s got %s\n", var_export($expected, true), vat_export($row, true)); + } + + prepex(5, $db, 'DROP TABLE IF EXISTS test'); + prepex(6, $db, sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + prepex(7, $db, 'INSERT INTO test(id, label) VALUES(1, ":placeholder")'); + $stmt = prepex(8, $db, 'SELECT label FROM test ORDER BY id ASC'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + prepex(9, $db, 'DELETE FROM test'); + prepex(10, $db, 'INSERT INTO test(id, label) VALUES(1, :placeholder)', + array(':placeholder' => 'first row')); + prepex(11, $db, 'INSERT INTO test(id, label) VALUES(2, :placeholder)', + array(':placeholder' => 'second row')); + $stmt = prepex(12, $db, 'SELECT label FROM test ORDER BY id ASC'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Is PDO fun? + $stmt = prepex(13, $db, 'SELECT label FROM test WHERE :placeholder > 1', + array(':placeholder' => 'id')); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + for ($num_params = 2; $num_params < 100; $num_params++) { + $params = array(':placeholder' => 'a'); + for ($i = 1; $i < $num_params; $i++) { + $params[str_repeat('a', $i)] = 'some data'; + } + prepex(16, $db, 'SELECT id, label FROM test WHERE label > :placeholder', + $params, array('execute' => array('sqlstate' => 'HY093'))); + } + + $stmt = prepex(16, $db, 'SELECT id, label FROM test WHERE :placeholder IS NOT NULL', + array(':placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[017] '1' IS NOT NULL evaluates to true, expecting two rows, got %d rows\n", $tmp); + + $stmt = prepex(18, $db, 'SELECT id, label FROM test WHERE :placeholder IS NULL', + array(':placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[019] '1' IS NOT NULL evaluates to true, expecting zero rows, got %d rows\n", $tmp); + + prepex(20, $db, 'DROP TABLE IF EXISTS test'); + prepex(21, $db, 'CREATE TABLE test(id INT, label CHAR(255)) ENGINE=MyISAM'); + // Not every MySQL Server version supports this + if (is_object(prepex(22, $db, 'CREATE FULLTEXT INDEX idx1 ON test(label)', null, null, true))) { + prepex(23, $db, 'INSERT INTO test(id, label) VALUES (1, :placeholder)', + array(':placeholder' => 'MySQL is the best database in the world!')); + prepex(24, $db, 'INSERT INTO test(id, label) VALUES (2, :placeholder)', + array(':placeholder' => 'If I have the freedom to choose, I would always go again for the MySQL Server')); + $stmt = prepex(25, $db, 'SELECT id, label FROM test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'mysql'), null, true); + if (is_object($stmt)) { + /* + Lets ignore this + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[033] Expecting two rows, got %d rows\n", $tmp); + */ + $stmt = prepex(26, $db, 'SELECT id, label FROM test ORDER BY id ASC'); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[027] Expecting two rows, got %d rows\n", $tmp); + + if ($tmp[0]['label'] !== 'MySQL is the best database in the world!') { + printf("[028] INSERT seems to have failed, dumping data, check manually\n"); + var_dump($tmp); + } + } + } + + $db->exec('DELETE FROM test'); + $db->exec('INSERT INTO test(id, label) VALUES (1, "row1")'); + $db->exec('INSERT INTO test(id, label) VALUES (2, "row2")'); + + $sql = sprintf("SELECT id, label FROM test WHERE (label LIKE %s) AND (id = :placeholder)", + $db->quote('%ro%')); + $stmt = prepex(29, $db, $sql, array('placeholder' => -1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[030] Expecting zero rows, got %d rows\n", $tmp); + + $sql = sprintf("SELECT id, label FROM test WHERE (id = :placeholder) OR (label LIKE %s)", + $db->quote('%go%')); + $stmt = prepex(31, $db, $sql, array('placeholder' => 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 1) + printf("[032] Expecting one row, got %d rows\n", $tmp); + + // and now, the same with anonymous placeholders... + prepex(33, $db, 'DROP TABLE IF EXISTS test'); + prepex(34, $db, sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + prepex(35, $db, 'INSERT INTO test(id, label) VALUES(1, "?")'); + $stmt = prepex(36, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + prepex(37, $db, 'DELETE FROM test'); + prepex(38, $db, 'INSERT INTO test(id, label) VALUES(1, ?)', + array('first row')); + prepex(39, $db, 'INSERT INTO test(id, label) VALUES(2, ?)', + array('second row')); + $stmt = prepex(40, $db, 'SELECT label FROM test'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Is PDO fun? + prepex(40, $db, 'SELECT label FROM test WHERE ? > 1', + array('id')); + prepex(41, $db, 'SELECT ? FROM test WHERE id > 1', + array('id')); + prepex(42, $db, 'SELECT ? FROM test WHERE ? > ?', + array('id', 'label', 'value')); + + for ($num_params = 2; $num_params < 100; $num_params++) { + $params = array('a'); + for ($i = 1; $i < $num_params; $i++) { + $params[] = 'some data'; + } + prepex(43, $db, 'SELECT id, label FROM test WHERE label > ?', + $params, array('execute' => array('sqlstate' => 'HY093'))); + } + + prepex(44, $db, 'DELETE FROM test'); + prepex(45, $db, 'INSERT INTO test(id, label) VALUES (1, ?), (2, ?)', + array('row', 'row')); + $stmt = prepex(46, $db, 'SELECT id, label FROM test'); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + $exp = array( + 0 => array( + "id" => "1", + "label" => "row" + ), + 1 => array( + "id" => "2", + "label" => "row" + ), + ); + + if (MySQLPDOTest::isPDOMySQLnd()) { + // mysqlnd returns native types + $exp[0]['id'] = 1; + $exp[1]['id'] = 2; + } + if ($tmp !== $exp) { + printf("[064] Results seem wrong. Please check dumps manually.\n"); + var_dump($exp); + var_dump($tmp); + } + + $stmt = prepex(47, $db, 'SELECT id, label FROM test WHERE ? IS NOT NULL', + array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[048] '1' IS NOT NULL evaluates to true, expecting two rows, got %d rows\n", $tmp); + + $stmt = prepex(49, $db, 'SELECT id, label FROM test WHERE ? IS NULL', + array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[050] '1' IS NOT NULL evaluates to true, expecting zero rows, got %d rows\n", $tmp); + + prepex(51, $db, 'DROP TABLE IF EXISTS test'); + prepex(52, $db, 'CREATE TABLE test(id INT, label CHAR(255)) ENGINE=MyISAM'); + if (is_object(prepex(53, $db, 'CREATE FULLTEXT INDEX idx1 ON test(label)', null, null, true))) { + prepex(54, $db, 'INSERT INTO test(id, label) VALUES (1, ?)', + array('MySQL is the best database in the world!')); + prepex(55, $db, 'INSERT INTO test(id, label) VALUES (1, ?)', + array('If I have the freedom to choose, I would always go again for the MySQL Server')); + $stmt = prepex(56, $db, 'SELECT id, label FROM test WHERE MATCH label AGAINST (?)', + array('mysql'), null, true); + /* + Lets ignore that + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[074] Expecting two rows, got %d rows\n", $tmp); + */ + } + + prepex(57, $db, 'DELETE FROM test'); + prepex(58, $db, 'INSERT INTO test(id, label) VALUES (1, ?), (2, ?)', + array('row1', 'row2')); + + /* + TODO enable after fix + $stmt = prepex(37, $db, 'SELECT id, label FROM \'test WHERE MATCH label AGAINST (:placeholder)', + array(':placeholder' => 'row'), + array('execute' => array('sqlstate' => '42000', 'mysql' => 1064))); + */ + + $stmt = prepex(59, $db, 'SELECT id, label AS "label" FROM test WHERE label = ?', + array('row1')); + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + $exp = array( + 0 => array("id" => "1", "label" => "row1") + ); + + if (MySQLPDOTest::isPDOMySQLnd()) { + // mysqlnd returns native types + $exp[0]['id'] = 1; + } + if ($tmp !== $exp) { + printf("[065] Results seem wrong. Please check dumps manually.\n"); + var_dump($exp); + var_dump($tmp); + } + + $sql = sprintf("SELECT id, label FROM test WHERE (label LIKE %s) AND (id = ?)", + $db->quote('%ro%')); + $stmt = prepex(60, $db, $sql, array(-1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 0) + printf("[061] Expecting zero rows, got %d rows\n", $tmp); + + $sql = sprintf("SELECT id, label FROM test WHERE (id = ?) OR (label LIKE %s)", + $db->quote('%ro%')); + $stmt = prepex(61, $db, $sql, array(1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 2) + printf("[062] Expecting two rows, got %d rows\n", $tmp); + + $sql = "SELECT id, label FROM test WHERE id = ? AND label = (SELECT label AS 'SELECT' FROM test WHERE id = ?)"; + $stmt = prepex(63, $db, $sql, array(1, 1)); + if (count(($tmp = $stmt->fetchAll(PDO::FETCH_ASSOC))) != 1) + printf("[064] Expecting one row, got %d rows\n", $tmp); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +array(1) { + [0]=> + array(1) { + ["label"]=> + string(12) ":placeholder" + } +} +array(2) { + [0]=> + array(1) { + ["label"]=> + string(9) "first row" + } + [1]=> + array(1) { + ["label"]=> + string(10) "second row" + } +} +array(0) { +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "?" + } +} +array(2) { + [0]=> + array(1) { + ["label"]=> + string(9) "first row" + } + [1]=> + array(1) { + ["label"]=> + string(10) "second row" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt new file mode 100644 index 0000000000..33c699c15f --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt @@ -0,0 +1,91 @@ +--TEST-- +MySQL PDO->prepare(), native PS, clear line after error +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + + // We need to run the emulated version first. Native version will cause a fatal error + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + // INSERT a single row + $db->exec('INSERT INTO test(id, label) VALUES (1, "row1")'); + + $stmt = $db->prepare('SELECT unknown_column FROM test WHERE id > :placeholder ORDER BY id ASC'); + $stmt->execute(array(':placeholder' => 0)); + if ('00000' !== $stmt->errorCode()) + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > :placeholder ORDER BY id ASC'); + $stmt->execute(array(':placeholder' => 0)); + if ('00000' !== $stmt->errorCode()) + printf("[004] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Native PS + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[005] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT unknown_column FROM test WHERE id > :placeholder ORDER BY id ASC'); + $stmt->execute(array(':placeholder' => 0)); + if ('00000' !== $stmt->errorCode()) + printf("[006] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > :placeholder ORDER BY id ASC'); + $stmt->execute(array(':placeholder' => 0)); + if ('00000' !== $stmt->errorCode()) + printf("[007] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Warning: PDOStatement::execute(): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'unknown_column' in 'field list' in %s on line %d +[003] Execute has failed, '42S22' array ( + 0 => '42S22', + 1 => 1054, + 2 => 'Unknown column \'unknown_column\' in \'field list\'', +) +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(4) "row1" + } +} + +Warning: PDO::prepare(): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'unknown_column' in 'field list' in %s on line %d + +Fatal error: Call to a member function execute() on a non-object in %s on line %d \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt new file mode 100644 index 0000000000..b497268f99 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt @@ -0,0 +1,44 @@ +--TEST-- +MySQL PDO->prepare(), native PS, named placeholder +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare("SELECT :param FROM test ORDER BY id ASC LIMIT 1"); + $stmt->execute(array(':param' => 'id')); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->prepare('SELECT :placeholder FROM test WHERE :placeholder > :placeholder'); + $stmt->execute(array(':placeholder' => 'test')); + + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +array(1) { + [0]=> + array(1) { + ["?"]=> + string(2) "id" + } +} + +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in %s on line %d +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt new file mode 100644 index 0000000000..a84c8d56d2 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt @@ -0,0 +1,136 @@ +--TEST-- +MySQL PDO->prepare(), native PS, named placeholder II +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label1 CHAR(255), label2 CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + printf("Native...\n"); + + // INSERT a single row + $stmt = $db->prepare('INSERT INTO test(id, label1, label2) VALUES (1, :placeholder, :placeholder)'); + + $stmt->execute(array(':placeholder' => 'row1')); + if ('00000' !== $stmt->errorCode()) + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + // Ok, what has happened: anything inserted into the DB? + $stmt = $db->prepare('SELECT id, label1, label2 FROM test WHERE id = 1'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Now the same with emulated PS. + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn on emulated prepared statements\n"); + printf("Emulated...\n"); + + $stmt = $db->prepare('INSERT INTO test(id, label1, label2) VALUES(2, :placeholder, :placeholder)'); + // No replacement shall be made + $stmt->execute(array(':placeholder' => 'row2')); + if ('00000' !== $stmt->errorCode()) + printf("[005] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + // Now, what do we have in the DB? + $stmt = $db->prepare('SELECT id, label1, label2 FROM test WHERE id = 2'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // + // Another variation of the theme + // + + $db->exec('DELETE FROM test'); + $db->exec('INSERT INTO test (id, label1, label2) VALUES (1, "row1", "row2")'); + $sql = "SELECT id, label1 FROM test WHERE id = :placeholder AND label1 = (SELECT label1 AS 'SELECT' FROM test WHERE id = :placeholder)"; + + // emulated... + $stmt = $db->prepare($sql); + $stmt->execute(array(':placeholder' => 1)); + if ('00000' !== $stmt->errorCode()) + printf("[006] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // native... + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[007] Unable to turn off emulated prepared statements\n"); + printf("Native...\n"); + + $stmt = $db->prepare($sql); + $stmt->execute(array(':placeholder' => 1)); + if ('00000' !== $stmt->errorCode()) + printf("[008] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Native... + +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d +[003] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +Emulated... +array(1) { + [0]=> + array(3) { + ["id"]=> + string(1) "2" + ["label1"]=> + string(4) "row2" + ["label2"]=> + string(4) "row2" + } +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label1"]=> + string(4) "row1" + } +} +Native... + +Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d +[008] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt new file mode 100644 index 0000000000..c8d1a5ba83 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt @@ -0,0 +1,35 @@ +--TEST-- +MySQL PDO->prepare(), native PS, mixed, wired style +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->query('DELETE FROM test'); + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (1, ?), (2, ?)'); + $stmt->execute(array('a', 'b')); + $stmt = $db->prepare("SELECT id, label FROM test WHERE id = :placeholder AND label = (SELECT label AS 'SELECT' FROM test WHERE id = ?)"); + $stmt->execute(array(1, 1)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Warning: PDO::prepare(): SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters in %s on line %d + +Warning: PDO::prepare(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d + +Fatal error: Call to a member function execute() on a non-object in %s on line %d \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt new file mode 100644 index 0000000000..6f673c78b2 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt @@ -0,0 +1,85 @@ +--TEST-- +MySQL PDO->prepare(), native PS, named placeholder +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + // INSERT a single row + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (100, ":placeholder")'); + + // Yes, there is no placeholder to bind to and named placeholder + // do not work with MySQL native PS, but lets see what happens! + // The ':placeholder' is a string constant in the INSERT statement. + // I would expect to get an error message, but this is not what happens. + $stmt->execute(array(':placeholder' => 'row1')); + if ('00000' !== $stmt->errorCode()) + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + // Ok, what has happened: anything inserted into the DB? + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // Now the same with emulated PS. + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn on emulated prepared statements\n"); + + // Note that the "named placeholder" is enclosed by double quotes. + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES(101, ":placeholder")'); + // No replacement shall be made + $stmt->execute(array(':placeholder' => 'row1')); + // Again, I'd like to see an error message + if ('00000' !== $stmt->errorCode()) + printf("[005] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + // Now, what do we have in the DB? + $stmt = $db->prepare('SELECT id, label FROM test ORDER BY id'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +[003] Execute has failed, 'HY093' array ( + 0 => 'HY093', +) +array(0) { +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(3) "101" + ["label"]=> + string(12) ":placeholder" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt new file mode 100644 index 0000000000..aa7815dea8 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt @@ -0,0 +1,85 @@ +--TEST-- +MySQL PDO->prepare(),native PS, anonymous placeholder +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to switch on emulated prepared statements, test will fail\n"); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label CHAR(255)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE)); + $db->exec('INSERT INTO test(id, label) VALUES (1, "row1")'); + + $stmt = $db->prepare('SELECT ?, id, label FROM test WHERE ? = ? ORDER BY id ASC'); + $stmt->execute(array('id', 'label', 'label')); + if ('00000' !== $stmt->errorCode()) + printf("[003] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + // now the same with native PS + printf("now the same with native PS\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to switch off emulated prepared statements, test will fail\n"); + + $stmt = $db->prepare('SELECT ?, id, label FROM test WHERE ? = ? ORDER BY id ASC'); + $stmt->execute(array('id', 'label', 'label')); + if ('00000' !== $stmt->errorCode()) + printf("[005] Execute has failed, %s %s\n", + var_export($stmt->errorCode(), true), + var_export($stmt->errorInfo(), true)); + + $tmp = $stmt->fetchAll(PDO::FETCH_ASSOC); + if (!MySQLPDOTest::isPDOMySQLnd()) { + if (isset($tmp[0]['id'])) { + // libmysql should return a string here whereas mysqlnd returns a native int + if (gettype($tmp[0]['id']) == 'string') + // convert to int for the test output... + settype($tmp[0]['id'], 'integer'); + } + } + var_dump($tmp); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(4) "row1" + } +} +now the same with native PS +array(1) { + [0]=> + array(3) { + ["?"]=> + string(2) "id" + ["id"]=> + int(1) + ["label"]=> + string(4) "row1" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_rollback.phpt b/ext/pdo_mysql/tests/pdo_mysql_rollback.phpt new file mode 100644 index 0000000000..358d4a780d --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_rollback.phpt @@ -0,0 +1,90 @@ +--TEST-- +PDO::rollBack() +--SKIPIF-- + +--FILE-- +beginTransaction(); + + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + $num = $row['_num']; + + $db->query('INSERT INTO test(id, label) VALUES (100, "z")'); + $num++; + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[001] INSERT has failed, test will fail\n"); + + $db->rollBack(); + $num--; + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[002] ROLLBACK has failed\n"); + + $db->beginTransaction(); + $db->query('INSERT INTO test(id, label) VALUES (100, "z")'); + $db->query('DROP TABLE IF EXISTS test2'); + $db->query('CREATE TABLE test2(id INT)'); + $num++; + $db->rollBack(); + $row = $db->query('SELECT COUNT(*) AS _num FROM test')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != $num) + printf("[002] ROLLBACK should have no effect because of the implicit COMMIT + triggered by DROP/CREATE TABLE\n"); + + + $db->query('DROP TABLE IF EXISTS test2'); + $db->query('CREATE TABLE test2(id INT) ENGINE=MyISAM'); + $db->beginTransaction(); + $db->query('INSERT INTO test2(id) VALUES (1)'); + $db->rollBack(); + $row = $db->query('SELECT COUNT(*) AS _num FROM test2')->fetch(PDO::FETCH_ASSOC); + if ($row['_num'] != 1) + printf("[003] ROLLBACK should have no effect\n"); + + $db->query('DROP TABLE IF EXISTS test2'); + + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1); + $db->beginTransaction(); + $db->query('DELETE FROM test'); + $db->rollBack(); + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0); + $db->beginTransaction(); + $db->query('DELETE FROM test'); + $db->rollBack(); + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1); + $db->beginTransaction(); + $db->query('DELETE FROM test'); + $db->commit(); + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0); + $db->beginTransaction(); + $db->query('DELETE FROM test'); + $db->commit(); + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec('DROP TABLE IF EXISTS test2'); + print "done!"; +--EXPECTF-- +int(1) +int(0) +int(1) +int(0) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt new file mode 100644 index 0000000000..394ed4f45d --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt @@ -0,0 +1,110 @@ +--TEST-- +MySQL PDOStatement->bindColumn() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id, label FROM test ORDER BY id ASC LIMIT 2'); + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[003] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[004] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $data = array(); + while ($stmt->fetch(PDO::FETCH_BOUND)) { + printf("id = %s (%s) / label = %s (%s)\n", + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + $data[] = array('id' => $id, 'label' => $label); + } + + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 2'); + $index = 0; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + if ($row['id'] != $data[$index]['id']) { + printf("[005] Fetch bound and fetch assoc differ - column 'id', bound: %s/%s, assoc: %s/%s\n", + var_export($data[$index]['id'], true), gettype($data[$index]['id']), + var_export($row['id'], true), gettype($row['id'])); + } + if ($row['label'] != $data[$index]['label']) { + printf("[006] Fetch bound and fetch assoc differ - column 'label', bound: %s/%s, assoc: %s/%s\n", + var_export($data[$index]['label'], true), gettype($data[$index]['label']), + var_export($row['label'], true), gettype($row['label'])); + } + $index++; + } + + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[007] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id, label FROM test ORDER BY id ASC LIMIT 2, 2'); + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[008] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[009] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $data = array(); + while ($stmt->fetch(PDO::FETCH_BOUND)) { + printf("id = %s (%s) / label = %s (%s)\n", + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + $data[] = array('id' => $id, 'label' => $label); + } + + $stmt = $db->query('SELECT id, label FROM test ORDER BY id ASC LIMIT 2, 2'); + $index = 0; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + if ($row['id'] != $data[$index]['id']) { + printf("[010] Fetch bound and fetch assoc differ - column 'id', bound: %s/%s, assoc: %s/%s\n", + var_export($data[$index]['id'], true), gettype($data[$index]['id']), + var_export($row['id'], true), gettype($row['id'])); + } + if ($row['label'] != $data[$index]['label']) { + printf("[011] Fetch bound and fetch assoc differ - column 'label', bound: %s/%s, assoc: %s/%s\n", + var_export($data[$index]['label'], true), gettype($data[$index]['label']), + var_export($row['label'], true), gettype($row['label'])); + } + $index++; + } + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +id = 1 (integer) / label = 'a' (string) +id = 2 (integer) / label = 'b' (string) +id = 3 (integer) / label = 'c' (string) +id = 4 (integer) / label = 'd' (string) +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt new file mode 100644 index 0000000000..993ea0447d --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt @@ -0,0 +1,155 @@ +--TEST-- +MySQL PDOStatement->bindParam() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + MySQLPDOTest::createTestTable($db); + + function pdo_mysql_stmt_bindparam($db, $offset) { + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindParam(1, $in)) + printf("[%03d + 1] Cannot bind parameter, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[%03d + 2] Cannot bind integer column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[%03d + 3] Cannot bind string column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Same again...\n"); + $stmt->execute(); + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + // NULL values + printf("NULL...\n"); + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (100, ?)'); + $label = null; + if (!$stmt->bindParam(1, $label)) + printf("[%03d + 4] Cannot bind parameter, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->execute()) + printf("[%03d + 5] Cannot execute statement, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + /* NOTE: you cannot use PDO::query() with unbuffered, native PS - see extra test */ + $stmt = $db->prepare('SELECT id, NULL AS _label FROM test WHERE label IS NULL'); + $stmt->execute(); + + $id = $label = 'bogus'; + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[%03d + 6] Cannot bind NULL column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[%03d + 3] Cannot bind string column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + } + + try { + printf("Emulated PS...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + printf("Buffered...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + pdo_mysql_stmt_bindparam($db, 3); + + printf("Unbuffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + pdo_mysql_stmt_bindparam($db, 4); + + printf("Native PS...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn off emulated prepared statements\n"); + + printf("Buffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + pdo_mysql_stmt_bindparam($db, 5); + + printf("Unbuffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + pdo_mysql_stmt_bindparam($db, 6); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Emulated PS... +Buffered... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Same again... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +NULL... +in = 0 -> id = 100 (integer) / label = NULL (NULL) +Unbuffered... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Same again... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +NULL... +in = 0 -> id = 100 (integer) / label = NULL (NULL) +Native PS... +Buffered... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Same again... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +NULL... +in = 0 -> id = 100 (integer) / label = NULL (NULL) +Unbuffered... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Same again... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +NULL... +in = 0 -> id = 100 (integer) / label = NULL (NULL) +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt new file mode 100644 index 0000000000..64d4df0641 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt @@ -0,0 +1,169 @@ +--TEST-- +MySQL PDOStatement->bindParam() - SQL column types +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + else + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + + $db->exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine()); + if ((!$stmt = @$db->prepare($sql)) || (!@$stmt->execute())) + // Server might not support column type - skip it + return true; + + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (1, ?)'); + if (!$stmt->bindParam(1, $value)) { + printf("[%03d/%s + 1] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + if (!$stmt->execute()) { + printf("[%03d/%s + 2] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + + $stmt = $db->query('SELECT id, label FROM test'); + $id = $label = null; + if (!$stmt->bindColumn(1, $id)) { + printf("[%03d/%s + 3] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + if (!$stmt->bindColumn(2, $label)) { + printf("[%03d/%s + 4] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + + if (!$stmt->fetch(PDO::FETCH_BOUND)) { + printf("[%03d/%s + 5] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + $stmt->closeCursor(); + + if ($label != $value) { + printf("[%03d/%s + 6] Got %s expecting %s - plase check manually\n", + $offset, ($native) ? 'native' : 'emulated', + var_export($label, true), var_export($value, true)); + // fall through + } + + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (empty($row)) { + printf("[%03d/%s + 7] %s\n", $offset, ($native) ? 'native' : 'emulated', + var_export($stmt->errorInfo(), true)); + return false; + } + + if ($row['label'] != $value) { + printf("[%03d/%s + 8] Got %s expecting %s - plase check manually\n", + $offset, ($native) ? 'native' : 'emulated', + var_export($row['label'], true), var_export($value, true)); + return false; + } + + if ($row['label'] != $label) { + printf("[%03d/%s + 9] Got %s from FETCH_ASSOC and %s from FETCH_BOUND- plase check manually\n", + $offset, ($native) ? 'native' : 'emulated', + var_export($row['label'], true), var_export($value, true)); + return false; + } + + $db->exec('DROP TABLE IF EXISTS test'); + return true; + } + + function pdo_mysql_stmt_bindparam_types($db, $offset, $sql_type, $value) { + + pdo_mysql_stmt_bindparam_types_do($db, $offset, true, $sql_type, $value); + pdo_mysql_stmt_bindparam_types_do($db, $offset, false, $sql_type, $value); + + } + + try { + + // pdo_mysql_stmt_bindparam_types($db, 2, 'BIT(8)', 1); + pdo_mysql_stmt_bindparam_types($db, 3, 'TINYINT', -127); + pdo_mysql_stmt_bindparam_types($db, 4, 'TINYINT UNSIGNED', 255); + pdo_mysql_stmt_bindparam_types($db, 5, 'BOOLEAN', 1); + pdo_mysql_stmt_bindparam_types($db, 6, 'SMALLINT', -32768); + pdo_mysql_stmt_bindparam_types($db, 7, 'SMALLINT UNSIGNED', 65535); + pdo_mysql_stmt_bindparam_types($db, 8, 'MEDIUMINT', -8388608); + pdo_mysql_stmt_bindparam_types($db, 9, 'MEDIUMINT UNSIGNED', 16777215); + pdo_mysql_stmt_bindparam_types($db, 10, 'INT', -2147483648); + pdo_mysql_stmt_bindparam_types($db, 11, 'INT UNSIGNED', 4294967295); + pdo_mysql_stmt_bindparam_types($db, 12, 'BIGINT', -1000); + pdo_mysql_stmt_bindparam_types($db, 13, 'BIGINT UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 14, 'REAL', -1000); + pdo_mysql_stmt_bindparam_types($db, 15, 'REAL UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 16, 'REAL ZEROFILL', '0000000000000000000000'); + pdo_mysql_stmt_bindparam_types($db, 17, 'REAL UNSIGNED ZEROFILL', '0000000000000000000010'); + pdo_mysql_stmt_bindparam_types($db, 18, 'DOUBLE', -1000); + pdo_mysql_stmt_bindparam_types($db, 19, 'DOUBLE UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 20, 'DOUBLE ZEROFILL', '000000000000'); + pdo_mysql_stmt_bindparam_types($db, 21, 'DOUBLE ZEROFILL UNSIGNED', '000000001000'); + pdo_mysql_stmt_bindparam_types($db, 22, 'FLOAT', -1000); + pdo_mysql_stmt_bindparam_types($db, 23, 'FLOAT UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 24, 'FLOAT ZEROFILL', '000000000000'); + pdo_mysql_stmt_bindparam_types($db, 25, 'FLOAT ZEROFILL UNSIGNED', '000000001000'); + pdo_mysql_stmt_bindparam_types($db, 26, 'DECIMAL', -1000); + pdo_mysql_stmt_bindparam_types($db, 27, 'DECIMAL UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 28, 'DECIMAL ZEROFILL', '000000000000'); + pdo_mysql_stmt_bindparam_types($db, 29, 'DECIMAL ZEROFILL UNSIGNED', '000000001000'); + pdo_mysql_stmt_bindparam_types($db, 30, 'NUMERIC', -1000); + pdo_mysql_stmt_bindparam_types($db, 31, 'NUMERIC UNSIGNED', 1000); + pdo_mysql_stmt_bindparam_types($db, 32, 'NUMERIC ZEROFILL', '000000000000'); + pdo_mysql_stmt_bindparam_types($db, 33, 'NUMERIC ZEROFILL UNSIGNED', '000000001000'); + pdo_mysql_stmt_bindparam_types($db, 34, 'DATE', '2008-04-23'); + pdo_mysql_stmt_bindparam_types($db, 35, 'TIME', '16:43:12'); + pdo_mysql_stmt_bindparam_types($db, 36, 'TIMESTAMP', '2008-04-23 16:44:53'); + pdo_mysql_stmt_bindparam_types($db, 37, 'DATETIME', '2008-04-23 16:44:53'); + pdo_mysql_stmt_bindparam_types($db, 38, 'YEAR', '2008'); + pdo_mysql_stmt_bindparam_types($db, 39, 'CHAR(1)', 'a'); + pdo_mysql_stmt_bindparam_types($db, 40, 'CHAR(255)', 'abc'); + pdo_mysql_stmt_bindparam_types($db, 41, 'VARCHAR(255)', str_repeat('a', 255)); + pdo_mysql_stmt_bindparam_types($db, 42, 'BINARY(255)', str_repeat('a', 255)); + pdo_mysql_stmt_bindparam_types($db, 43, 'VARBINARY(255)', str_repeat('a', 255)); + pdo_mysql_stmt_bindparam_types($db, 44, 'TINYBLOB', str_repeat('a', 255)); + pdo_mysql_stmt_bindparam_types($db, 45, 'BLOB', str_repeat('b', 300)); + pdo_mysql_stmt_bindparam_types($db, 46, 'MEDIUMBLOB', str_repeat('b', 300)); + pdo_mysql_stmt_bindparam_types($db, 47, 'LONGBLOB', str_repeat('b', 300)); + pdo_mysql_stmt_bindparam_types($db, 48, 'TINYTEXT', str_repeat('c', 255)); + pdo_mysql_stmt_bindparam_types($db, 49, 'TINYTEXT BINARY', str_repeat('c', 255)); + pdo_mysql_stmt_bindparam_types($db, 50, 'TEXT', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 51, 'TEXT BINARY', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 52, 'MEDIUMTEXT', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 53, 'MEDIUMTEXT BINARY', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 54, 'LONGTEXT', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 55, 'LONGTEXT BINARY', str_repeat('d', 300)); + pdo_mysql_stmt_bindparam_types($db, 56, "ENUM('yes', 'no') DEFAULT 'yes'", "no"); + pdo_mysql_stmt_bindparam_types($db, 57, "SET('yes', 'no') DEFAULT 'yes'", "no"); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt new file mode 100644 index 0000000000..6dc557d14f --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt @@ -0,0 +1,332 @@ +--TEST-- +MySQL PDOStatement->bindValue() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + printf("Binding variable...\n"); + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[003] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[004] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[005] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding value and not variable...\n"); + if (!$stmt->bindValue(1, 0)) + printf("[006] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[007] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[008] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding variable which references another variable...\n"); + $in = 0; + $in_ref = &$in; + if (!$stmt->bindValue(1, $in_ref)) + printf("[009] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[010] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[011] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + + printf("Binding a variable and a value...\n"); + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? AND id <= ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[012] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindValue(2, 2)) + printf("[013] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[014] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[015] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding a variable to two placeholders and changing the variable value in between the binds...\n"); + // variable value change shall have no impact + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? AND id <= ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[016] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $in = 2; + if (!$stmt->bindValue(2, $in)) + printf("[017] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[018] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[019] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + printf("Testing emulated PS...\n"); + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + printf("Binding variable...\n"); + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[003] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[004] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[005] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding value and not variable...\n"); + if (!$stmt->bindValue(1, 0)) + printf("[006] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[007] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[008] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding variable which references another variable...\n"); + $in = 0; + $in_ref = &$in; + if (!$stmt->bindValue(1, $in_ref)) + printf("[009] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[010] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[011] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + + printf("Binding a variable and a value...\n"); + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? AND id <= ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[012] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindValue(2, 2)) + printf("[013] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[014] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[015] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + printf("Binding a variable to two placeholders and changing the variable value in between the binds...\n"); + // variable value change shall have no impact + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? AND id <= ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindValue(1, $in)) + printf("[016] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $in = 2; + if (!$stmt->bindValue(2, $in)) + printf("[017] Cannot bind value, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[018] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[019] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Testing native PS... +Binding variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding value and not variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding variable which references another variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding a variable and a value... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding a variable to two placeholders and changing the variable value in between the binds... +in = 2 -> id = 1 (integer) / label = 'a' (string) +in = 2 -> id = 2 (integer) / label = 'b' (string) +Testing emulated PS... +Binding variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding value and not variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding variable which references another variable... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding a variable and a value... +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Binding a variable to two placeholders and changing the variable value in between the binds... +in = 2 -> id = 1 (integer) / label = 'a' (string) +in = 2 -> id = 2 (integer) / label = 'b' (string) +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt new file mode 100644 index 0000000000..88b847ce8c --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt @@ -0,0 +1,143 @@ +--TEST-- +MySQL PDOStatement - inserting BLOB from stream +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label BLOB) ENGINE=%s', PDO_MYSQL_TEST_ENGINE); + $db->exec($sql); + + if (!$stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)')) { + printf("[%03d + 4] %s\n", $offset, var_export($db->errorInfo(), true)); + return false; + } + + $fp = fopen($file, 'r'); + if (!$fp) { + printf("[%03d + 5] Cannot create test file '%s'\n", $offset, $file); + return false; + } + + + $id = 1; + $stmt->bindParam(1, $id); + if (true !== ($tmp = $stmt->bindParam(2, $fp, PDO::PARAM_LOB))) { + printf("[%03d + 6] Expecting true, got %s. %s\n", + $offset, + var_export($tmp, true), + var_export($db->errorInfo(), true)); + return false; + } + + if (true !== $stmt->execute()) { + printf("[%03d + 7] Failed to INSERT data, %s\n", $offset, var_export($stmt->errorInfo(), true)); + return false; + } + + $stmt2 = $db->query('SELECT id, label FROM test WHERE id = 1'); + $row = $stmt2->fetch(PDO::FETCH_ASSOC); + if ($row['label'] != $blob) { + printf("[%03d + 8] INSERT and/or SELECT has failed, dumping data.\n", $offset); + var_dump($row); + var_dump($blob); + return false; + } + + // Lets test the chr(0) handling in case the streaming has failed: + // is the bug about chr(0) or the streaming... + $db->exec('DELETE FROM test'); + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)'); + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $blob); + if (true !== $stmt->execute()) + printf("[%03d + 9] %s\n", $offset, var_export($stmt->errorInfo(), true)); + + $stmt2 = $db->query('SELECT id, label FROM test WHERE id = 1'); + $row = $stmt2->fetch(PDO::FETCH_ASSOC); + if ($row['label'] != $blob) { + printf("[%03d + 10] INSERT and/or SELECT has failed, dumping data.\n", $offset); + var_dump($row); + var_dump($blob); + return false; + } + + return true; + } + + $db = MySQLPDOTest::factory(); + $blob = 'I am a mighty BLOB!' . chr(0) . "I am a binary thingie!"; + $tmp = MySQLPDOTest::getTempDir(); + $file = $tmp . DIRECTORY_SEPARATOR . 'pdoblob.tst'; + + try { + + printf("Emulated PS...\n"); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + blob_from_stream(10, $db, $file, $blob); + + printf("Native PS...\n"); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + blob_from_stream(30, $db, $file, $blob); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + @unlink($file); + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Emulated PS... +Native PS... +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt new file mode 100644 index 0000000000..2914583f6b --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt @@ -0,0 +1,91 @@ +--TEST-- +MySQL Prepared Statements and BLOBs +--SKIPIF-- + +--FILE-- + 255, + 'TINYTEXT' => 255, + 'BLOB' => 32767, + 'TEXT' => 32767, + 'MEDIUMBLOB' => 100000, + 'MEDIUMTEXT' => 100000, + 'LONGBLOB' => 100000, + 'LONGTEXT' => 100000, + ); + + function test_blob($db, $offset, $sql_type, $test_len) { + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, PDO_MYSQL_TEST_ENGINE)); + + $value = str_repeat('a', $test_len); + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)'); + $stmt->bindValue(1, 1); + $stmt->bindValue(2, $value); + if (!$stmt->execute()) { + printf("[%03d + 1] %d %s\n", + $offset, $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + + $stmt = $db->query('SELECT id, label FROM test'); + $id = $label = NULL; + $stmt->bindColumn(1, $id, PDO::PARAM_INT); + $stmt->bindColumn(2, $label, PDO::PARAM_LOB); + + if (!$stmt->fetch(PDO::FETCH_BOUND)) { + printf("[%03d + 2] %d %s\n", + $offset, $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + + if ($label !== $value) { + printf("[%03d + 3] Returned value seems to be wrong (%d vs. %d charachters). Check manually\n", + $offset, strlen($label), strlen($value)); + return false; + } + + if (1 != $id) { + printf("[%03d + 3] Returned id column value seems wrong, expecting 1 got %s.\n", + $offset, var_export($id, true)); + return false; + } + + $stmt = $db->query('SELECT id, label FROM test'); + $ret = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($ret['label'] !== $value) { + printf("[%03d + 3] Returned value seems to be wrong (%d vs. %d charachters). Check manually\n", + $offset, strlen($ret['label']), strlen($value)); + return false; + } + + if (1 != $ret['id']) { + printf("[%03d + 3] Returned id column value seems wrong, expecting 1 got %s.\n", + $offset, var_export($ret['id'], true)); + return false; + } + + return true; + } + + $offset = 0; + foreach ($blobs as $sql_type => $test_len) { + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + test_blob($db, ++$offset, $sql_type, $test_len); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + test_blob($db, ++$offset, $sql_type, $test_len); + } + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt new file mode 100644 index 0000000000..c96da1f347 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt @@ -0,0 +1,172 @@ +--TEST-- +MySQL PDOStatement->closeCursor() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + $stmt1 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + // query() shall fail! + $stmt2 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + $stmt1->closeCursor(); + + // This is proper usage of closeCursor(). It shall prevent any further error messages. + if (MySQLPDOTest::isPDOMySQLnd()) { + $stmt1 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + } else { + // see pdo_mysql_stmt_unbuffered_2050.phpt for an explanation + unset($stmt1); + $stmt1 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + } + // fetch only the first rows and let closeCursor() clean up + $row1 = $stmt1->fetch(PDO::FETCH_ASSOC); + $stmt1->closeCursor(); + + $stmt2 = $db->prepare('UPDATE test SET label = ? WHERE id = ?'); + $stmt2->bindValue(1, "z"); + + $stmt2->bindValue(2, $row1['id']); + $stmt2->execute(); + $stmt2->closeCursor(); + + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + // check if changing the fetch mode from unbuffered to buffered will + // cause any harm to a statement created prior to the change + $stmt1->execute(); + $row2 = $stmt1->fetch(PDO::FETCH_ASSOC); + $stmt1->closeCursor(); + if (!isset($row2['label']) || ('z' !== $row2['label'])) + printf("Expecting array(id => 1, label => z) got %s\n", var_export($row2, true)); + unset($stmt1); + + $stmt1 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + // should work + $stmt2 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + $stmt1->closeCursor(); + + $stmt1 = $db->query('SELECT id, label FROM test ORDER BY id ASC'); + // fetch only the first rows and let closeCursor() clean up + $row3 = $stmt1->fetch(PDO::FETCH_ASSOC); + $stmt1->closeCursor(); + assert($row3 == $row2); + + $stmt2 = $db->prepare('UPDATE test SET label = ? WHERE id = ?'); + $stmt2->bindValue(1, "a"); + $stmt2->bindValue(2, $row1['id']); + $stmt2->execute(); + $stmt2->closeCursor(); + + $stmt1->execute(); + $row4 = $stmt1->fetch(PDO::FETCH_ASSOC); + $stmt1->closeCursor(); + assert($row4 == $row1); + + $offset = 0; + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindParam(1, $in)) + printf("[%03d + 1] Cannot bind parameter, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[%03d + 2] Cannot bind integer column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[%03d + 3] Cannot bind string column, %s %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + $stmt->closeCursor(); + $stmt->execute(); + + } + + + try { + + printf("Testing emulated PS...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + printf("Buffered...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + MySQLPDOTest::createTestTable($db); + pdo_mysql_stmt_closecursor($db); + + printf("Unbuffered...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + MySQLPDOTest::createTestTable($db); + pdo_mysql_stmt_closecursor($db); + + printf("Testing native PS...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + printf("Buffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + pdo_mysql_stmt_closecursor($db); + + printf("Unbuffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + pdo_mysql_stmt_closecursor($db); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Testing emulated PS... +Buffered... + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Unbuffered... + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Testing native PS... +Buffered... + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +Unbuffered... + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in %s on line %d +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt new file mode 100644 index 0000000000..a575014c72 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt @@ -0,0 +1,69 @@ +--TEST-- +MySQL PDOStatement->closeCursor() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + MySQLPDOTest::createTestTable($db); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id > ? ORDER BY id ASC LIMIT 2'); + $in = 0; + if (!$stmt->bindParam(1, $in)) + printf("[003] Cannot bind parameter, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + $stmt->execute(); + $id = $label = null; + + if (!$stmt->bindColumn(1, $id, PDO::PARAM_INT)) + printf("[004] Cannot bind integer column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + if (!$stmt->bindColumn(2, $label, PDO::PARAM_STR)) + printf("[005] Cannot bind string column, %s %s\n", + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + $stmt->closeCursor(); + $stmt->execute(); + while ($stmt->fetch(PDO::FETCH_BOUND)) + printf("in = %d -> id = %s (%s) / label = %s (%s)\n", + $in, + var_export($id, true), gettype($id), + var_export($label, true), gettype($label)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +in = 0 -> id = 1 (integer) / label = 'a' (string) +in = 0 -> id = 2 (integer) / label = 'b' (string) +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt new file mode 100644 index 0000000000..508ba0a3ef --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt @@ -0,0 +1,65 @@ +--TEST-- +MySQL PDOStatement->columnCount() +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id, label, "?" as foo FROM test'); + $stmt->execute(); + var_dump($stmt->columnCount()); + + $stmt = $db->query('SELECT * FROM test'); + var_dump($stmt->columnCount()); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + printf("Testing native PS...\n"); + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id, label, "?" as foo, "TODO - Stored Procedure" as bar FROM test'); + $stmt->execute(); + var_dump($stmt->columnCount()); + + $stmt = $db->query('SELECT * FROM test'); + var_dump($stmt->columnCount()); + + } catch (PDOException $e) { + printf("[003] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Testing emulated PS... +int(3) +int(2) +Testing native PS... +int(4) +int(2) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt new file mode 100644 index 0000000000..877200631c --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt @@ -0,0 +1,61 @@ +--TEST-- +MySQL PDOStatement->errorCode(); +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS ihopeitdoesnotexist'); + + printf("Testing emulated PS...\n"); + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id FROM ihopeitdoesnotexist ORDER BY id ASC'); + $stmt->execute(); + var_dump($stmt->errorCode()); + + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + printf("Testing native PS...\n"); + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id FROM ihopeitdoesnotexist ORDER BY id ASC'); + $stmt->execute(); + var_dump($stmt->errorCode()); + + + + } catch (PDOException $e) { + printf("[003] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + print "done!"; +?> +--EXPECTF-- +Testing emulated PS... + +Warning: PDOStatement::execute(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.ihopeitdoesnotexist' doesn't exist in %s on line %d +string(5) "42S02" +Testing native PS... + +Warning: PDO::prepare(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.ihopeitdoesnotexist' doesn't exist in %s on line %d + +Fatal error: Call to a member function execute() on a non-object in %s on line %d \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt new file mode 100644 index 0000000000..c29ffff157 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt @@ -0,0 +1,119 @@ +--TEST-- +MySQL PDOStatement->errorInfo(); +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id FROM ihopeitdoesnotexist ORDER BY id ASC'); + var_dump($stmt->errorInfo()); + $stmt->execute(); + var_dump($stmt->errorInfo()); + + MySQLPDOTest::createTestTable($db); + $stmt = $db->prepare('SELECT label FROM test ORDER BY id ASC LIMIT 1'); + $db->exec('DROP TABLE test'); + var_dump($stmt->execute()); + var_dump($stmt->errorInfo()); + var_dump($db->errorInfo()); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); + } + + printf("Testing native PS...\n"); + try { + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id FROM ihopeitdoesnotexist ORDER BY id ASC'); + var_dump($stmt); + + MySQLPDOTest::createTestTable($db); + $stmt = $db->prepare('SELECT label FROM test ORDER BY id ASC LIMIT 1'); + var_dump($stmt->errorInfo()); + $db->exec('DROP TABLE test'); + $stmt->execute(); + var_dump($stmt->errorInfo()); + var_dump($db->errorInfo()); + + } catch (PDOException $e) { + printf("[003] %s [%s] %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); + } + print "done!"; +?> +--EXPECTF-- +Testing emulated PS... +array(1) { + [0]=> + string(0) "" +} + +Warning: PDOStatement::execute(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.ihopeitdoesnotexist' doesn't exist in %s on line %d +array(3) { + [0]=> + string(5) "42S02" + [1]=> + int(1146) + [2]=> + string(%d) "Table '%s.ihopeitdoesnotexist' doesn't exist" +} + +Warning: PDOStatement::execute(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.test' doesn't exist in %s on line %d +bool(false) +array(3) { + [0]=> + string(5) "42S02" + [1]=> + int(1146) + [2]=> + string(%d) "Table '%s.test' doesn't exist" +} +array(1) { + [0]=> + string(5) "00000" +} +Testing native PS... + +Warning: PDO::prepare(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.ihopeitdoesnotexist' doesn't exist in %s on line %d +bool(false) +array(1) { + [0]=> + string(0) "" +} + +Warning: PDOStatement::execute(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.test' doesn't exist in %s on line %d +array(3) { + [0]=> + string(5) "42S02" + [1]=> + int(1146) + [2]=> + string(%d) "Table '%s.test' doesn't exist" +} +array(3) { + [0]=> + string(5) "00000" + [1]=> + int(1146) + [2]=> + string(%d) "Table '%s.ihopeitdoesnotexist' doesn't exist" +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt new file mode 100644 index 0000000000..fdf63ea14a --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt @@ -0,0 +1,187 @@ +--TEST-- +MySQL PDOStatement->execute()/fetch(), Non-SELECT +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 1); + if (1 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn on emulated prepared statements\n"); + + if (!is_object($stmt = $db->query('DESCRIBE test id'))) + printf("[003] Emulated PS, DESCRIBE failed, %s\n", var_export($db->errorInfo(), true)); + + $describe = array(); + $valid = false; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $describe[] = $row; + foreach ($row as $column => $value) + if (isset($row['field']) && ($row['field'] == 'id')) + $valid = true; + } + if (empty($describe)) + printf("[004] Emulated PS, DESCRIBE returned no results\n"); + else if (!$valid) + printf("[005] Emulated PS, DESCRIBE, returned data seems wrong, dumping %s\n", + var_export($describe, true)); + + if (!is_object($stmt = $db->query('SHOW ENGINES'))) + printf("[006] Emulated PS, SHOW failed, %s\n", var_export($db->errorInfo(), true)); + + $show = array(); + $valid = false; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $show[] = $row; + foreach ($row as $column => $value) + // MyISAM engine should be part of _every_ MySQL today + if ($value == 'MyISAM') + $valid = true; + } + if (empty($show)) + printf("[007] Emulated PS, SHOW returned no results\n"); + else if (!$valid) + printf("[008] Emulated PS, SHOW data seems wrong, dumping %s\n", + var_export($show, true)); + + if (!is_object($stmt = $db->query("EXPLAIN SELECT id FROM test"))) + printf("[009] Emulated PS, EXPLAIN returned no results\n"); + + $explain = array(); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) + $explain[] = $row; + + if (empty($explain)) + printf("[010] Emulated PS, EXPLAIN returned no results\n"); + + // And now native PS + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[011] Unable to turn off emulated prepared statements\n"); + + $native_support = 'no'; + if ($db->exec('PREPARE mystmt FROM "DESCRIBE test id"')) { + $native_support = 'yes'; + $db->exec('DEALLOCATE PREPARE mystmt'); + } + + if (!is_object($stmt = $db->query('DESCRIBE test id'))) + printf("[012] Native PS (native support: %s), DESCRIBE failed, %s\n", + $native_support, + var_export($db->errorInfo(), true)); + + $describe_native = array(); + $valid = false; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $describe_native[] = $row; + foreach ($row as $column => $value) + if (isset($row['field']) && ($row['field'] == 'id')) + $valid = true; + } + if (empty($describe_native)) + printf("[013] Native PS (native support: %s), DESCRIBE returned no results\n", + $native_support); + else if (!$valid) + printf("[014] Native PS (native support: %s), DESCRIBE, returned data seems wrong, dumping %s\n", + $native_support, + var_export($describe_native, true)); + + if ($describe != $describe_native) + printf("[015] Emulated and native PS (native support: %s) results of DESCRIBE differ: %s vs. %s\n", + $native_support, + var_export($describe, true), + var_export($describe_native, true)); + + + $native_support = 'no'; + if ($db->exec('PREPARE mystmt FROM "SHOW ENGINES"')) { + $native_support = 'yes'; + $db->exec('DEALLOCATE PREPARE mystmt'); + } + + if (!is_object($stmt = $db->query('SHOW ENGINES'))) + printf("[016] Native PS (native support: %s), SHOW failed, %s\n", + $native_support, + var_export($db->errorInfo(), true)); + + $show_native = array(); + $valid = false; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $show_native[] = $row; + foreach ($row as $column => $value) + // MyISAM engine should be part of _every_ MySQL today + if ($value == 'MyISAM') + $valid = true; + } + if (empty($show_native)) + printf("[017] Native PS (native support: %s), SHOW returned no results\n", + $native_support); + else if (!$valid) + printf("[018] Native PS (native support: %s), SHOW data seems wrong, dumping %s\n", + var_export($show_native, true)); + + if ($show != $show_native) + printf("Native PS (native support: %s) and emulated PS returned different data for SHOW: %s vs. %s\n", + $native_support, + var_export($show, true), + var_export($show_native, true)); + + $native_support = 'no'; + if ($db->exec('PREPARE mystmt FROM "EXPLAIN SELECT id FROM test"')) { + $native_support = 'yes'; + $db->exec('DEALLOCATE PREPARE mystmt'); + } + + if (!is_object($stmt = $db->query("EXPLAIN SELECT id FROM test"))) + printf("[012] Native PS (native support: %s), EXPLAIN failed, %s\n", + $native_support, + var_export($db->errorInfo(), true)); + + $explain_native = array(); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) + $explain_native[] = $row; + + if (empty($explain_native)) + printf("[013] Native PS (native support: %s), EXPLAIN returned no results\n", + $native_support); + + if ($explain != $explain_native) + printf("Native PS (native support: %s) and emulated PS returned different data for EXPLAIN: %s vs. %s\n", + $native_support, + var_export($explain, true), + var_export($explain_native, true)); + + $stmt->execute(); + $explain_native = $stmt->fetchAll(PDO::FETCH_ASSOC); + if ($explain != $explain_native) + printf("Native PS (native support: %s) and emulated PS returned different data for EXPLAIN: %s vs. %s\n", + $native_support, + var_export($explain, true), + var_export($explain_native, true)); + + $stmt->execute(); + $stmt->execute(); + // libmysql needs this - otherwise we get a 2015 error + if (!MYSQLPDOTest::isPDOMySQLnd()) + $stmt->fetchAll(PDO::FETCH_ASSOC); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!\n"; +?> +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt new file mode 100644 index 0000000000..73ed5a2eab --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt @@ -0,0 +1,151 @@ +--TEST-- +MySQL PDOStatement->fetch(), PDO::FETCH_SERIALIZE +--SKIPIF-- + +--FILE-- +{$prop} = $value; + } + + public function __get($prop) { + printf("%s(%s)\n", __METHOD__, $prop); + return NULL; + } + + // Singleton + public static function singleton($caller) { + printf("%s(%s)\n", __METHOD__, $caller); + + if (!self::$instance) { + $c = __CLASS__; + self::$instance = new $c($caller); + } + return self::$instance; + } + + // Serializable + public function serialize() { + printf("%s()\n", __METHOD__); + return 'Data from serialize'; + } + + public function unserialize($data) { + printf("%s(%s)\n", __METHOD__, var_export($data, true)); + } + + } + + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[002] Unable to turn off emulated prepared statements\n"); + + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(id INT, myobj BLOB) ENGINE=%s', + MySQLPDOTest::getTableEngine())); + + printf("Creating an object, serializing it and writing it to DB...\n"); + $id = 1; + $obj = myclass::singleton('Creating object'); + $myobj = serialize($obj); + $stmt = $db->prepare('INSERT INTO test(id, myobj) VALUES (?, ?)'); + $stmt->bindValue(1, $id); + $stmt->bindValue(2, $myobj); + $stmt->execute(); + + printf("\nUnserializing the previously serialized object...\n"); + var_dump(unserialize($myobj)); + + printf("\nUsing PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE to fetch the object from DB and unserialize it...\n"); + $stmt = $db->prepare('SELECT myobj FROM test'); + $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'myclass', array('PDO shall not call __construct()')); + $stmt->execute(); + var_dump($stmt->fetch()); + + printf("\nUsing PDO::FETCH_CLASS to fetch the object from DB and unserialize it...\n"); + $stmt = $db->prepare('SELECT myobj FROM test'); + $stmt->setFetchMode(PDO::FETCH_CLASS, 'myclass', array('PDO shall call __construct()')); + $stmt->execute(); + var_dump($stmt->fetch()); + + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!\n"; +?> +--EXPECTF-- +Creating an object, serializing it and writing it to DB... +myclass::singleton(Creating object) +myclass::__construct(Creating object) +myclass::serialize() + +Unserializing the previously serialized object... +myclass::unserialize('Data from serialize') +object(myclass)#4 (1) { + ["myprotected":protected]=> + string(19) "a protected propery" +} + +Using PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE to fetch the object from DB and unserialize it... +myclass::__set(myobj, 'C:7:"myclass":19:{Data from serialize}') +myclass::__construct(PDO shall not call __construct()) +object(myclass)#%d (2) { + ["myprotected":protected]=> + string(19) "a protected propery" + ["myobj"]=> + string(38) "C:7:"myclass":19:{Data from serialize}" +} + +Using PDO::FETCH_CLASS to fetch the object from DB and unserialize it... +myclass::__set(myobj, 'C:7:"myclass":19:{Data from serialize}') +myclass::__construct(PDO shall call __construct()) +object(myclass)#%d (2) { + ["myprotected":protected]=> + string(19) "a protected propery" + ["myobj"]=> + string(38) "C:7:"myclass":19:{Data from serialize}" +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt new file mode 100644 index 0000000000..4233d911e9 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt @@ -0,0 +1,98 @@ +--TEST-- +MySQL PDOStatement->fetch(), PDO::FETCH_SERIALIZE +--SKIPIF-- + +--FILE-- +{$prop} = $value; + } + + public function serialize() { + printf("%s()\n", __METHOD__); + return 'Value from serialize()'; + } + + public function unserialize($data) { + printf("%s(%s)\n", __METHOD__, var_export($data, true)); + } + + } + + printf("Lets see what the Serializeable interface makes our object behave like...\n"); + $obj = new myclass('Called by script'); + $tmp = unserialize(serialize($obj)); + var_dump($tmp); + + printf("\nAnd now magic PDO using fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE)...\n"); + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec(sprintf('CREATE TABLE test(myobj BLOB) ENGINE=%s', MySQLPDOTest::getTableEngine())); + $db->exec('INSERT INTO test(myobj) VALUES ("Data fetched from DB to be given to unserialize()")'); + + $stmt = $db->prepare('SELECT myobj FROM test'); + $stmt->execute(); + $rows = $stmt->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'myclass', array('Called by PDO')); + var_dump($rows[0]); + + $stmt->execute(); + $rows = $stmt->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'myclass'); + var_dump($rows[0]); + + printf("\nAnd now PDO using setFetchMode(PDO::FETCH:CLASS|PDO::FETCH_SERIALIZE) + fetch()...\n"); + $stmt = $db->prepare('SELECT myobj FROM test'); + $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'myclass', array('Called by PDO')); + $stmt->execute(); + var_dump($stmt->fetch()); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!\n"; +?> +--EXPECTF-- +Lets see what the Serializeable interface makes our object behave like... +myclass::__construct('Called by script') - note that it must not be called when unserializing +myclass::serialize() +myclass::unserialize('Value from serialize()') +object(myclass)#%d (0) { +} + +And now magic PDO using fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE)... +myclass::unserialize('Data fetched from DB to be given to unserialize()') +myclass::__construct('Called by PDO') - note that it must not be called when unserializing +object(myclass)#%d (0) { +} +myclass::unserialize('Data fetched from DB to be given to unserialize()') +myclass::__construct(NULL) - note that it must not be called when unserializing +object(myclass)#%d (0) { +} + +And now PDO using setFetchMode(PDO::FETCH:CLASS|PDO::FETCH_SERIALIZE) + fetch()... +myclass::__set('myobj', 'Data fetched from DB to be given to unserialize()') +myclass::__construct('Called by PDO') - note that it must not be called when unserializing +object(myclass)#%d (1) { + ["myobj"]=> + string(49) "Data fetched from DB to be given to unserialize()" +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt new file mode 100644 index 0000000000..79ef0c34a2 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt @@ -0,0 +1,95 @@ +--TEST-- +MySQL PDO: PDOStatement->fetchObject() +--SKIPIF-- + +--FILE-- +prepare($query); + + class myclass { + + private $set_calls = 0; + protected static $static_set_calls = 0; + + // NOTE: PDO does not care about protected + protected $grp; + + // NOTE: PDO does not care about private and calls __construct() after __set() + private function __construct($param1, $param2) { + printf("myclass::__construct(%s, %s): %d / %d\n", + $param1, $param2, + self::$static_set_calls, $this->set_calls); + } + + // NOTE: PDO will call __set() prior to calling __construct() + public function __set($prop, $value) { + $this->not_a_magic_one(); + printf("myclass::__set(%s, -%s-) %d\n", + $prop, var_export($value, true), $this->set_calls, self::$static_set_calls); + $this->{$prop} = $value; + } + + // NOTE: PDO can call regular methods prior to calling __construct() + public function not_a_magic_one() { + $this->set_calls++; + self::$static_set_calls++; + } + + } + $stmt->execute(); + $rowno = 0; + $rows[] = array(); + while (is_object($rows[] = $stmt->fetchObject('myclass', array($rowno++, $rowno)))) + ; + + var_dump($rows[$rowno - 1]); + +} catch (PDOException $e) { + // we should never get here, we use warnings, but never trust a system... + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); +} + +$db->exec('DROP TABLE IF EXISTS test'); +print "done!"; +?> +--EXPECTF-- +myclass::__set(id, -'1'-) 1 +myclass::__set(, -''-) 2 +myclass::__set(null, -NULL-) 3 +myclass::__set(, -''-) 4 +myclass::__construct(0, 1): 4 / 4 +myclass::__set(id, -'2'-) 1 +myclass::__set(, -''-) 2 +myclass::__set(null, -NULL-) 3 +myclass::__set(, -''-) 4 +myclass::__construct(1, 2): 8 / 4 +myclass::__set(id, -'3'-) 1 +myclass::__set(, -''-) 2 +myclass::__set(null, -NULL-) 3 +myclass::__set(, -''-) 4 +myclass::__construct(2, 3): 12 / 4 +object(myclass)#%d (4) { + ["set_calls":"myclass":private]=> + int(4) + ["grp":protected]=> + NULL + ["id"]=> + string(1) "3" + ["null"]=> + NULL +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt new file mode 100644 index 0000000000..19ae1951dc --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt @@ -0,0 +1,309 @@ +--TEST-- +MySQL: PDOStatement->getColumnMeta() +--SKIPIF-- +query('SELECT VERSION() as _version'); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +$version = ((int)substr($row['_version'], 0, 1) * 10) + (int)substr($row['_version'], 2, 1); +if ($version < 51) + die("skip Test needs MySQL 5.1+"); +?> +--FILE-- +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); +MySQLPDOTest::createTestTable($db); + +try { + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + + // execute() has not been called yet + // NOTE: no warning + if (false !== ($tmp = $stmt->getColumnMeta(0))) + printf("[002] Expecting false got %s\n", var_export($tmp, true)); + + $stmt->execute(); + // Warning: PDOStatement::getColumnMeta() expects exactly 1 parameter, 0 given in + if (false !== ($tmp = @$stmt->getColumnMeta())) + printf("[003] Expecting false got %s\n", var_export($tmp, true)); + + // invalid offset + if (false !== ($tmp = @$stmt->getColumnMeta(-1))) + printf("[004] Expecting false got %s\n", var_export($tmp, true)); + + // Warning: PDOStatement::getColumnMeta() expects parameter 1 to be long, array given in + if (false !== ($tmp = @$stmt->getColumnMeta(array()))) + printf("[005] Expecting false got %s\n", var_export($tmp, true)); + + // Warning: PDOStatement::getColumnMeta() expects exactly 1 parameter, 2 given in + if (false !== ($tmp = @$stmt->getColumnMeta(1, 1))) + printf("[006] Expecting false got %s\n", var_export($tmp, true)); + + $emulated = $stmt->getColumnMeta(0); + + printf("Testing native PS...\n"); + $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[007] Unable to turn off emulated prepared statements\n"); + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + $stmt->execute(); + $native = $stmt->getColumnMeta(0); + if (count($native) == 0) { + printf("[008] Meta data seems wrong, %s / %s\n", + var_export($native, true), var_export($emulated, true)); + } + + // invalid offset + if (false !== ($tmp = $stmt->getColumnMeta(1))) + printf("[009] Expecting false because of invalid offset got %s\n", var_export($tmp, true)); + + + function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) { + + $db->exec('DROP TABLE IF EXISTS test'); + + $sql = sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine()); + if (!($stmt = @$db->prepare($sql)) || (!@$stmt->execute())) { + // Some engines and/or MySQL server versions might not support the data type + return true; + } + + if (!$db->exec(sprintf('INSERT INTO test(id, label) VALUES (1, "%s")', $value))) { + printf("[%03d] + 1] Insert failed, %d - %s\n", $offset, + $db->errorCode(), var_export($db->errorInfo(), true)); + return false; + } + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + $meta = $stmt->getColumnMeta(1); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (empty($meta)) { + printf("[%03d + 2] getColumnMeta() failed, %d - %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + + $elements = array('flags', 'table', 'name', 'len', 'precision', 'pdo_type'); + foreach ($elements as $k => $element) + if (!isset($meta[$element])) { + printf("[%03d + 3] Element %s missing, %s\n", $offset, + $element, var_export($meta, true)); + return false; + } + + if (($meta['table'] != 'test') || ($meta['name'] != 'label')) { + printf("[%03d + 4] Table or field name is wrong, %s\n", $offset, + var_export($meta, true)); + return false; + } + + if (!is_null($native_type)) { + if (!isset($meta['native_type'])) { + printf("[%03d + 5] Element native_type missing, %s\n", $offset, + var_export($meta, true)); + return false; + } + + if (!is_array($native_type)) + $native_type = array($native_type); + + $found = false; + foreach ($native_type as $k => $type) { + if ($meta['native_type'] == $type) { + $found = true; + break; + } + } + + if (!$found) { + printf("[%03d + 6] Expecting native type %s, %s\n", $offset, + var_export($native_type, true), var_export($meta, true)); + return false; + } + } + + if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) { + printf("[%03d + 6] Expecting PDO type %s got %s (%s)\n", $offset, + $pdo_type, var_export($meta, true), var_export($meta['native_type'])); + return false; + } + + return true; + } + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + $is_mysqlnd = MySQLPDOTest::isPDOMySQLnd(); + test_meta($db, 20, 'BIT(8)', 1, NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 30, 'TINYINT', -127, NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 40, 'TINYINT UNSIGNED', 255, NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 50, 'BOOLEAN', 1, NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + + test_meta($db, 60, 'SMALLINT', -32768, 'SHORT', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 70, 'SMALLINT UNSIGNED', 65535, 'SHORT', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + + test_meta($db, 80, 'MEDIUMINT', -8388608, 'INT24', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 90, 'MEDIUMINT UNSIGNED', 16777215, 'INT24', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + + test_meta($db, 100, 'INT', -2147483648, 'LONG', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 110, 'INT UNSIGNED', 4294967295, 'LONG', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + + test_meta($db, 120, 'BIGINT', -9223372036854775808, 'LONGLONG', ($is_mysqlnd) ? ((PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT) : PDO::PARAM_STR); + test_meta($db, 130, 'BIGINT UNSIGNED', 18446744073709551615, 'LONGLONG', ($is_mysqlnd) ? ((PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT) : PDO::PARAM_STR); + + test_meta($db, 130, 'REAL', -1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 140, 'REAL UNSIGNED', 1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 150, 'REAL ZEROFILL', -1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 160, 'REAL UNSIGNED ZEROFILL', 1.01, 'DOUBLE', PDO::PARAM_STR); + + test_meta($db, 170, 'DOUBLE', -1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 180, 'DOUBLE UNSIGNED', 1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 190, 'DOUBLE ZEROFILL', -1.01, 'DOUBLE', PDO::PARAM_STR); + test_meta($db, 200, 'DOUBLE UNSIGNED ZEROFILL', 1.01, 'DOUBLE', PDO::PARAM_STR); + + test_meta($db, 210, 'FLOAT', -1.01, 'FLOAT', PDO::PARAM_STR); + test_meta($db, 220, 'FLOAT UNSIGNED', 1.01, 'FLOAT', PDO::PARAM_STR); + test_meta($db, 230, 'FLOAT ZEROFILL', -1.01, 'FLOAT', PDO::PARAM_STR); + test_meta($db, 240, 'FLOAT UNSIGNED ZEROFILL', 1.01, 'FLOAT', PDO::PARAM_STR); + + test_meta($db, 250, 'DECIMAL', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 260, 'DECIMAL UNSIGNED', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 270, 'DECIMAL ZEROFILL', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 280, 'DECIMAL UNSIGNED ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + + test_meta($db, 290, 'NUMERIC', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 300, 'NUMERIC UNSIGNED', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 310, 'NUMERIC ZEROFILL', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + test_meta($db, 320, 'NUMERIC UNSIGNED ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR); + + test_meta($db, 330, 'DATE', '2008-04-23', array('DATE', 'NEWDATE'), PDO::PARAM_STR); + test_meta($db, 340, 'TIME', '14:37:00', 'TIME', PDO::PARAM_STR); + test_meta($db, 350, 'TIMESTAMP', time(), 'TIMESTAMP', PDO::PARAM_STR); + test_meta($db, 360, 'DATETIME', '2008-03-23 14:38:00', 'DATETIME', PDO::PARAM_STR); + test_meta($db, 370, 'YEAR', '2008', NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + + test_meta($db, 380, 'CHAR(1)', 'a', 'STRING', PDO::PARAM_STR); + test_meta($db, 390, 'CHAR(10)', '0123456789', 'STRING', PDO::PARAM_STR); + test_meta($db, 400, 'CHAR(255)', str_repeat('z', 255), 'STRING', PDO::PARAM_STR); + test_meta($db, 410, 'VARCHAR(1)', 'a', 'VAR_STRING', PDO::PARAM_STR); + test_meta($db, 420, 'VARCHAR(10)', '0123456789', 'VAR_STRING', PDO::PARAM_STR); + test_meta($db, 430, 'VARCHAR(255)', str_repeat('z', 255), 'VAR_STRING', PDO::PARAM_STR); + + test_meta($db, 440, 'BINARY(1)', str_repeat('a', 1), 'STRING', PDO::PARAM_STR); + test_meta($db, 450, 'BINARY(255)', str_repeat('b', 255), 'STRING', PDO::PARAM_STR); + test_meta($db, 460, 'VARBINARY(1)', str_repeat('a', 1), 'VAR_STRING', PDO::PARAM_STR); + test_meta($db, 470, 'VARBINARY(255)', str_repeat('b', 255), 'VAR_STRING', PDO::PARAM_STR); + + test_meta($db, 480, 'TINYBLOB', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR); + test_meta($db, 490, 'BLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + test_meta($db, 500, 'MEDIUMBLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + test_meta($db, 510, 'LONGBLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + + test_meta($db, 520, 'TINYTEXT', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR); + test_meta($db, 530, 'TINYTEXT BINARY', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR); + + test_meta($db, 560, 'TEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + test_meta($db, 570, 'TEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + + test_meta($db, 580, 'MEDIUMTEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + test_meta($db, 590, 'MEDIUMTEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + + test_meta($db, 600, 'LONGTEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + test_meta($db, 610, 'LONGTEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR); + + test_meta($db, 620, "ENUM('yes', 'no') DEFAULT 'yes'", 'no', NULL, PDO::PARAM_STR); + test_meta($db, 630, "SET('yes', 'no') DEFAULT 'yes'", 'no', NULL, PDO::PARAM_STR); + +/* + | spatial_type +*/ + + // unique key + $db->exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label INT UNIQUE) ENGINE = %s', MySQLPDOTest::getTableEngine()); + if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) { + $db->exec('INSERT INTO test(id, label) VALUES (1, 2)'); + $stmt = $db->query('SELECT id, label FROM test'); + $meta = $stmt->getColumnMeta(1); + if (!isset($meta['flags'])) { + printf("[1000] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'unique_key') + $found = true; + } + if (!$found) + printf("[1001] Flags seem wrong %s\n", var_export($meta, true)); + } + } + + // primary key + $db->exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT PRIMARY KEY NOT NULL AUTO_INCREMENT) ENGINE = %s', MySQLPDOTest::getTableEngine()); + if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) { + $db->exec('INSERT INTO test(id) VALUES (1)'); + $stmt = $db->query('SELECT id FROM test'); + $meta = $stmt->getColumnMeta(0); + if (!isset($meta['flags'])) { + printf("[1002] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'primary_key') + $found = true; + } + if (!$found) + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + } + + // multiple key + $db->exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label1 INT, label2 INT, INDEX idx1(label1, label2)) ENGINE = %s', MySQLPDOTest::getTableEngine()); + if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) { + $db->exec('INSERT INTO test(id, label1, label2) VALUES (1, 2, 3)'); + $stmt = $db->query('SELECT id, label1, label2 FROM test'); + $meta = $stmt->getColumnMeta(1); + if (!isset($meta['flags'])) { + printf("[1004] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'multiple_key') + $found = true; + } + if (!$found) + printf("[1005] Flags seem wrong %s\n", var_export($meta, true)); + } + } + + $stmt = $db->query('SELECT NULL AS col1'); + $meta = $stmt->getColumnMeta(0); + if ('NULL' !== $meta['native_type']) + printf("[1006] Expecting NULL got %s\n", $meta['native_type']); + +} catch (PDOException $e) { + // we should never get here, we use warnings, but never trust a system... + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); +} + +$db->exec('DROP TABLE IF EXISTS test'); +print "done!"; +?> +--EXPECTF-- +Testing native PS... +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt new file mode 100644 index 0000000000..dbf60d527a --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt @@ -0,0 +1,90 @@ +--TEST-- +PDOStatements and multi query +--SKIPIF-- + +--FILE-- +query('SELECT label FROM test ORDER BY id ASC LIMIT 1; SELECT label FROM test ORDER BY id ASC LIMIT 1'); + var_dump($stmt->errorInfo()); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + var_dump($stmt->errorInfo()); + + } + + function mysql_stmt_multiquery_proper_usage($db) { + + $stmt = $db->query('SELECT label FROM test ORDER BY id ASC LIMIT 1; SELECT label FROM test ORDER BY id ASC LIMIT 1'); + do { + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } while ($stmt->nextRowset()); + + } + + try { + + printf("Emulated Prepared Statements...\n"); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + mysql_stmt_multiquery_wrong_usage($db); + mysql_stmt_multiquery_proper_usage($db); + + printf("Native Prepared Statements...\n"); + $db = MySQLPDOTest::factory(); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + mysql_stmt_multiquery_wrong_usage($db); + mysql_stmt_multiquery_proper_usage($db); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Emulated Prepared Statements... +array(1) { + [0]=> + string(5) "00000" +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + string(5) "00000" +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(1) { + ["label"]=> + string(1) "a" + } +} +Native Prepared Statements... + +Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '; SELECT label FROM test ORDER BY id ASC LIMIT 1' at line %d in %s on line %d + +Fatal error: Call to a member function errorInfo() on a non-object in %s on line %d \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt new file mode 100644 index 0000000000..f491536abc --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt @@ -0,0 +1,309 @@ +--TEST-- +MySQL PDOStatement->nextRowSet() +--SKIPIF-- +query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 50000) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); + +if (!MySQLPDOTest::isPDOMySQLnd()) + die("skip This will not work with libmysql"); +?> +--FILE-- +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + MySQLPDOTest::createTestTable($db); + + $stmt = $db->query('SELECT id FROM test'); + if (false !== ($tmp = $stmt->nextRowSet())) + printf("[002] Expecting false got %s\n", var_export($tmp, true)); + + // TODO: should give a warning, but its PDO, let's ignore the missing warning for now + if (false !== ($tmp = $stmt->nextRowSet(1))) + printf("[003] Expecting false got %s\n", var_export($tmp, true)); + + function test_proc1($db) { + + $stmt = $db->query('SELECT @VERSION as _version'); + $tmp = $stmt->fetch(PDO::FETCH_ASSOC); + assert($tmp['_version'] === NULL); + while ($stmt->fetch()) ; + + $db->exec('DROP PROCEDURE IF EXISTS p'); + $db->exec('CREATE PROCEDURE p(OUT ver_param VARCHAR(25)) BEGIN SELECT VERSION() INTO ver_param; END;'); + $db->exec('CALL p(@VERSION)'); + $stmt = $db->query('SELECT @VERSION as _version'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + var_dump($stmt->nextRowSet()); + + } + + function test_proc2($db) { + + $db->exec('DROP PROCEDURE IF EXISTS p'); + $db->exec('CREATE PROCEDURE p() BEGIN SELECT id FROM test ORDER BY id ASC LIMIT 3; SELECT id, label FROM test WHERE id < 4 ORDER BY id DESC LIMIT 3; END;'); + $stmt = $db->query('CALL p()'); + do { + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } while ($stmt->nextRowSet()); + var_dump($stmt->nextRowSet()); + + } + + try { + + // Emulated PS + printf("Emulated PS...\n"); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); + test_proc1($db); + test_proc2($db); + + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 0); + test_proc1($db); + test_proc2($db); + + // Native PS + printf("Native PS...\n"); + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + test_proc1($db); + test_proc2($db); + + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 0); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + + test_proc1($db); + test_proc2($db); + + @$db->exec('DROP PROCEDURE IF EXISTS p'); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Emulated PS... +array(1) { + [0]=> + array(1) { + ["_version"]=> + string(%d) "%s" + } +} +bool(false) +array(3) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } + [1]=> + array(1) { + ["id"]=> + string(1) "2" + } + [2]=> + array(1) { + ["id"]=> + string(1) "3" + } +} +array(3) { + [0]=> + array(2) { + ["id"]=> + string(1) "3" + ["label"]=> + string(1) "c" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(1) "b" + } + [2]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +bool(false) +array(1) { + [0]=> + array(1) { + ["_version"]=> + string(%d) "%s" + } +} +bool(false) +array(3) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } + [1]=> + array(1) { + ["id"]=> + string(1) "2" + } + [2]=> + array(1) { + ["id"]=> + string(1) "3" + } +} +array(3) { + [0]=> + array(2) { + ["id"]=> + string(1) "3" + ["label"]=> + string(1) "c" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(1) "b" + } + [2]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +bool(false) +Native PS... +array(1) { + [0]=> + array(1) { + ["_version"]=> + string(%d) "%s" + } +} +bool(false) +array(3) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } + [1]=> + array(1) { + ["id"]=> + string(1) "2" + } + [2]=> + array(1) { + ["id"]=> + string(1) "3" + } +} +array(3) { + [0]=> + array(2) { + ["id"]=> + string(1) "3" + ["label"]=> + string(1) "c" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(1) "b" + } + [2]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +bool(false) +array(1) { + [0]=> + array(1) { + ["_version"]=> + string(%d) "%s" + } +} +bool(false) +array(3) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } + [1]=> + array(1) { + ["id"]=> + string(1) "2" + } + [2]=> + array(1) { + ["id"]=> + string(1) "3" + } +} +array(3) { + [0]=> + array(2) { + ["id"]=> + string(1) "3" + ["label"]=> + string(1) "c" + } + [1]=> + array(2) { + ["id"]=> + string(1) "2" + ["label"]=> + string(1) "b" + } + [2]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +bool(false) +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt new file mode 100644 index 0000000000..8883b8496b --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt @@ -0,0 +1,32 @@ +--TEST-- +MySQL PDOStatement->rowCount() @ SELECT +--SKIPIF-- + +--FILE-- +query('SELECT id FROM test WHERE 1 = 0')->rowCount())) + printf("[002] Expecting 0 got %s", var_export($tmp, true)); + + if (1 !== ($tmp = $db->query('SELECT id FROM test WHERE id = 1')->rowCount())) + printf("[003] Expecting 1 got %s", var_export($tmp, true)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt new file mode 100644 index 0000000000..0be9eacc76 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt @@ -0,0 +1,180 @@ +--TEST-- +MySQL PDO:query() vs. PDO::prepare() and MySQL error 2050 +--SKIPIF-- + +--FILE-- +setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0); + if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY)) + printf("[004] Unable to turn off emulated prepared statements\n"); + + printf("Buffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + printf("Unbuffered...\n"); + MySQLPDOTest::createTestTable($db); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + /* + NOTE - this will cause an error and it OK + When using unbuffered prepared statements MySQL expects you to + fetch all data from the row before sending new data to the server. + PDO::query() will prepare and execute a statement in one step. + After the execution of PDO::query(), MySQL expects you to fetch + the results from the line before sending new commands. However, + PHP/PDO will send a CLOSE message as part of the PDO::query() call. + + The following happens: + + $stmt = PDO::query() + mysql_stmt_prepare() + mysql_stmt_execute() + + $stmt->fetchAll() + mysql_stmt_fetch() + + And now the right side of the expression will be executed first: + $stmt = PDO::query() + PDO::query() + mysql_stmt_prepare + mysql_stmt_execute + + PHP continues at the left side of the expression: + + $stmt = PDO::query() + + What happens is that $stmt gets overwritten. The reference counter of the + zval representing the current value of $stmt. PDO gets a callback that + it has to free the resources associated with the zval representing the + current value of stmt: + mysql_stmt_close + ---> ERROR + ---> execute() has been send on the line, you are supposed to fetch + ---> you must not try to send a CLOSE after execute() + ---> Error: 2050 (CR_FETCH_CANCELED) + ---> Message: Row retrieval was canceled by mysql_stmt_close() call + ---> MySQL does its best to recover the line and cancels the retrieval + + PHP proceeds and assigns the new statement object/zval obtained from + PDO to $stmt. + + Solutions: + - use mysqlnd + - use prepare() + execute() instead of query() + - as there is no explicit close() in PDO, try unset($stmt) before the new assignment + - fix PDO::query() [not the driver, fix PDO itself] + */ + + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id = 1'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $db->prepare('SELECT id, label FROM test WHERE id = 1'); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + unset($stmt); + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + unset($stmt); + $stmt = $db->query('SELECT id, label FROM test WHERE id = 1'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!"; +?> +--EXPECTF-- +Native PS... +Buffered... +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +Unbuffered... + +Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050 in %s on line %d +array(0) { +} + +Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050 in %s on line %d +array(0) { +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" + } +} +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt new file mode 100644 index 0000000000..853f5a3855 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt @@ -0,0 +1,122 @@ +--TEST-- +MySQL Prepared Statements and different column counts +--SKIPIF-- +query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC); +$matches = array(); +if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) + die(sprintf("skip Cannot determine MySQL Server version\n")); + +$version = $matches[0] * 10000 + $matches[1] * 100 + $matches[2]; +if ($version < 50000) + die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n", + $matches[0], $matches[1], $matches[2], $version)); +?> +--FILE-- +fetch(PDO::FETCH_ASSOC); + } while ($stmt->nextRowSet()); + + if (!isset($row['one']) || ($row['one'] != 1)) { + printf("[%03d + 1] Expecting array('one' => 1), got %s\n", $offset, var_export($row, true)); + return false; + } + + if (($columns == 2) && + (!isset($row['two']) || ($row['two'] != 2))) { + printf("[%03d + 2] Expecting array('one' => 1, 'two' => 2), got %s\n", $offset, var_export($row, true)); + return false; + } else if (($columns == 1) && isset($row['two'])) { + printf("[%03d + 3] Expecting one array element got two\n", $offset); + return false; + } + + return true; + } + + try { + + // What will happen if a PS returns a differen number of result set column upon each execution? + // Lets try with a SP accepting parameters... + $db->exec('DROP PROCEDURE IF EXISTS p'); + $db->exec('CREATE PROCEDURE p(IN cols INT) BEGIN IF cols < 2 THEN SELECT cols AS "one"; ELSE SELECT 1 AS "one", cols AS "two"; END IF; END;'); + + // Emulates PS first + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + $stmt = $db->prepare('CALL p(?)'); + + $columns = null; + $stmt->bindParam(1, $columns); + for ($i = 0; $i < 5; $i++) { + $columns = ($i % 2) + 1; + $stmt->execute(); + check_result($i, $stmt, $columns); + } + + if (MySQLPDOTest::isPDOMySQLnd()) { + // Native PS + // Libmysql cannot handle such a stored procedure. You will see leaks with libmysql + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); + $stmt = $db->prepare('CALL p(?)'); + $stmt->bindParam(1, $columns); + for ($i = 5; $i < 10; $i++) { + $columns = ($i % 2) + 1; + $stmt->execute(); + check_result($i, $stmt, $columns); + } + } + + // And now without parameters... - this gives a different control flow inside PDO + $db->exec('DROP PROCEDURE IF EXISTS p'); + $db->exec('CREATE PROCEDURE p() BEGIN DECLARE cols INT; SELECT @numcols INTO cols; IF cols < 2 THEN SET @numcols = 2; SELECT cols AS "one"; ELSE SET @numcols = 1; SELECT 1 AS "one", cols AS "two"; END IF; END;'); + + // Emulates PS first + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); + $db->exec('SET @numcols = 1'); + $stmt = $db->prepare('CALL p()'); + $stmt->execute(); + check_result(11, $stmt, 1); + $stmt->execute(); + check_result(12, $stmt, 2); + $db->exec('SET @numcols = 1'); + $stmt->execute(); + check_result(13, $stmt, 1); + + if (MySQLPDOTest::isPDOMySQLnd()) { + // Native PS + // Libmysql cannot handle such a stored procedure. You will see leaks with libmysql + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1); + $db->exec('SET @numcols = 1'); + $stmt = $db->prepare('CALL p()'); + $stmt->execute(); + check_result(14, $stmt, 1); + $stmt->execute(); + check_result(15, $stmt, 2); + $db->exec('SET @numcols = 1'); + $stmt->execute(); + check_result(16, $stmt, 1); + } + + } catch (PDOException $e) { + printf("[99] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + print "done!"; +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pdo_mysql_subclass.phpt b/ext/pdo_mysql/tests/pdo_mysql_subclass.phpt new file mode 100644 index 0000000000..90d4fb25ba --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_subclass.phpt @@ -0,0 +1,101 @@ +--TEST-- +MySQL PDOStatement->execute()/fetch(), Non-SELECT +--SKIPIF-- + +--FILE-- +protocol(); + return call_user_func_array(array($this, 'parent::__construct'), func_get_args()); + } + + public function exec() { + $this->protocol(); + return call_user_func_array(array($this, 'parent::exec'), func_get_args()); + } + + public function query() { + $this->protocol(); + return call_user_func_array(array($this, 'parent::query'), func_get_args()); + } + + public function __call($method, $args) { + print "__call()"; + // $this->protocol(); + } + + private function protocol() { + $stack = debug_backtrace(); + if (!isset($stack[1])) + return; + + printf("%s(", $stack[1]['function']); + $args = ''; + foreach ($stack[1]['args'] as $k => $v) + $args .= sprintf("%s, ", var_export($v, true)); + if ($args != '') + printf("%s", substr($args, 0, -2)); + printf(")\n"); + } + + } + + $db = new MyPDO(PDO_MYSQL_TEST_DSN, PDO_MYSQL_TEST_USER, PDO_MYSQL_TEST_PASS); + $db->exec('DROP TABLE IF EXISTS test'); + $db->exec('CREATE TABLE test(id INT)'); + $db->exec('INSERT INTO test(id) VALUES (1), (2)'); + $stmt = $db->query('SELECT * FROM test ORDER BY id ASC'); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + var_dump($stmt->fetch()); + $db->intercept_call(); + + + } catch (PDOException $e) { + printf("[001] %s [%s] %s\n", + $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo())); + } + + $db->exec('DROP TABLE IF EXISTS test'); + print "done!\n"; +?> +--XFAIL-- +PDO doesn't like __call() +--EXPECTF-- +__construct('%s', '%s', '%s') +exec('DROP TABLE IF EXISTS test') +exec('CREATE TABLE test(id INT)') +exec('INSERT INTO test(id) VALUES (1), (2)') +query('SELECT * FROM test ORDER BY id ASC') +array(2) { + [0]=> + array(1) { + ["id"]=> + string(1) "1" + } + [1]=> + array(1) { + ["id"]=> + string(1) "2" + } +} +bool(false) +__call('intercept_call', array ( +)) +exec('DROP TABLE IF EXISTS test') +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_types.phpt b/ext/pdo_mysql/tests/pdo_mysql_types.phpt new file mode 100644 index 0000000000..ae4005b8c6 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_types.phpt @@ -0,0 +1,174 @@ +--TEST-- +MySQL PDO->exec(), native types wo ZEROFILL +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine()); + @$db->exec($sql); + if ($db->errorCode() != 0) { + // not all MySQL Server versions and/or engines might support the type + return true; + } + + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)'); + $stmt->bindValue(1, $offset); + $stmt->bindValue(2, $value); + if (!$stmt->execute()) { + printf("[%03d + 1] INSERT failed, %s\n", $offset, var_export($stmt->errorInfo(), true)); + return false; + } + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + $stmt = $db->query('SELECT id, label FROM test'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + if (!isset($row['id']) || !isset($row['label'])) { + printf("[%03d + 2] Fetched result seems wrong, dumping result: %s\n", $offset, var_export($row, true)); + return false; + } + + if ($row['id'] != $offset) { + printf("[%03d + 3] Expecting %s got %s\n", $offset, $row['id']); + return false; + } + + if (!is_null($pattern)) { + if (!preg_match($pattern, $row['label'])) { + printf("[%03d + 5] Value seems wrong, accepting pattern %s got %s, check manually\n", + $offset, $pattern, var_export($row['label'], true)); + return false; + } + + } else { + + $exp = $value; + if (!is_null($ret_value)) { + // we expect a different return value than our input value + // typically the difference is only the type + $exp = $ret_value; + } + if ($row['label'] !== $exp) { + printf("[%03d + 4] %s - input = %s/%s, output = %s/%s\n", $offset, + $sql_type, var_export($exp, true), gettype($exp), + var_export($row['label'], true), gettype($row['label'])); + return false; + } + + } + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $stmt = $db->query('SELECT id, label FROM test'); + $row_string = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if (is_null($pattern) && ($row['label'] != $row_string['label'])) { + printf("%s - STRINGIGY = %s, NATIVE = %s\n", $sql_type, var_export($row_string['label'], true), var_export($row['label'], true)); + return false; + } else if (!is_null($pattern) && !preg_match($pattern, $row_string['label'])) { + printf("%s - STRINGIGY = %s, NATIVE = %s, pattern '%s'\n", $sql_type, var_export($row_string['label'], true), var_export($row['label'], true), $pattern); + return false; + } + + + return true; + } + + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +/* + test_type($db, 20, 'BIT(8)', 1); +*/ + $is_mysqlnd = MySQLPDOTest::isPDOMySQLnd(); + + test_type($db, 30, 'TINYINT', -127, ($is_mysqlnd) ? -127: '-127'); + test_type($db, 40, 'TINYINT UNSIGNED', 255, ($is_mysqlnd) ? 255 : '255'); + test_type($db, 50, 'BOOLEAN', 1, ($is_mysqlnd) ? 1 : '1'); + + test_type($db, 60, 'SMALLINT', -32768, ($is_mysqlnd) ? -32768 : '-32768'); + test_type($db, 70, 'SMALLINT UNSIGNED', 65535, ($is_mysqlnd) ? 65535 : '65535'); + + test_type($db, 80, 'MEDIUMINT', -8388608, ($is_mysqlnd) ? -8388608 : '-8388608'); + test_type($db, 90, 'MEDIUMINT UNSIGNED', 16777215, ($is_mysqlnd) ? 16777215 : '16777215'); + + test_type($db, 100, 'INT', -2147483648, ($is_mysqlnd) ? -2147483648 : '-2147483648'); + test_type($db, 110, 'INT UNSIGNED', 4294967295, ($is_mysqlnd) ? 4294967295 : '4294967295'); + + // no chance to return int with the current PDO version - we are forced to return strings + test_type($db, 120, 'BIGINT', 1, ($is_mysqlnd) ? 1 : '1'); + // to avoid trouble with numeric ranges, lets pass the numbers as a string + test_type($db, 130, 'BIGINT', '-9223372036854775808', NULL, '/^\-9[\.]*22/'); + test_type($db, 140, 'BIGINT UNSIGNED', '18446744073709551615', NULL, '/^1[\.]*844/'); + + test_type($db, 150, 'REAL', -1.01, ($is_mysqlnd) ? -1.01 : '-1.01'); + test_type($db, 160, 'REAL UNSIGNED', 1.01, ($is_mysqlnd) ? 1.01 : '1.01'); + + test_type($db, 170, 'DOUBLE', -1.01, ($is_mysqlnd) ? -1.01 : '-1.01'); + test_type($db, 180, 'DOUBLE UNSIGNED', 1.01, ($is_mysqlnd) ? 1.01 : '1.01'); + + test_type($db, 210, 'FLOAT', -1.01, NULL, '/^\-1.0\d+/'); + test_type($db, 220, 'FLOAT UNSIGNED', 1.01, NULL, '/^1.0\d+/'); + + test_type($db, 250, 'DECIMAL', -1.01, '-1'); + test_type($db, 260, 'DECIMAL UNSIGNED', 1.01, '1'); + + + test_type($db, 290, 'NUMERIC', -1.01, '-1'); + test_type($db, 300, 'NUMERIC UNSIGNED', 1.01, '1'); + + test_type($db, 330, 'DATE', '2008-04-23'); + test_type($db, 340, 'TIME', '14:37:00'); + test_type($db, 350, 'TIMESTAMP', '2008-05-06 21:09:00'); + test_type($db, 360, 'DATETIME', '2008-03-23 14:38:00'); + test_type($db, 370, 'YEAR', 2008, ($is_mysqlnd) ? 2008 : '2008'); + + test_type($db, 380, 'CHAR(1)', 'a'); + test_type($db, 390, 'CHAR(10)', '0123456789'); + test_type($db, 400, 'CHAR(255)', str_repeat('z', 255)); + test_type($db, 410, 'VARCHAR(1)', 'a'); + test_type($db, 420, 'VARCHAR(10)', '0123456789'); + test_type($db, 430, 'VARCHAR(255)', str_repeat('z', 255)); + + test_type($db, 440, 'BINARY(1)', str_repeat('a', 1)); + test_type($db, 450, 'BINARY(255)', str_repeat('b', 255)); + test_type($db, 460, 'VARBINARY(1)', str_repeat('a', 1)); + test_type($db, 470, 'VARBINARY(255)', str_repeat('b', 255)); + + test_type($db, 480, 'TINYBLOB', str_repeat('b', 255)); + test_type($db, 490, 'BLOB', str_repeat('b', 256)); + test_type($db, 500, 'MEDIUMBLOB', str_repeat('b', 256)); + test_type($db, 510, 'LONGBLOB', str_repeat('b', 256)); + + test_type($db, 520, 'TINYTEXT', str_repeat('b', 255)); + test_type($db, 530, 'TINYTEXT BINARY', str_repeat('b', 255)); + + test_type($db, 560, 'TEXT', str_repeat('b', 256)); + test_type($db, 570, 'TEXT BINARY', str_repeat('b', 256)); + + test_type($db, 580, 'MEDIUMTEXT', str_repeat('b', 256)); + test_type($db, 590, 'MEDIUMTEXT BINARY', str_repeat('b', 256)); + + test_type($db, 600, 'LONGTEXT', str_repeat('b', 256)); + test_type($db, 610, 'LONGTEXT BINARY', str_repeat('b', 256)); + + test_type($db, 620, "ENUM('yes', 'no') DEFAULT 'yes'", 'no'); + test_type($db, 630, "SET('yes', 'no') DEFAULT 'yes'", 'no'); + + test_type($db, 640, 'DECIMAL(3,2)', -1.01, '-1.01'); + + + echo "done!\n"; +?> +--EXPECTF-- +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt b/ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt new file mode 100644 index 0000000000..9d521a1b85 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt @@ -0,0 +1,117 @@ +--TEST-- +MySQL PDO->exec(), native types - ZEROFILL +--SKIPIF-- + +--FILE-- +exec('DROP TABLE IF EXISTS test'); + $sql = sprintf('CREATE TABLE test(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine()); + @$db->exec($sql); + if ($db->errorCode() != 0) { + // not all MySQL Server versions and/or engines might support the type + return true; + } + + $stmt = $db->prepare('INSERT INTO test(id, label) VALUES (?, ?)'); + $stmt->bindValue(1, $offset); + $stmt->bindValue(2, $value); + try { + if (!$stmt->execute()) { + printf("[%03d + 1] INSERT failed, %s\n", $offset, var_export($stmt->errorInfo(), true)); + return false; + } + } catch (PDOException $e) { + // This might be a SQL warning on signed values inserted in unsigned columns + // Zerofill implies unsigned but the test plays with signed = negative values as well! + return true; + } + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + $stmt = $db->query('SELECT id, label FROM test'); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if (!isset($row['id']) || !isset($row['label'])) { + printf("[%03d + 2] Fetched result seems wrong, dumping result: %s\n", $offset, var_export($row, true)); + return false; + } + + if ($row['id'] != $offset) { + printf("[%03d + 3] Expecting %s got %s\n", $offset, $row['id']); + return false; + } + + if (!is_null($pattern)) { + + if (!preg_match($pattern, $row['label'])) { + printf("[%03d + 5] Value seems wrong, accepting pattern %s got %s, check manually\n", + $offset, $pattern, var_export($row['label'], true)); + return false; + } + + } else { + + $exp = $value; + if (!is_null($ret_value)) { + // we expect a different return value than our input value + // typically the difference is only the type + $exp = $ret_value; + } + + if ($row['label'] !== $exp) { + printf("[%03d + 4] %s - input = %s/%s, output = %s/%s\n", $offset, + $sql_type, var_export($exp, true), gettype($exp), + var_export($row['label'], true), gettype($row['label'])); + return false; + } + + } + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $stmt = $db->query('SELECT id, label FROM test'); + $row_string = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if ($row['label'] != $row_string['label']) { + printf("%s - STRINGIGY = %s, NATIVE = %s\n", $sql_type, var_export($row_string['label'], true), var_export($row['label'], true)); + return false; + } + + return true; + } + + $db = MySQLPDOTest::factory(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + test_type($db, 100, 'REAL ZEROFILL', -1.01, NULL, '/^[0]*0$/'); + test_type($db, 110, 'REAL ZEROFILL', 1.01, NULL, '/^[0]*1\.01$/'); + test_type($db, 120, 'REAL UNSIGNED ZEROFILL', 1.01, NULL, '/^[0]*1\.01$/'); + + test_type($db, 130, 'DOUBLE ZEROFILL', -1.01, NULL, '/^[0]*0$/'); + test_type($db, 140, 'DOUBLE ZEROFILL', 1.01, NULL, '/^[0]*1\.01$/'); + test_type($db, 150, 'DOUBLE UNSIGNED ZEROFILL', 1.01, NULL, '/^[0]*1\.01$/'); + + test_type($db, 160, 'FLOAT ZEROFILL', -1.01, NULL, '/^[0]*0$/'); + test_type($db, 170, 'FLOAT ZEROFILL', 1, NULL, '/^[0]*1$/'); + test_type($db, 180, 'FLOAT UNSIGNED ZEROFILL', -1, NULL, '/^[0]*0$/'); + + test_type($db, 190, 'DECIMAL ZEROFILL', -1.01, NULL, '/^[0]*0$/'); + test_type($db, 200, 'DECIMAL ZEROFILL', 1.01, NULL, '/^[0]*1$/'); + test_type($db, 210, 'DECIMAL UNSIGNED ZEROFILL', 1.01, NULL, '/^[0]*1$/'); + + test_type($db, 220, 'NUMERIC ZEROFILL', -1, NULL, '/^[0]*0$/'); + test_type($db, 230, 'NUMERIC ZEROFILL', 1, NULL, '/^[0]*1$/'); + test_type($db, 240, 'NUMERIC UNSIGNED ZEROFILL', 1.01, NULL, '/^[0]*1$/'); + + echo "done!\n"; +?> +--EXPECTF-- +done! \ No newline at end of file diff --git a/ext/pdo_mysql/tests/pecl_bug_5200.phpt b/ext/pdo_mysql/tests/pecl_bug_5200.phpt new file mode 100644 index 0000000000..d07fe94326 --- /dev/null +++ b/ext/pdo_mysql/tests/pecl_bug_5200.phpt @@ -0,0 +1,30 @@ +--TEST-- +PDO MySQL PECL Bug #5200 (Describe table gives unexpected result mysql and type enum) +--SKIPIF-- + +--FILE-- +exec("CREATE TABLE test (bar INT NOT NULL, phase enum('please_select', 'I', 'II', 'IIa', 'IIb', 'III', 'IV'))"); + +foreach ($db->query('DESCRIBE test phase')->fetchAll(PDO::FETCH_ASSOC) as $row) { + print_r($row); +} + +--EXPECT-- +Array +( + [field] => phase + [type] => enum('please_select','I','II','IIa','IIb','III','IV') + [null] => YES + [key] => + [default] => + [extra] => +) diff --git a/ext/pdo_mysql/tests/pecl_bug_5780.phpt b/ext/pdo_mysql/tests/pecl_bug_5780.phpt new file mode 100644 index 0000000000..c2e0a79371 --- /dev/null +++ b/ext/pdo_mysql/tests/pecl_bug_5780.phpt @@ -0,0 +1,39 @@ +--TEST-- +PDO MySQL PECL Bug #5780 (Failure to produce an error when one is expected) +--SKIPIF-- + +--FILE-- +exec("CREATE TABLE test (login varchar(32) NOT NULL, data varchar(64) NOT NULL)"); +$db->exec("CREATE TABLE test2 (login varchar(32) NOT NULL, password varchar(64) NOT NULL)"); +$db->exec("INSERT INTO test2 (login, password) VALUES ('testing', 'testing')"); +$db->exec("INSERT INTO test2 (login, password) VALUES ('test2', 'testpw2')"); + +$logstmt = $db->prepare('INSERT INTO test (login, data) VALUES (:var1, :var2)'); +$authstmt = $db->prepare('SELECT * FROM test2 WHERE login = :varlog AND password = :varpass'); +$authstmt->execute(array(':varlog' => 'testing', ':varpass' => 'testing')); +var_dump($authstmt->fetch(PDO::FETCH_NUM)); +@var_dump($logstmt->execute(array(':var1' => 'test1', ':var2' => 'test2'))); +$info = $logstmt->errorInfo(); +unset($info[2]); +var_dump($info); +--EXPECT-- +array(2) { + [0]=> + string(7) "testing" + [1]=> + string(7) "testing" +} +bool(true) +array(1) { + [0]=> + string(5) "00000" +} diff --git a/ext/pdo_mysql/tests/pecl_bug_5802.phpt b/ext/pdo_mysql/tests/pecl_bug_5802.phpt new file mode 100644 index 0000000000..fa22ecdcb6 --- /dev/null +++ b/ext/pdo_mysql/tests/pecl_bug_5802.phpt @@ -0,0 +1,52 @@ +--TEST-- +PDO MySQL PECL Bug #5802 (bindParam/bindValue retain the is_null flag) +--SKIPIF-- + +--FILE-- +exec('create table test ( bar char(3) NULL )'); +$stmt = $db->prepare('insert into test (bar) values(:bar)') or var_dump($db->errorInfo()); + +$bar = 'foo'; +$stmt->bindParam(':bar', $bar); +$stmt->execute() or var_dump($stmt->errorInfo()); + +$bar = null; +$stmt->bindParam(':bar', $bar); +$stmt->execute() or var_dump($stmt->errorInfo()); + +$bar = 'qaz'; +$stmt->bindParam(':bar', $bar); +$stmt->execute() or var_dump($stmt->errorInfo()); + +$stmt = $db->prepare('select * from test') or var_dump($db->errorInfo()); + +if($stmt) $stmt->execute(); +if($stmt) var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + +--EXPECT-- +array(3) { + [0]=> + array(1) { + ["bar"]=> + string(3) "foo" + } + [1]=> + array(1) { + ["bar"]=> + NULL + } + [2]=> + array(1) { + ["bar"]=> + string(3) "qaz" + } +} diff --git a/ext/pdo_mysql/tests/show_tables.phpt b/ext/pdo_mysql/tests/show_tables.phpt index dc95d5f5f7..1b1ef67680 100644 --- a/ext/pdo_mysql/tests/show_tables.phpt +++ b/ext/pdo_mysql/tests/show_tables.phpt @@ -3,14 +3,14 @@ PDO MySQL SHOW TABLES --SKIPIF-- --FILE-- query('SHOW TABLES')); --EXPECT-- diff --git a/ext/pdo_mysql/tests/skipif.inc b/ext/pdo_mysql/tests/skipif.inc new file mode 100755 index 0000000000..d48670aec6 --- /dev/null +++ b/ext/pdo_mysql/tests/skipif.inc @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/ext/pdo_mysql/tests/table.inc b/ext/pdo_mysql/tests/table.inc new file mode 100644 index 0000000000..c7bb9cc5ee --- /dev/null +++ b/ext/pdo_mysql/tests/table.inc @@ -0,0 +1,9 @@ +exec('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE test(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=' . $engine); +$db->exec('INSERT INTO test(id, label) VALUES (1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), (6, "f")'); +?> \ No newline at end of file -- 2.40.0