summaryrefslogtreecommitdiff
path: root/ext/pdo_mysql
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /ext/pdo_mysql
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/pdo_mysql')
-rwxr-xr-xext/pdo_mysql/CREDITS3
-rwxr-xr-xext/pdo_mysql/config.m4159
-rw-r--r--ext/pdo_mysql/config.w3220
-rw-r--r--ext/pdo_mysql/get_error_codes.php27
-rw-r--r--ext/pdo_mysql/mysql_driver.c786
-rw-r--r--ext/pdo_mysql/mysql_statement.c930
-rw-r--r--ext/pdo_mysql/package2.xml93
-rw-r--r--ext/pdo_mysql/pdo_mysql.c261
-rw-r--r--ext/pdo_mysql/php_pdo_mysql.h48
-rw-r--r--ext/pdo_mysql/php_pdo_mysql_int.h176
-rw-r--r--ext/pdo_mysql/php_pdo_mysql_sqlstate.h646
-rw-r--r--ext/pdo_mysql/tests/README16
-rw-r--r--ext/pdo_mysql/tests/bug41125.phpt162
-rw-r--r--ext/pdo_mysql/tests/bug44327.phpt64
-rw-r--r--ext/pdo_mysql/tests/bug46292.phpt84
-rw-r--r--ext/pdo_mysql/tests/bug53551.phpt73
-rw-r--r--ext/pdo_mysql/tests/bug53782.phpt40
-rw-r--r--ext/pdo_mysql/tests/bug54929.phpt74
-rw-r--r--ext/pdo_mysql/tests/bug_33689.phpt64
-rw-r--r--ext/pdo_mysql/tests/bug_37445.phpt20
-rw-r--r--ext/pdo_mysql/tests/bug_39483.phptbin0 -> 756 bytes
-rw-r--r--ext/pdo_mysql/tests/bug_39858.phpt102
-rw-r--r--ext/pdo_mysql/tests/bug_41125.phpt51
-rw-r--r--ext/pdo_mysql/tests/bug_41698.phpt37
-rw-r--r--ext/pdo_mysql/tests/bug_41997.phpt70
-rw-r--r--ext/pdo_mysql/tests/bug_42499.phpt80
-rw-r--r--ext/pdo_mysql/tests/bug_43371.phpt19
-rw-r--r--ext/pdo_mysql/tests/bug_44454.phpt114
-rw-r--r--ext/pdo_mysql/tests/bug_44707.phpt92
-rw-r--r--ext/pdo_mysql/tests/bug_45120.phpt48
-rw-r--r--ext/pdo_mysql/tests/bug_50323.phpt61
-rw-r--r--ext/pdo_mysql/tests/bug_51670.phpt24
-rw-r--r--ext/pdo_mysql/tests/bug_61207.phpt108
-rw-r--r--ext/pdo_mysql/tests/bug_61411.phpt53
-rw-r--r--ext/pdo_mysql/tests/bug_61755.phpt41
-rw-r--r--ext/pdo_mysql/tests/bug_pecl_12925.phpt62
-rw-r--r--ext/pdo_mysql/tests/bug_pecl_7976.phpt92
-rw-r--r--ext/pdo_mysql/tests/common.phpt28
-rw-r--r--ext/pdo_mysql/tests/config.inc52
-rw-r--r--ext/pdo_mysql/tests/last_insert_id.phpt35
-rw-r--r--ext/pdo_mysql/tests/mysql_pdo_test.inc177
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql___construct.phpt300
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt56
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt184
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt83
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt76
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt98
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt224
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt37
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt37
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt31
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt166
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt42
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt48
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt77
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt121
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt21
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt48
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt65
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt155
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt206
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_bit.phpt64
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt85
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_commit.phpt90
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_connect_charset.phpt33
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt86
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt111
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_exec.phpt185
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt92
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt108
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt65
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt88
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt106
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_interface.phpt60
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt119
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt97
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt31
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt419
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt85
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt84
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_load_data.phpt118
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt54
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt385
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt96
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt49
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt145
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt39
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt92
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt90
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_rollback.phpt96
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt115
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt159
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt173
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt336
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt148
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt98
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt176
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt74
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt69
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt59
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt136
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt191
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt153
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt93
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt109
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt314
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt102
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt313
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt36
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt184
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt123
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_subclass.phpt105
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_types.phpt184
-rw-r--r--ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt128
-rw-r--r--ext/pdo_mysql/tests/pecl_bug_5200.phpt36
-rw-r--r--ext/pdo_mysql/tests/pecl_bug_5780.phpt49
-rw-r--r--ext/pdo_mysql/tests/pecl_bug_5802.phpt61
-rw-r--r--ext/pdo_mysql/tests/show_tables.phpt20
-rw-r--r--ext/pdo_mysql/tests/skipif.inc7
-rw-r--r--ext/pdo_mysql/tests/table.inc9
120 files changed, 14099 insertions, 0 deletions
diff --git a/ext/pdo_mysql/CREDITS b/ext/pdo_mysql/CREDITS
new file mode 100755
index 0000000..0423ccc
--- /dev/null
+++ b/ext/pdo_mysql/CREDITS
@@ -0,0 +1,3 @@
+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
new file mode 100755
index 0000000..a2ba2fd
--- /dev/null
+++ b/ext/pdo_mysql/config.m4
@@ -0,0 +1,159 @@
+dnl $Id$
+dnl config.m4 for extension pdo_mysql
+dnl vim: se ts=2 sw=2 et:
+
+PHP_ARG_WITH(pdo-mysql, for MySQL support for PDO,
+[ --with-pdo-mysql[=DIR] PDO: MySQL support. DIR is the MySQL base directory
+ If no value or mysqlnd is passed as DIR, the
+ MySQL native driver will be used])
+
+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
+
+ if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then
+ AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])
+ fi
+
+ 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" != "yes" && test "$PHP_PDO_MYSQL" != "mysqlnd"; then
+ if test -f $PHP_PDO_MYSQL && test -x $PHP_PDO_MYSQL ; then
+ PDO_MYSQL_CONFIG=$PHP_PDO_MYSQL
+ else
+ 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
+ fi
+ fi
+ fi
+
+ if test "$PHP_PDO_MYSQL" = "yes" || test "$PHP_PDO_MYSQL" = "mysqlnd"; then
+ dnl enables build of mysqnd library
+ PHP_MYSQLND_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 -n "$PDO_MYSQL_CONFIG"; then
+ AC_MSG_RESULT($PDO_MYSQL_CONFIG)
+ if test "x$SED" = "x"; then
+ AC_PATH_PROG(SED, sed)
+ 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"`
+ elif test -n "$PDO_MYSQL_DIR"; then
+ 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
+ else
+ AC_MSG_RESULT([not found])
+ AC_MSG_ERROR([Unable to find your mysql installation])
+ fi
+
+ PHP_CHECK_LIBRARY($PDO_MYSQL_LIBNAME, mysql_commit,
+ [
+ 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_commit, [], [
+ AC_MSG_ERROR([PDO_MYSQL configure failed, MySQL 4.1 needed. 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=<DIR>. Please check config.log for more information.])
+ ], [
+ -L$PDO_MYSQL_LIB_DIR
+ ])
+ PDO_MYSQL_LIBS="$PDO_MYSQL_LIBS -lz"
+ fi
+
+ PHP_EVAL_INCLINE($PDO_MYSQL_INCLUDE)
+ PHP_EVAL_LIBLINE($PDO_MYSQL_LIBS, PDO_MYSQL_SHARED_LIBADD)
+ ],[
+ $PDO_MYSQL_LIBS
+ ])
+ fi
+
+ ifdef([PHP_CHECK_PDO_INCLUDES],
+ [
+ PHP_CHECK_PDO_INCLUDES
+ ],[
+ AC_MSG_CHECKING([for PDO includes])
+ if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then
+ pdo_cv_inc_path=$abs_srcdir/ext
+ elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then
+ pdo_cv_inc_path=$abs_srcdir/ext
+ elif test -f $prefix/include/php/ext/pdo/php_pdo_driver.h; then
+ pdo_cv_inc_path=$prefix/include/php/ext
+ else
+ AC_MSG_ERROR([Cannot find php_pdo_driver.h.])
+ fi
+ AC_MSG_RESULT($pdo_cv_inc_path)
+ ])
+
+ if test -n "$PDO_MYSQL_CONFIG"; then
+ PDO_MYSQL_SOCKET=`$PDO_MYSQL_CONFIG --socket`
+ AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ])
+ fi
+
+ 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_cv_inc_path -I)
+ ifdef([PHP_ADD_EXTENSION_DEP],
+ [
+ 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
+
+ PHP_SUBST(PDO_MYSQL_SHARED_LIBADD)
+ PHP_SUBST_OLD(PDO_MYSQL_MODULE_TYPE)
+fi
diff --git a/ext/pdo_mysql/config.w32 b/ext/pdo_mysql/config.w32
new file mode 100644
index 0000000..da085dc
--- /dev/null
+++ b/ext/pdo_mysql/config.w32
@@ -0,0 +1,20 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("pdo-mysql", "MySQL support for PDO", "no");
+
+if (PHP_PDO_MYSQL != "no") {
+ 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 {
+ 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");
+ }
+ }
+}
diff --git a/ext/pdo_mysql/get_error_codes.php b/ext/pdo_mysql/get_error_codes.php
new file mode 100644
index 0000000..a1ed850
--- /dev/null
+++ b/ext/pdo_mysql/get_error_codes.php
@@ -0,0 +1,27 @@
+<?php
+ $codes = array();
+ $maxlen = 0;
+
+ while (!feof(STDIN)) {
+ $line = fgets(STDIN);
+
+ if (ereg('^\{[[:space:]]+(ER_.*)[[:space:]]+,[[:space:]]*"(.*)",[[:space:]]*"(.*)"', $line, $matches)) {
+ $codes[$matches[1]] = $matches[2];
+ $maxlen = max($maxlen, strlen($matches[1]));
+ }
+ }
+
+ if (empty($codes)) {
+ fputs(STDERR, "input doesn't look like a MySQL sql_state.h file\n");
+ exit(3);
+ }
+
+ echo "/* DO NOT EDIT THIS FILE!!! It is auto generated by get_error_codes.php */\n";
+ foreach ($codes as $code => $state) {
+ echo "#ifdef $code\n";
+ printf(" case %-{$maxlen}s: return \"%s\";\n", $code, $state);
+ echo "#endif\n";
+ }
+
+
+?>
diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c
new file mode 100644
index 0000000..a703f93
--- /dev/null
+++ b/ext/pdo_mysql/mysql_driver.c
@@ -0,0 +1,786 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Schlossnagle <george@omniti.com> |
+ | Wez Furlong <wez@php.net> |
+ | Johannes Schlueter <johannes@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#include "php_pdo_mysql.h"
+#include "php_pdo_mysql_int.h"
+#ifndef PDO_USE_MYSQLND
+#include <mysqld_error.h>
+#endif
+#include "zend_exceptions.h"
+
+#if defined(PDO_USE_MYSQLND)
+# define pdo_mysql_init(persistent) mysqlnd_init(persistent)
+#else
+# define pdo_mysql_init(persistent) mysql_init(NULL)
+#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;
+ pdo_error_type *pdo_err;
+ 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;
+ einfo = &S->einfo;
+ } else {
+ pdo_err = &dbh->error_code;
+ einfo = &H->einfo;
+ }
+
+ if (S && S->stmt) {
+ einfo->errcode = mysql_stmt_errno(S->stmt);
+ } else {
+ einfo->errcode = mysql_errno(H->server);
+ }
+
+ einfo->file = file;
+ einfo->line = line;
+
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ einfo->errmsg = NULL;
+ }
+
+ if (einfo->errcode) {
+ 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.",
+ 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);
+ PDO_DBG_RETURN(0);
+ }
+
+ if (S && S->stmt) {
+ strcpy(*pdo_err, mysql_stmt_sqlstate(S->stmt));
+ } else {
+ strcpy(*pdo_err, mysql_sqlstate(H->server));
+ }
+
+ if (!dbh->methods) {
+ PDO_DBG_INF("Throwing exception");
+ zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
+ *pdo_err, einfo->errcode, einfo->errmsg);
+ }
+
+ 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;
+ } else {
+ einfo = &H->einfo;
+ }
+
+ if (einfo->errcode) {
+ add_next_index_long(info, einfo->errcode);
+ add_next_index_string(info, einfo->errmsg, 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);
+ H->server = NULL;
+ }
+ if (H->einfo.errmsg) {
+ pefree(H->einfo.errmsg, dbh->is_persistent);
+ H->einfo.errmsg = NULL;
+ }
+ pefree(H, dbh->is_persistent);
+ dbh->driver_data = NULL;
+ }
+ 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));
+ char *nsql = NULL;
+ int nsql_len = 0;
+ int ret;
+ int server_version;
+
+ 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;
+
+ if (H->emulate_prepare) {
+ goto end;
+ }
+
+ 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);
+
+ if (ret == 1) {
+ /* query was rewritten */
+ sql = nsql;
+ sql_len = nsql_len;
+ } else if (ret == -1) {
+ /* failed to parse */
+ strcpy(dbh->error_code, stmt->error_code);
+ PDO_DBG_RETURN(0);
+ }
+
+ if (!(S->stmt = mysql_stmt_init(H->server))) {
+ pdo_mysql_error(dbh);
+ if (nsql) {
+ efree(nsql);
+ }
+ PDO_DBG_RETURN(0);
+ }
+
+ if (mysql_stmt_prepare(S->stmt, sql, sql_len)) {
+ /* TODO: might need to pull statement specific info here? */
+ /* if the query isn't supported by the protocol, fallback to emulation */
+ if (mysql_errno(H->server) == 1295) {
+ if (nsql) {
+ efree(nsql);
+ }
+ goto fallback;
+ }
+ pdo_mysql_error(dbh);
+ if (nsql) {
+ efree(nsql);
+ }
+ PDO_DBG_RETURN(0);
+ }
+ if (nsql) {
+ efree(nsql);
+ }
+
+ S->num_params = mysql_stmt_param_count(S->stmt);
+
+ if (S->num_params) {
+ S->params_given = 0;
+#if defined(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);
+
+ PDO_DBG_RETURN(1);
+
+fallback:
+end:
+ stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
+
+ 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);
+ PDO_DBG_RETURN(-1);
+ } else {
+ my_ulonglong c = mysql_affected_rows(H->server);
+ if (c == (my_ulonglong) -1) {
+ pdo_mysql_error(dbh);
+ PDO_DBG_RETURN(H->einfo.errcode ? -1 : 0);
+ } else {
+
+ /* 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);
+ }
+ }
+ 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);
+ 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';
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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
+}
+/* }}} */
+
+/* {{{ 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) {
+ PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("SET AUTOCOMMIT=1") TSRMLS_CC));
+ } else {
+ 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);
+
+ /* 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
+
+ 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);
+ break;
+
+ case PDO_ATTR_SERVER_VERSION:
+ ZVAL_STRING(return_value, (char *)mysql_get_server_info(H->server), 1);
+ break;
+
+ 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 defined(PDO_USE_MYSQLND)
+ unsigned 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);
+ PDO_DBG_RETURN(-1);
+ }
+ }
+ break;
+ case PDO_ATTR_AUTOCOMMIT:
+ ZVAL_LONG(return_value, dbh->auto_commit);
+ break;
+
+ case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
+ ZVAL_LONG(return_value, H->buffered);
+ break;
+
+ case PDO_MYSQL_ATTR_DIRECT_QUERY:
+ ZVAL_LONG(return_value, H->emulate_prepare);
+ break;
+
+#ifndef PDO_USE_MYSQLND
+ case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE:
+ ZVAL_LONG(return_value, H->max_buffer_size);
+ break;
+#endif
+
+ default:
+ PDO_DBG_RETURN(0);
+ }
+
+ 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;
+#if MYSQL_VERSION_ID <= 32230
+ void (*handler) (int);
+ 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)) {
+ PDO_DBG_RETURN(FAILURE);
+ }
+#else /* no mysql_ping() */
+ handler = signal(SIGPIPE, SIG_IGN);
+ mysql_stat(H->server);
+ switch (mysql_errno(H->server)) {
+ case CR_SERVER_GONE_ERROR:
+ case CR_SERVER_LOST:
+ signal(SIGPIPE, handler);
+ PDO_DBG_RETURN(FAILURE);
+ default:
+ break;
+ }
+ signal(SIGPIPE, handler);
+#endif /* end mysql_ping() */
+ PDO_DBG_RETURN(SUCCESS);
+}
+/* }}} */
+
+/* {{{ mysql_methods */
+static struct pdo_dbh_methods mysql_methods = {
+ mysql_handle_closer,
+ mysql_handle_preparer,
+ mysql_handle_doer,
+ mysql_handle_quoter,
+ mysql_handle_begin,
+ mysql_handle_commit,
+ mysql_handle_rollback,
+ pdo_mysql_set_attribute,
+ pdo_mysql_last_insert_id,
+ pdo_mysql_fetch_error_func,
+ pdo_mysql_get_attribute,
+ pdo_mysql_check_liveness
+};
+/* }}} */
+
+#ifdef PHP_WIN32
+# define MYSQL_UNIX_ADDR NULL
+#else
+# define MYSQL_UNIX_ADDR PDO_MYSQL_G(default_socket)
+#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;
+ int i, ret = 0;
+ char *host = NULL, *unix_socket = NULL;
+ unsigned int port = 3306;
+ char *dbname;
+ struct pdo_data_src_parser vars[] = {
+ { "charset", NULL, 0 },
+ { "dbname", "", 0 },
+ { "host", "localhost", 0 },
+ { "port", "3306", 0 },
+ { "unix_socket", MYSQL_UNIX_ADDR, 0 },
+ };
+ int connect_opts = 0
+#ifdef CLIENT_MULTI_RESULTS
+ |CLIENT_MULTI_RESULTS
+#endif
+#ifdef CLIENT_MULTI_STATEMENTS
+ |CLIENT_MULTI_STATEMENTS
+#endif
+ ;
+
+#if defined(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 = pdo_mysql_init(dbh->is_persistent))) {
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+
+ dbh->driver_data = H;
+
+#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);
+ char *init_cmd = NULL;
+#ifndef PDO_USE_MYSQLND
+ char *default_file = NULL, *default_group = NULL;
+#endif
+ long compress = 0;
+ char *ssl_key = NULL, *ssl_cert = NULL, *ssl_ca = NULL, *ssl_capath = NULL, *ssl_cipher = NULL;
+ 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, 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 (pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_FOUND_ROWS, 0 TSRMLS_CC)) {
+ connect_opts |= CLIENT_FOUND_ROWS;
+ }
+
+ if (pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_IGNORE_SPACE, 0 TSRMLS_CC)) {
+ connect_opts |= CLIENT_IGNORE_SPACE;
+ }
+
+ if (mysql_options(H->server, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout)) {
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+
+#if PHP_API_VERSION < 20100412
+ 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 defined(MYSQL_OPT_LOCAL_INFILE) || defined(PDO_USE_MYSQLND)
+ if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) {
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+#endif
+#ifdef MYSQL_OPT_RECONNECT
+ /* since 5.0.3, the default for this option is 0 if not specified.
+ * we want the old behaviour
+ * mysqlnd doesn't support reconnect, thus we don't have "|| defined(PDO_USE_MYSQLND)"
+ */
+ {
+ long reconnect = 1;
+ mysql_options(H->server, MYSQL_OPT_RECONNECT, (const char*)&reconnect);
+ }
+#endif
+ 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)) {
+ efree(init_cmd);
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+ efree(init_cmd);
+ }
+#ifndef PDO_USE_MYSQLND
+ default_file = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_READ_DEFAULT_FILE, NULL TSRMLS_CC);
+ if (default_file) {
+ if (mysql_options(H->server, MYSQL_READ_DEFAULT_FILE, (const char *)default_file)) {
+ efree(default_file);
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+ efree(default_file);
+ }
+
+ default_group= pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP, NULL TSRMLS_CC);
+ if (default_group) {
+ if (mysql_options(H->server, MYSQL_READ_DEFAULT_GROUP, (const char *)default_group)) {
+ efree(default_group);
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+ efree(default_group);
+ }
+#endif
+ compress = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_COMPRESS, 0 TSRMLS_CC);
+ if (compress) {
+ if (mysql_options(H->server, MYSQL_OPT_COMPRESS, 0)) {
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+ }
+
+ ssl_key = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_KEY, NULL TSRMLS_CC);
+ ssl_cert = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CERT, NULL TSRMLS_CC);
+ ssl_ca = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CA, NULL TSRMLS_CC);
+ ssl_capath = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CAPATH, NULL TSRMLS_CC);
+ ssl_cipher = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CIPHER, NULL TSRMLS_CC);
+
+ if (ssl_key || ssl_cert || ssl_ca || ssl_capath || ssl_cipher) {
+ mysql_ssl_set(H->server, ssl_key, ssl_cert, ssl_ca, ssl_capath, ssl_cipher);
+ if (ssl_key) {
+ efree(ssl_key);
+ }
+ if (ssl_cert) {
+ efree(ssl_cert);
+ }
+ if (ssl_ca) {
+ efree(ssl_ca);
+ }
+ if (ssl_capath) {
+ efree(ssl_capath);
+ }
+ if (ssl_cipher) {
+ efree(ssl_cipher);
+ }
+ }
+ }
+
+#ifdef PDO_MYSQL_HAS_CHARSET
+ if (vars[0].optval && mysql_options(H->server, MYSQL_SET_CHARSET_NAME, vars[0].optval)) {
+ pdo_mysql_error(dbh);
+ goto cleanup;
+ }
+#endif
+
+ dbname = vars[1].optval;
+ host = vars[2].optval;
+ if(vars[3].optval) {
+ port = atoi(vars[3].optval);
+ }
+ 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 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;
+ }
+
+ if (!dbh->auto_commit) {
+ mysql_handle_autocommit(dbh TSRMLS_CC);
+ }
+
+ H->attached = 1;
+
+ dbh->alloc_own_columns = 1;
+ dbh->max_escaped_char_length = 2;
+ dbh->methods = &mysql_methods;
+
+ ret = 1;
+
+cleanup:
+ for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
+ if (vars[i].freeme) {
+ efree(vars[i].optval);
+ }
+ }
+
+ dbh->methods = &mysql_methods;
+
+ PDO_DBG_RETURN(ret);
+}
+/* }}} */
+
+pdo_driver_t pdo_mysql_driver = {
+ PDO_DRIVER_HEADER(mysql),
+ pdo_mysql_handle_factory
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c
new file mode 100644
index 0000000..f2e36c1
--- /dev/null
+++ b/ext/pdo_mysql/mysql_statement.c
@@ -0,0 +1,930 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Schlossnagle <george@omniti.com> |
+ | Wez Furlong <wez@php.net> |
+ | Johannes Schlueter <johannes@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#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) /* {{{ */
+{
+ 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);
+ S->result = NULL;
+ }
+ if (S->einfo.errmsg) {
+ pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
+ S->einfo.errmsg = NULL;
+ }
+ if (S->stmt) {
+ pdo_mysql_stmt_close(S->stmt);
+ S->stmt = NULL;
+ }
+
+#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);
+ }
+
+ if (S->bound_result)
+ {
+ int i;
+ for (i = 0; i < stmt->column_count; i++) {
+ pdo_free_bound_result(S->bound_result[i]);
+ }
+
+ efree(S->bound_result);
+ efree(S->out_null);
+ efree(S->out_length);
+ }
+#endif
+
+
+ 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);
+ }
+ }
+ }
+
+#if PDO_USE_MYSQLND
+ if (!S->stmt && S->current_data) {
+ mnd_free(S->current_data);
+ }
+#endif /* PDO_USE_MYSQLND */
+
+ efree(S);
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+
+static void pdo_mysql_stmt_set_row_count(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
+{
+ long row_count;
+ pdo_mysql_stmt *S = stmt->driver_data;
+ row_count = (long) mysql_stmt_affected_rows(S->stmt);
+ if (row_count != (long)-1) {
+ stmt->row_count = row_count;
+ }
+}
+/* }}} */
+
+static int pdo_mysql_fill_stmt_from_result(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_fill_stmt_from_result");
+
+ row_count = mysql_affected_rows(H->server);
+ if (row_count == (my_ulonglong)-1) {
+ /* we either have a query that returned a result set or an error occurred
+ lets see if we have access to a result set */
+ if (!H->buffered) {
+ S->result = mysql_use_result(H->server);
+ } else {
+ S->result = mysql_store_result(H->server);
+ }
+ if (NULL == S->result) {
+ pdo_mysql_error_stmt(stmt);
+ PDO_DBG_RETURN(0);
+ }
+
+ stmt->row_count = (long) mysql_num_rows(S->result);
+ 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 = (long) row_count;
+ }
+
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+
+#ifndef PDO_USE_MYSQLND
+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;
+
+ 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) {
+ memset(S->params, 0, S->num_params * sizeof(MYSQL_BIND));
+ }
+ 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) {
+ 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);
+ }
+
+ 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 + 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;
+ }
+ }
+
+ /* 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;
+ }
+
+ 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 TSRMLS_CC);
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+#endif
+
+#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;
+ 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 = mysql_stmt_field_count(S->stmt);
+ 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);
+ }
+ }
+ }
+
+ pdo_mysql_stmt_set_row_count(stmt TSRMLS_CC);
+ 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;
+ PDO_DBG_ENTER("pdo_mysql_stmt_execute");
+ PDO_DBG_INF_FMT("stmt=%p", S->stmt);
+
+ if (S->stmt) {
+ PDO_DBG_RETURN(pdo_mysql_stmt_execute_prepared(stmt));
+ }
+
+ /* ensure that we free any previous unfetched results */
+ if (S->result) {
+ mysql_free_result(S->result);
+ S->result = NULL;
+ }
+
+ if (mysql_real_query(H->server, stmt->active_query_string, stmt->active_query_stringlen) != 0) {
+ pdo_mysql_error_stmt(stmt);
+ PDO_DBG_RETURN(0);
+ }
+
+ PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC));
+}
+/* }}} */
+
+static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
+{
+ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
+ pdo_mysql_db_handle *H = S->H;
+ long row_count;
+ int ret;
+ PDO_DBG_ENTER("pdo_mysql_stmt_next_rowset");
+ PDO_DBG_INF_FMT("stmt=%p", S->stmt);
+
+#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 */
+ int i;
+
+ stmt->column_count = mysql_stmt_field_count(S->stmt);
+ 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 = (long) mysql_stmt_affected_rows(S->stmt);
+ if (row_count != (long)-1) {
+ stmt->row_count = row_count;
+ }
+ PDO_DBG_RETURN(1);
+ }
+#endif
+
+/* ensure that we free any previous unfetched results */
+#ifndef PDO_USE_MYSQLND
+ if (S->stmt) {
+ stmt->column_count = (int)mysql_num_fields(S->result);
+ mysql_stmt_free_result(S->stmt);
+ }
+#endif
+ if (S->result) {
+ mysql_free_result(S->result);
+ S->result = NULL;
+ }
+
+ ret = mysql_next_result(H->server);
+#if PDO_USE_MYSQLND
+ /* for whatever reason mysqlnd breaks with libmysql compatibility at the C level, no -1 */
+ if (PASS != ret) {
+ pdo_mysql_error_stmt(stmt);
+ PDO_DBG_RETURN(0);
+ }
+ if (mysql_more_results(H->server)) {
+ PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC));
+ } else {
+ /* No more results */
+ PDO_DBG_RETURN(0);
+ }
+#else
+ if (ret > 0) {
+ pdo_mysql_error_stmt(stmt);
+ PDO_DBG_RETURN(0);
+ } else if (ret < 0) {
+ /* No more results */
+ PDO_DBG_RETURN(0);
+ } else {
+ PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC));
+ }
+#endif
+}
+/* }}} */
+
+
+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) /* {{{ */
+{
+#ifndef PDO_USE_MYSQLND
+ PDO_MYSQL_PARAM_BIND *b;
+#endif
+ 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");
+ 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];
+ /* recall how many parameters have been provided */
+#endif
+ PDO_DBG_RETURN(1);
+
+ case PDO_PARAM_EVT_EXEC_PRE:
+ if (S->params_given < (unsigned int) 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_USE_MYSQLND
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||
+ Z_TYPE_P(param->parameter) == IS_NULL) {
+ 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_STMT:
+ 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, &param->parameter);
+ if (stm) {
+ SEPARATE_ZVAL_IF_NOT_REF(&param->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);
+ PDO_DBG_RETURN(1);
+
+ case IS_LONG:
+ b->buffer_type = MYSQL_TYPE_LONG;
+ b->buffer = &Z_LVAL_P(param->parameter);
+ PDO_DBG_RETURN(1);
+
+ case IS_DOUBLE:
+ b->buffer_type = MYSQL_TYPE_DOUBLE;
+ b->buffer = &Z_DVAL_P(param->parameter);
+ PDO_DBG_RETURN(1);
+
+ default:
+ 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;
+ }
+ }
+
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+
+static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) /* {{{ */
+{
+ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
+#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
+ int ret;
+
+ if (S->stmt) {
+ ret = mysql_stmt_fetch(S->stmt);
+
+# ifdef MYSQL_DATA_TRUNCATED
+ if (ret == MYSQL_DATA_TRUNCATED) {
+ ret = 0;
+ }
+# endif
+
+ if (ret) {
+ if (ret != MYSQL_NO_DATA) {
+ pdo_mysql_error_stmt(stmt);
+ }
+ PDO_DBG_RETURN(0);
+ }
+
+ PDO_DBG_RETURN(1);
+ }
+#endif /* PDO_USE_MYSQLND */
+
+ if (!S->result) {
+ strcpy(stmt->error_code, "HY000");
+ PDO_DBG_RETURN(0);
+ }
+#if PDO_USE_MYSQLND
+ if (!S->stmt && S->current_data) {
+ mnd_free(S->current_data);
+ }
+#endif /* PDO_USE_MYSQLND */
+
+ if ((S->current_data = mysql_fetch_row(S->result)) == NULL) {
+#if PDO_USE_MYSQLND
+ if (S->result->unbuf && !S->result->unbuf->eof_reached && mysql_errno(S->H->server)) {
+#else
+ if (!S->result->eof && mysql_errno(S->H->server)) {
+#endif
+ pdo_mysql_error_stmt(stmt);
+ }
+ PDO_DBG_RETURN(0);
+ }
+
+ S->current_lengths = mysql_fetch_lengths(S->result);
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+
+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;
+ int i;
+
+ PDO_DBG_ENTER("pdo_mysql_stmt_describe");
+ PDO_DBG_INF_FMT("stmt=%p", S->stmt);
+ if (!S->result) {
+ PDO_DBG_RETURN(0);
+ }
+
+ if (colno >= stmt->column_count) {
+ /* error invalid column */
+ PDO_DBG_RETURN(0);
+ }
+
+ /* fetch all on demand, this seems easiest
+ ** if we've been here before bail out
+ */
+ if (cols[0].name) {
+ PDO_DBG_RETURN(1);
+ }
+ for (i = 0; i < stmt->column_count; i++) {
+ int namelen;
+
+ 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;
+
+#ifdef PDO_USE_MYSQLND
+ if (S->stmt) {
+ cols[i].param_type = PDO_PARAM_ZVAL;
+ } else
+#endif
+ {
+ cols[i].param_type = PDO_PARAM_STR;
+ }
+ }
+ 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) /* {{{ */
+{
+ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
+
+ PDO_DBG_ENTER("pdo_mysql_stmt_get_col");
+ PDO_DBG_INF_FMT("stmt=%p", S->stmt);
+ if (!S->result) {
+ PDO_DBG_RETURN(0);
+ }
+
+ /* With mysqlnd data is stored inside mysqlnd, not S->current_data */
+ if (!S->stmt) {
+ if (S->current_data == NULL || !S->result) {
+ PDO_DBG_RETURN(0);
+ }
+ }
+
+ if (colno >= stmt->column_count) {
+ /* error invalid column */
+ PDO_DBG_RETURN(0);
+ }
+#if PDO_USE_MYSQLND
+ if (S->stmt) {
+ Z_ADDREF_P(S->stmt->data->result_bind[colno].zv);
+ *ptr = (char*)&S->stmt->data->result_bind[colno].zv;
+ *len = sizeof(zval);
+ PDO_DBG_RETURN(1);
+ }
+#else
+ if (S->stmt) {
+ if (S->out_null[colno]) {
+ *ptr = NULL;
+ *len = 0;
+ 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];
+ PDO_DBG_RETURN(1);
+ }
+#endif
+ *ptr = S->current_data[colno];
+ *len = S->current_lengths[colno];
+ PDO_DBG_RETURN(1);
+} /* }}} */
+
+static char *type_to_name_native(int type) /* {{{ */
+{
+#define PDO_MYSQL_NATIVE_TYPE_NAME(x) case FIELD_TYPE_##x: return #x;
+
+ switch (type) {
+ PDO_MYSQL_NATIVE_TYPE_NAME(STRING)
+ PDO_MYSQL_NATIVE_TYPE_NAME(VAR_STRING)
+#ifdef MYSQL_HAS_TINY
+ PDO_MYSQL_NATIVE_TYPE_NAME(TINY)
+#endif
+ PDO_MYSQL_NATIVE_TYPE_NAME(SHORT)
+ PDO_MYSQL_NATIVE_TYPE_NAME(LONG)
+ PDO_MYSQL_NATIVE_TYPE_NAME(LONGLONG)
+ PDO_MYSQL_NATIVE_TYPE_NAME(INT24)
+ PDO_MYSQL_NATIVE_TYPE_NAME(FLOAT)
+ PDO_MYSQL_NATIVE_TYPE_NAME(DOUBLE)
+ PDO_MYSQL_NATIVE_TYPE_NAME(DECIMAL)
+#ifdef FIELD_TYPE_NEWDECIMAL
+ PDO_MYSQL_NATIVE_TYPE_NAME(NEWDECIMAL)
+#endif
+#ifdef FIELD_TYPE_GEOMETRY
+ PDO_MYSQL_NATIVE_TYPE_NAME(GEOMETRY)
+#endif
+ PDO_MYSQL_NATIVE_TYPE_NAME(TIMESTAMP)
+#ifdef MYSQL_HAS_YEAR
+ PDO_MYSQL_NATIVE_TYPE_NAME(YEAR)
+#endif
+ PDO_MYSQL_NATIVE_TYPE_NAME(SET)
+ PDO_MYSQL_NATIVE_TYPE_NAME(ENUM)
+ PDO_MYSQL_NATIVE_TYPE_NAME(DATE)
+#ifdef FIELD_TYPE_NEWDATE
+ PDO_MYSQL_NATIVE_TYPE_NAME(NEWDATE)
+#endif
+ PDO_MYSQL_NATIVE_TYPE_NAME(TIME)
+ PDO_MYSQL_NATIVE_TYPE_NAME(DATETIME)
+ PDO_MYSQL_NATIVE_TYPE_NAME(TINY_BLOB)
+ PDO_MYSQL_NATIVE_TYPE_NAME(MEDIUM_BLOB)
+ PDO_MYSQL_NATIVE_TYPE_NAME(LONG_BLOB)
+ PDO_MYSQL_NATIVE_TYPE_NAME(BLOB)
+ PDO_MYSQL_NATIVE_TYPE_NAME(NULL)
+ 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) /* {{{ */
+{
+ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
+ 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) {
+ PDO_DBG_RETURN(FAILURE);
+ }
+ if (colno >= stmt->column_count) {
+ /* error invalid column */
+ PDO_DBG_RETURN(FAILURE);
+ }
+
+ array_init(return_value);
+ MAKE_STD_ZVAL(flags);
+ array_init(flags);
+
+ F = S->fields + colno;
+
+ if (F->def) {
+ add_assoc_string(return_value, "mysql:def", F->def, 1);
+ }
+ if (IS_NOT_NULL(F->flags)) {
+ add_next_index_string(flags, "not_null", 1);
+ }
+ if (IS_PRI_KEY(F->flags)) {
+ add_next_index_string(flags, "primary_key", 1);
+ }
+ if (F->flags & MULTIPLE_KEY_FLAG) {
+ add_next_index_string(flags, "multiple_key", 1);
+ }
+ if (F->flags & UNIQUE_KEY_FLAG) {
+ add_next_index_string(flags, "unique_key", 1);
+ }
+ if (IS_BLOB(F->flags)) {
+ add_next_index_string(flags, "blob", 1);
+ }
+ str = type_to_name_native(F->type);
+ if (str) {
+ 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);
+ add_assoc_string(return_value, "table", (char *) (F->table?F->table:""), 1);
+ PDO_DBG_RETURN(SUCCESS);
+} /* }}} */
+
+static int pdo_mysql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
+{
+ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
+
+ 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 (S->stmt) {
+ int retval;
+ retval = mysql_stmt_free_result(S->stmt);
+ PDO_DBG_RETURN(retval ? 0 : 1);
+ }
+
+ 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);
+ }
+ }
+ PDO_DBG_RETURN(1);
+}
+/* }}} */
+
+struct pdo_stmt_methods mysql_stmt_methods = {
+ pdo_mysql_stmt_dtor,
+ pdo_mysql_stmt_execute,
+ pdo_mysql_stmt_fetch,
+ pdo_mysql_stmt_describe,
+ pdo_mysql_stmt_get_col,
+ pdo_mysql_stmt_param_hook,
+ NULL, /* set_attr */
+ NULL, /* get_attr */
+ pdo_mysql_stmt_col_meta,
+ pdo_mysql_stmt_next_rowset,
+ pdo_mysql_stmt_cursor_closer
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/pdo_mysql/package2.xml b/ext/pdo_mysql/package2.xml
new file mode 100644
index 0000000..cee6cc7
--- /dev/null
+++ b/ext/pdo_mysql/package2.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>PDO_MYSQL</name>
+ <channel>pecl.php.net</channel>
+ <summary>MySQL driver for PDO</summary>
+ <description>This extension provides a MySQL driver for PDO.
+ </description>
+ <lead>
+ <name>George Schlossnagle</name>
+ <user>gschlossnagle</user>
+ <email>george@omniti.com</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Ilia Alshanetsky</name>
+ <user>iliaa</user>
+ <email>iliaa@php.net</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Wez Furlong</name>
+ <user>wez</user>
+ <email>wez@php.net</email>
+ <active>yes</active>
+ </lead>
+ <date>2006-05-01</date>
+ <version>
+ <release>1.0.2</release>
+ <api>1.0.2</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://www.php.net/license">PHP</license>
+ <notes>
+This PECL release corresponds to PHP 5.1.3.
+
+Added PDO::ATTR_EMULATE_PREPARES which can be used to force use of emulated or
+native prepares. This attribute should can be set on the database handle, and
+will cause subsequent prepares to use emulation.
+
+- Fixed bug #36572 (cannot use native prepared statements with internal
+ queries like "show master status") (Ilia)
+- Repackage using package2.xml
+- Fixed Bug #35480 and #35415, crash when using persistent connections.
+- Improved error detection for OPTIMIZE queries
+- Added PDO::MYSQL_ATTR_LOCAL_INFILE, PDO::MYSQL_ATTR_INIT_COMMAND,
+ PDO::MYSQL_ATTR_READ_DEFAULT_FILE, PDO::MYSQL_ATTR_READ_DEFAULT_GROUP
+- Improved error reporting when using native prepared statements
+- Fixed PECL Bug #5193: improved bounds checking when calling getColumnMeta()
+- Fixed Bug #34630: improved (emulated) LOB support
+- Fixed Bug #34623: crash when selecting longtext fields
+- Fixed PECL Bug #5802; is_null flag was sticking
+- Fixed PECL Bug #5645; added mysql client library version information to phpinfo() output.
+
+Windows binaries can be found at http://pecl4win.php.net/ext.php/php_pdo_mysql.dll
+
+ </notes>
+ <contents>
+ <dir name="/">
+ <file name="config.m4" role="src" />
+ <file name="CREDITS" role="doc" />
+ <file name="mysql_driver.c" role="src" />
+ <file name="mysql_statement.c" role="src" />
+ <file name="pdo_mysql.c" role="src" />
+ <file name="php_pdo_mysql.h" role="src" />
+ <file name="php_pdo_mysql_int.h" role="src" />
+ <file name="php_pdo_mysql_sqlstate.h" role="src" />
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.0.3</min>
+ </php>
+ <pearinstaller>
+ <min>1.4.0</min>
+ </pearinstaller>
+ <package>
+ <name>pdo</name>
+ <channel>pecl.php.net</channel>
+ <min>1.0.3</min>
+ <providesextension>PDO</providesextension>
+ </package>
+ </required>
+ </dependencies>
+ <providesextension>PDO_MYSQL</providesextension>
+ <extsrcrelease />
+</package>
diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c
new file mode 100644
index 0000000..1dbb30f
--- /dev/null
+++ b/ext/pdo_mysql/pdo_mysql.c
@@ -0,0 +1,261 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Schlossnagle <george@omniti.com> |
+ | Johannes Schlueter <johannes@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#include "php_pdo_mysql.h"
+#include "php_pdo_mysql_int.h"
+
+#ifdef COMPILE_DL_PDO_MYSQL
+ZEND_GET_MODULE(pdo_mysql)
+#endif
+
+ZEND_DECLARE_MODULE_GLOBALS(pdo_mysql)
+
+/*
+ The default socket location is sometimes defined by configure.
+ With libmysql `mysql_config --socket` will fill PDO_MYSQL_UNIX_ADDR
+ and the user can use --with-mysql-sock=SOCKET which will fill
+ PDO_MYSQL_UNIX_ADDR. If both aren't set we're using mysqlnd and use
+ /tmp/mysql.sock as default on *nix and NULL for Windows (default
+ named pipe name is set in mysqlnd).
+*/
+#ifndef PDO_MYSQL_UNIX_ADDR
+# ifdef PHP_MYSQL_UNIX_SOCK_ADDR
+# define PDO_MYSQL_UNIX_ADDR PHP_MYSQL_UNIX_SOCK_ADDR
+# else
+# if !PHP_WIN32
+# define PDO_MYSQL_UNIX_ADDR "/tmp/mysql.sock"
+# else
+# define PDO_MYSQL_UNIX_ADDR NULL
+# endif
+# endif
+#endif
+
+#ifdef PDO_USE_MYSQLND
+#include "ext/mysqlnd/mysqlnd_reverse_api.h"
+static MYSQLND * pdo_mysql_convert_zv_to_mysqlnd(zval * zv TSRMLS_DC)
+{
+ if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == php_pdo_get_dbh_ce()) {
+ pdo_dbh_t * dbh = zend_object_store_get_object(zv TSRMLS_CC);
+
+ if (!dbh || dbh->driver != &pdo_mysql_driver) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided PDO instance is not using MySQL but %s", dbh->driver->driver_name);
+ return NULL;
+ }
+
+ return ((pdo_mysql_db_handle *)dbh->driver_data)->server;
+ }
+ return NULL;
+}
+
+static MYSQLND_REVERSE_API pdo_mysql_reverse_api = {
+ &pdo_mysql_module_entry,
+ pdo_mysql_convert_zv_to_mysqlnd
+};
+#endif
+
+
+/* {{{ PHP_INI_BEGIN
+*/
+PHP_INI_BEGIN()
+#ifndef PHP_WIN32
+ STD_PHP_INI_ENTRY("pdo_mysql.default_socket", PDO_MYSQL_UNIX_ADDR, PHP_INI_SYSTEM, OnUpdateStringUnempty, default_socket, zend_pdo_mysql_globals, pdo_mysql_globals)
+#endif
+#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
+PHP_INI_END()
+/* }}} */
+
+/* true global environment */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(pdo_mysql)
+{
+ REGISTER_INI_ENTRIES();
+
+ 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);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_INIT_COMMAND", (long)PDO_MYSQL_ATTR_INIT_COMMAND);
+#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_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_COMPRESS", (long)PDO_MYSQL_ATTR_COMPRESS);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_DIRECT_QUERY", (long)PDO_MYSQL_ATTR_DIRECT_QUERY);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_FOUND_ROWS", (long)PDO_MYSQL_ATTR_FOUND_ROWS);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_IGNORE_SPACE", (long)PDO_MYSQL_ATTR_IGNORE_SPACE);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_KEY", (long)PDO_MYSQL_ATTR_SSL_KEY);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CERT", (long)PDO_MYSQL_ATTR_SSL_CERT);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CA", (long)PDO_MYSQL_ATTR_SSL_CA);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CAPATH", (long)PDO_MYSQL_ATTR_SSL_CAPATH);
+ REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CIPHER", (long)PDO_MYSQL_ATTR_SSL_CIPHER);
+
+#ifdef PDO_USE_MYSQLND
+ mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api TSRMLS_CC);
+#endif
+
+ return php_pdo_register_driver(&pdo_mysql_driver);
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+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
+ */
+static PHP_MINFO_FUNCTION(pdo_mysql)
+{
+ php_info_print_table_start();
+
+ php_info_print_table_header(2, "PDO Driver for MySQL", "enabled");
+ php_info_print_table_row(2, "Client API version", mysql_get_client_info());
+
+ php_info_print_table_end();
+
+#ifndef PHP_WIN32
+ DISPLAY_INI_ENTRIES();
+#endif
+}
+/* }}} */
+
+
+#if PDO_USE_MYSQLND && PDO_DBG_ENABLED
+/* {{{ PHP_RINIT_FUNCTION
+ */
+static PHP_RINIT_FUNCTION(pdo_mysql)
+{
+ if (PDO_MYSQL_G(debug)) {
+ MYSQLND_DEBUG *dbg = mysqlnd_debug_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
+ if (!dbg) {
+ return FAILURE;
+ }
+ dbg->m->set_mode(dbg, PDO_MYSQL_G(debug));
+ PDO_MYSQL_G(dbg) = dbg;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+static PHP_RSHUTDOWN_FUNCTION(pdo_mysql)
+{
+ 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;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+#endif
+
+/* {{{ PHP_GINIT_FUNCTION
+ */
+static PHP_GINIT_FUNCTION(pdo_mysql)
+{
+#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
+}
+/* }}} */
+
+/* {{{ pdo_mysql_functions[] */
+const zend_function_entry pdo_mysql_functions[] = {
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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
+ ZEND_MOD_END
+};
+#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 && PDO_DBG_ENABLED
+ PHP_RINIT(pdo_mysql),
+ PHP_RSHUTDOWN(pdo_mysql),
+#else
+ NULL,
+ NULL,
+#endif
+ PHP_MINFO(pdo_mysql),
+ "1.0.2",
+ PHP_MODULE_GLOBALS(pdo_mysql),
+ PHP_GINIT(pdo_mysql),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/pdo_mysql/php_pdo_mysql.h b/ext/pdo_mysql/php_pdo_mysql.h
new file mode 100644
index 0000000..e4493b5
--- /dev/null
+++ b/ext/pdo_mysql/php_pdo_mysql.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Schlossnagle <george@omniti.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef PHP_PDO_MYSQL_H
+#define PHP_PDO_MYSQL_H
+
+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
+
+
+#endif /* PHP_PDO_MYSQL_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h
new file mode 100644
index 0000000..65d7435
--- /dev/null
+++ b/ext/pdo_mysql/php_pdo_mysql_int.h
@@ -0,0 +1,176 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Schlossnagle <george@omniti.com> |
+ | Wez Furlong <wez@php.net> |
+ | Johannes Schlueter <johannes@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef PHP_PDO_MYSQL_INT_H
+#define PHP_PDO_MYSQL_INT_H
+
+#if defined(PDO_USE_MYSQLND)
+# include "ext/mysqlnd/mysqlnd.h"
+# include "ext/mysqlnd/mysqlnd_libmysql_compat.h"
+# define PDO_MYSQL_PARAM_BIND MYSQLND_PARAM_BIND
+#else
+# include <mysql.h>
+# define PDO_MYSQL_PARAM_BIND MYSQL_BIND
+#endif
+
+#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007 || defined(MYSQL_USE_MYSQLND)
+# define PDO_MYSQL_HAS_CHARSET
+#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__, 0); 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__, 0); 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
+
+ZEND_BEGIN_MODULE_GLOBALS(pdo_mysql)
+#ifndef PHP_WIN32
+ char *default_socket;
+#endif
+#if PDO_DBG_ENABLED
+ char *debug; /* The actual string */
+ MYSQLND_DEBUG *dbg; /* The DBG object */
+#endif
+#if defined(PHP_WIN32) && !PDO_DBG_ENABLED
+ /* dummy member so we get at least one member in the struct
+ * and avoids build errors.
+ */
+ void *dummymemmber;
+#endif
+ZEND_END_MODULE_GLOBALS(pdo_mysql)
+
+ZEND_EXTERN_MODULE_GLOBALS(pdo_mysql)
+
+#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;
+ int line;
+ unsigned int errcode;
+ char *errmsg;
+} pdo_mysql_error_info;
+
+/* stuff we use in a mySQL database handle */
+typedef struct {
+ MYSQL *server;
+
+ unsigned attached:1;
+ unsigned buffered:1;
+ unsigned emulate_prepare:1;
+ 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;
+
+typedef struct {
+ MYSQL_FIELD *def;
+} pdo_mysql_column;
+
+typedef struct {
+ pdo_mysql_db_handle *H;
+ MYSQL_RES *result;
+ 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 PDO_USE_MYSQLND
+ MYSQLND_STMT *stmt;
+#else
+ MYSQL_STMT *stmt;
+#endif
+ int num_params;
+ PDO_MYSQL_PARAM_BIND *params;
+#ifndef PDO_USE_MYSQLND
+ my_bool *in_null;
+ unsigned long *in_length;
+#endif
+ PDO_MYSQL_PARAM_BIND *bound_result;
+ my_bool *out_null;
+ unsigned long *out_length;
+ unsigned int params_given;
+ unsigned max_length:1;
+} pdo_mysql_stmt;
+
+extern pdo_driver_t pdo_mysql_driver;
+
+extern int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line TSRMLS_DC);
+#define pdo_mysql_error(s) _pdo_mysql_error(s, NULL, __FILE__, __LINE__ TSRMLS_CC)
+#define pdo_mysql_error_stmt(s) _pdo_mysql_error(stmt->dbh, stmt, __FILE__, __LINE__ TSRMLS_CC)
+
+extern struct pdo_stmt_methods mysql_stmt_methods;
+
+enum {
+ PDO_MYSQL_ATTR_USE_BUFFERED_QUERY = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_MYSQL_ATTR_LOCAL_INFILE,
+ PDO_MYSQL_ATTR_INIT_COMMAND,
+#ifndef PDO_USE_MYSQLND
+ PDO_MYSQL_ATTR_READ_DEFAULT_FILE,
+ PDO_MYSQL_ATTR_READ_DEFAULT_GROUP,
+ PDO_MYSQL_ATTR_MAX_BUFFER_SIZE,
+#endif
+ PDO_MYSQL_ATTR_COMPRESS,
+ PDO_MYSQL_ATTR_DIRECT_QUERY,
+ PDO_MYSQL_ATTR_FOUND_ROWS,
+ PDO_MYSQL_ATTR_IGNORE_SPACE,
+ PDO_MYSQL_ATTR_SSL_KEY,
+ PDO_MYSQL_ATTR_SSL_CERT,
+ PDO_MYSQL_ATTR_SSL_CA,
+ PDO_MYSQL_ATTR_SSL_CAPATH,
+ PDO_MYSQL_ATTR_SSL_CIPHER
+};
+
+#endif
diff --git a/ext/pdo_mysql/php_pdo_mysql_sqlstate.h b/ext/pdo_mysql/php_pdo_mysql_sqlstate.h
new file mode 100644
index 0000000..70fc95e
--- /dev/null
+++ b/ext/pdo_mysql/php_pdo_mysql_sqlstate.h
@@ -0,0 +1,646 @@
+/* DO NOT EDIT THIS FILE!!! It is auto generated by get_error_codes.php */
+#ifdef ER_DUP_KEY
+ case ER_DUP_KEY : return "23000";
+#endif
+#ifdef ER_OUTOFMEMORY
+ case ER_OUTOFMEMORY : return "HY001";
+#endif
+#ifdef ER_OUT_OF_SORTMEMORY
+ case ER_OUT_OF_SORTMEMORY : return "HY001";
+#endif
+#ifdef ER_CON_COUNT_ERROR
+ case ER_CON_COUNT_ERROR : return "08004";
+#endif
+#ifdef ER_BAD_HOST_ERROR
+ case ER_BAD_HOST_ERROR : return "08S01";
+#endif
+#ifdef ER_HANDSHAKE_ERROR
+ case ER_HANDSHAKE_ERROR : return "08S01";
+#endif
+#ifdef ER_DBACCESS_DENIED_ERROR
+ case ER_DBACCESS_DENIED_ERROR : return "42000";
+#endif
+#ifdef ER_ACCESS_DENIED_ERROR
+ case ER_ACCESS_DENIED_ERROR : return "28000";
+#endif
+#ifdef ER_NO_DB_ERROR
+ case ER_NO_DB_ERROR : return "3D000";
+#endif
+#ifdef ER_UNKNOWN_COM_ERROR
+ case ER_UNKNOWN_COM_ERROR : return "08S01";
+#endif
+#ifdef ER_BAD_NULL_ERROR
+ case ER_BAD_NULL_ERROR : return "23000";
+#endif
+#ifdef ER_BAD_DB_ERROR
+ case ER_BAD_DB_ERROR : return "42000";
+#endif
+#ifdef ER_TABLE_EXISTS_ERROR
+ case ER_TABLE_EXISTS_ERROR : return "42S01";
+#endif
+#ifdef ER_BAD_TABLE_ERROR
+ case ER_BAD_TABLE_ERROR : return "42S02";
+#endif
+#ifdef ER_NON_UNIQ_ERROR
+ case ER_NON_UNIQ_ERROR : return "23000";
+#endif
+#ifdef ER_SERVER_SHUTDOWN
+ case ER_SERVER_SHUTDOWN : return "08S01";
+#endif
+#ifdef ER_BAD_FIELD_ERROR
+ case ER_BAD_FIELD_ERROR : return "42S22";
+#endif
+#ifdef ER_WRONG_FIELD_WITH_GROUP
+ case ER_WRONG_FIELD_WITH_GROUP : return "42000";
+#endif
+#ifdef ER_WRONG_GROUP_FIELD
+ case ER_WRONG_GROUP_FIELD : return "42000";
+#endif
+#ifdef ER_WRONG_SUM_SELECT
+ case ER_WRONG_SUM_SELECT : return "42000";
+#endif
+#ifdef ER_WRONG_VALUE_COUNT
+ case ER_WRONG_VALUE_COUNT : return "21S01";
+#endif
+#ifdef ER_TOO_LONG_IDENT
+ case ER_TOO_LONG_IDENT : return "42000";
+#endif
+#ifdef ER_DUP_FIELDNAME
+ case ER_DUP_FIELDNAME : return "42S21";
+#endif
+#ifdef ER_DUP_KEYNAME
+ case ER_DUP_KEYNAME : return "42000";
+#endif
+#ifdef ER_DUP_ENTRY
+ case ER_DUP_ENTRY : return "23000";
+#endif
+#ifdef ER_WRONG_FIELD_SPEC
+ case ER_WRONG_FIELD_SPEC : return "42000";
+#endif
+#ifdef ER_PARSE_ERROR
+ case ER_PARSE_ERROR : return "42000";
+#endif
+#ifdef ER_EMPTY_QUERY
+ case ER_EMPTY_QUERY : return "42000";
+#endif
+#ifdef ER_NONUNIQ_TABLE
+ case ER_NONUNIQ_TABLE : return "42000";
+#endif
+#ifdef ER_INVALID_DEFAULT
+ case ER_INVALID_DEFAULT : return "42000";
+#endif
+#ifdef ER_MULTIPLE_PRI_KEY
+ case ER_MULTIPLE_PRI_KEY : return "42000";
+#endif
+#ifdef ER_TOO_MANY_KEYS
+ case ER_TOO_MANY_KEYS : return "42000";
+#endif
+#ifdef ER_TOO_MANY_KEY_PARTS
+ case ER_TOO_MANY_KEY_PARTS : return "42000";
+#endif
+#ifdef ER_TOO_LONG_KEY
+ case ER_TOO_LONG_KEY : return "42000";
+#endif
+#ifdef ER_KEY_COLUMN_DOES_NOT_EXITS
+ case ER_KEY_COLUMN_DOES_NOT_EXITS : return "42000";
+#endif
+#ifdef ER_BLOB_USED_AS_KEY
+ case ER_BLOB_USED_AS_KEY : return "42000";
+#endif
+#ifdef ER_TOO_BIG_FIELDLENGTH
+ case ER_TOO_BIG_FIELDLENGTH : return "42000";
+#endif
+#ifdef ER_WRONG_AUTO_KEY
+ case ER_WRONG_AUTO_KEY : return "42000";
+#endif
+#ifdef ER_FORCING_CLOSE
+ case ER_FORCING_CLOSE : return "08S01";
+#endif
+#ifdef ER_IPSOCK_ERROR
+ case ER_IPSOCK_ERROR : return "08S01";
+#endif
+#ifdef ER_NO_SUCH_INDEX
+ case ER_NO_SUCH_INDEX : return "42S12";
+#endif
+#ifdef ER_WRONG_FIELD_TERMINATORS
+ case ER_WRONG_FIELD_TERMINATORS : return "42000";
+#endif
+#ifdef ER_BLOBS_AND_NO_TERMINATED
+ case ER_BLOBS_AND_NO_TERMINATED : return "42000";
+#endif
+#ifdef ER_CANT_REMOVE_ALL_FIELDS
+ case ER_CANT_REMOVE_ALL_FIELDS : return "42000";
+#endif
+#ifdef ER_CANT_DROP_FIELD_OR_KEY
+ case ER_CANT_DROP_FIELD_OR_KEY : return "42000";
+#endif
+#ifdef ER_BLOB_CANT_HAVE_DEFAULT
+ case ER_BLOB_CANT_HAVE_DEFAULT : return "42000";
+#endif
+#ifdef ER_WRONG_DB_NAME
+ case ER_WRONG_DB_NAME : return "42000";
+#endif
+#ifdef ER_WRONG_TABLE_NAME
+ case ER_WRONG_TABLE_NAME : return "42000";
+#endif
+#ifdef ER_TOO_BIG_SELECT
+ case ER_TOO_BIG_SELECT : return "42000";
+#endif
+#ifdef ER_UNKNOWN_PROCEDURE
+ case ER_UNKNOWN_PROCEDURE : return "42000";
+#endif
+#ifdef ER_WRONG_PARAMCOUNT_TO_PROCEDURE
+ case ER_WRONG_PARAMCOUNT_TO_PROCEDURE : return "42000";
+#endif
+#ifdef ER_UNKNOWN_TABLE
+ case ER_UNKNOWN_TABLE : return "42S02";
+#endif
+#ifdef ER_FIELD_SPECIFIED_TWICE
+ case ER_FIELD_SPECIFIED_TWICE : return "42000";
+#endif
+#ifdef ER_UNSUPPORTED_EXTENSION
+ case ER_UNSUPPORTED_EXTENSION : return "42000";
+#endif
+#ifdef ER_TABLE_MUST_HAVE_COLUMNS
+ case ER_TABLE_MUST_HAVE_COLUMNS : return "42000";
+#endif
+#ifdef ER_UNKNOWN_CHARACTER_SET
+ case ER_UNKNOWN_CHARACTER_SET : return "42000";
+#endif
+#ifdef ER_TOO_BIG_ROWSIZE
+ case ER_TOO_BIG_ROWSIZE : return "42000";
+#endif
+#ifdef ER_WRONG_OUTER_JOIN
+ case ER_WRONG_OUTER_JOIN : return "42000";
+#endif
+#ifdef ER_NULL_COLUMN_IN_INDEX
+ case ER_NULL_COLUMN_IN_INDEX : return "42000";
+#endif
+#ifdef ER_PASSWORD_ANONYMOUS_USER
+ case ER_PASSWORD_ANONYMOUS_USER : return "42000";
+#endif
+#ifdef ER_PASSWORD_NOT_ALLOWED
+ case ER_PASSWORD_NOT_ALLOWED : return "42000";
+#endif
+#ifdef ER_PASSWORD_NO_MATCH
+ case ER_PASSWORD_NO_MATCH : return "42000";
+#endif
+#ifdef ER_WRONG_VALUE_COUNT_ON_ROW
+ case ER_WRONG_VALUE_COUNT_ON_ROW : return "21S01";
+#endif
+#ifdef ER_INVALID_USE_OF_NULL
+ case ER_INVALID_USE_OF_NULL : return "22004";
+#endif
+#ifdef ER_REGEXP_ERROR
+ case ER_REGEXP_ERROR : return "42000";
+#endif
+#ifdef ER_MIX_OF_GROUP_FUNC_AND_FIELDS
+ case ER_MIX_OF_GROUP_FUNC_AND_FIELDS : return "42000";
+#endif
+#ifdef ER_NONEXISTING_GRANT
+ case ER_NONEXISTING_GRANT : return "42000";
+#endif
+#ifdef ER_TABLEACCESS_DENIED_ERROR
+ case ER_TABLEACCESS_DENIED_ERROR : return "42000";
+#endif
+#ifdef ER_COLUMNACCESS_DENIED_ERROR
+ case ER_COLUMNACCESS_DENIED_ERROR : return "42000";
+#endif
+#ifdef ER_ILLEGAL_GRANT_FOR_TABLE
+ case ER_ILLEGAL_GRANT_FOR_TABLE : return "42000";
+#endif
+#ifdef ER_GRANT_WRONG_HOST_OR_USER
+ case ER_GRANT_WRONG_HOST_OR_USER : return "42000";
+#endif
+#ifdef ER_NO_SUCH_TABLE
+ case ER_NO_SUCH_TABLE : return "42S02";
+#endif
+#ifdef ER_NONEXISTING_TABLE_GRANT
+ case ER_NONEXISTING_TABLE_GRANT : return "42000";
+#endif
+#ifdef ER_NOT_ALLOWED_COMMAND
+ case ER_NOT_ALLOWED_COMMAND : return "42000";
+#endif
+#ifdef ER_SYNTAX_ERROR
+ case ER_SYNTAX_ERROR : return "42000";
+#endif
+#ifdef ER_ABORTING_CONNECTION
+ case ER_ABORTING_CONNECTION : return "08S01";
+#endif
+#ifdef ER_NET_PACKET_TOO_LARGE
+ case ER_NET_PACKET_TOO_LARGE : return "08S01";
+#endif
+#ifdef ER_NET_READ_ERROR_FROM_PIPE
+ case ER_NET_READ_ERROR_FROM_PIPE : return "08S01";
+#endif
+#ifdef ER_NET_FCNTL_ERROR
+ case ER_NET_FCNTL_ERROR : return "08S01";
+#endif
+#ifdef ER_NET_PACKETS_OUT_OF_ORDER
+ case ER_NET_PACKETS_OUT_OF_ORDER : return "08S01";
+#endif
+#ifdef ER_NET_UNCOMPRESS_ERROR
+ case ER_NET_UNCOMPRESS_ERROR : return "08S01";
+#endif
+#ifdef ER_NET_READ_ERROR
+ case ER_NET_READ_ERROR : return "08S01";
+#endif
+#ifdef ER_NET_READ_INTERRUPTED
+ case ER_NET_READ_INTERRUPTED : return "08S01";
+#endif
+#ifdef ER_NET_ERROR_ON_WRITE
+ case ER_NET_ERROR_ON_WRITE : return "08S01";
+#endif
+#ifdef ER_NET_WRITE_INTERRUPTED
+ case ER_NET_WRITE_INTERRUPTED : return "08S01";
+#endif
+#ifdef ER_TOO_LONG_STRING
+ case ER_TOO_LONG_STRING : return "42000";
+#endif
+#ifdef ER_TABLE_CANT_HANDLE_BLOB
+ case ER_TABLE_CANT_HANDLE_BLOB : return "42000";
+#endif
+#ifdef ER_TABLE_CANT_HANDLE_AUTO_INCREMENT
+ case ER_TABLE_CANT_HANDLE_AUTO_INCREMENT : return "42000";
+#endif
+#ifdef ER_WRONG_COLUMN_NAME
+ case ER_WRONG_COLUMN_NAME : return "42000";
+#endif
+#ifdef ER_WRONG_KEY_COLUMN
+ case ER_WRONG_KEY_COLUMN : return "42000";
+#endif
+#ifdef ER_DUP_UNIQUE
+ case ER_DUP_UNIQUE : return "23000";
+#endif
+#ifdef ER_BLOB_KEY_WITHOUT_LENGTH
+ case ER_BLOB_KEY_WITHOUT_LENGTH : return "42000";
+#endif
+#ifdef ER_PRIMARY_CANT_HAVE_NULL
+ case ER_PRIMARY_CANT_HAVE_NULL : return "42000";
+#endif
+#ifdef ER_TOO_MANY_ROWS
+ case ER_TOO_MANY_ROWS : return "42000";
+#endif
+#ifdef ER_REQUIRES_PRIMARY_KEY
+ case ER_REQUIRES_PRIMARY_KEY : return "42000";
+#endif
+#ifdef ER_KEY_DOES_NOT_EXITS
+ case ER_KEY_DOES_NOT_EXITS : return "42000";
+#endif
+#ifdef ER_CHECK_NO_SUCH_TABLE
+ case ER_CHECK_NO_SUCH_TABLE : return "42000";
+#endif
+#ifdef ER_CHECK_NOT_IMPLEMENTED
+ case ER_CHECK_NOT_IMPLEMENTED : return "42000";
+#endif
+#ifdef ER_CANT_DO_THIS_DURING_AN_TRANSACTION
+ case ER_CANT_DO_THIS_DURING_AN_TRANSACTION : return "25000";
+#endif
+#ifdef ER_NEW_ABORTING_CONNECTION
+ case ER_NEW_ABORTING_CONNECTION : return "08S01";
+#endif
+#ifdef ER_MASTER_NET_READ
+ case ER_MASTER_NET_READ : return "08S01";
+#endif
+#ifdef ER_MASTER_NET_WRITE
+ case ER_MASTER_NET_WRITE : return "08S01";
+#endif
+#ifdef ER_TOO_MANY_USER_CONNECTIONS
+ case ER_TOO_MANY_USER_CONNECTIONS : return "42000";
+#endif
+#ifdef ER_READ_ONLY_TRANSACTION
+ case ER_READ_ONLY_TRANSACTION : return "25000";
+#endif
+#ifdef ER_NO_PERMISSION_TO_CREATE_USER
+ case ER_NO_PERMISSION_TO_CREATE_USER : return "42000";
+#endif
+#ifdef ER_LOCK_DEADLOCK
+ case ER_LOCK_DEADLOCK : return "40001";
+#endif
+#ifdef ER_NO_REFERENCED_ROW
+ case ER_NO_REFERENCED_ROW : return "23000";
+#endif
+#ifdef ER_ROW_IS_REFERENCED
+ case ER_ROW_IS_REFERENCED : return "23000";
+#endif
+#ifdef ER_CONNECT_TO_MASTER
+ case ER_CONNECT_TO_MASTER : return "08S01";
+#endif
+#ifdef ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT
+ case ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT : return "21000";
+#endif
+#ifdef ER_USER_LIMIT_REACHED
+ case ER_USER_LIMIT_REACHED : return "42000";
+#endif
+#ifdef ER_SPECIFIC_ACCESS_DENIED_ERROR
+ case ER_SPECIFIC_ACCESS_DENIED_ERROR : return "42000";
+#endif
+#ifdef ER_NO_DEFAULT
+ case ER_NO_DEFAULT : return "42000";
+#endif
+#ifdef ER_WRONG_VALUE_FOR_VAR
+ case ER_WRONG_VALUE_FOR_VAR : return "42000";
+#endif
+#ifdef ER_WRONG_TYPE_FOR_VAR
+ case ER_WRONG_TYPE_FOR_VAR : return "42000";
+#endif
+#ifdef ER_CANT_USE_OPTION_HERE
+ case ER_CANT_USE_OPTION_HERE : return "42000";
+#endif
+#ifdef ER_NOT_SUPPORTED_YET
+ case ER_NOT_SUPPORTED_YET : return "42000";
+#endif
+#ifdef ER_WRONG_FK_DEF
+ case ER_WRONG_FK_DEF : return "42000";
+#endif
+#ifdef ER_OPERAND_COLUMNS
+ case ER_OPERAND_COLUMNS : return "21000";
+#endif
+#ifdef ER_SUBQUERY_NO_1_ROW
+ case ER_SUBQUERY_NO_1_ROW : return "21000";
+#endif
+#ifdef ER_ILLEGAL_REFERENCE
+ case ER_ILLEGAL_REFERENCE : return "42S22";
+#endif
+#ifdef ER_DERIVED_MUST_HAVE_ALIAS
+ case ER_DERIVED_MUST_HAVE_ALIAS : return "42000";
+#endif
+#ifdef ER_SELECT_REDUCED
+ case ER_SELECT_REDUCED : return "01000";
+#endif
+#ifdef ER_TABLENAME_NOT_ALLOWED_HERE
+ case ER_TABLENAME_NOT_ALLOWED_HERE : return "42000";
+#endif
+#ifdef ER_NOT_SUPPORTED_AUTH_MODE
+ case ER_NOT_SUPPORTED_AUTH_MODE : return "08004";
+#endif
+#ifdef ER_SPATIAL_CANT_HAVE_NULL
+ case ER_SPATIAL_CANT_HAVE_NULL : return "42000";
+#endif
+#ifdef ER_COLLATION_CHARSET_MISMATCH
+ case ER_COLLATION_CHARSET_MISMATCH : return "42000";
+#endif
+#ifdef ER_WARN_TOO_FEW_RECORDS
+ case ER_WARN_TOO_FEW_RECORDS : return "01000";
+#endif
+#ifdef ER_WARN_TOO_MANY_RECORDS
+ case ER_WARN_TOO_MANY_RECORDS : return "01000";
+#endif
+#ifdef ER_WARN_NULL_TO_NOTNULL
+ case ER_WARN_NULL_TO_NOTNULL : return "22004";
+#endif
+#ifdef ER_WARN_DATA_OUT_OF_RANGE
+ case ER_WARN_DATA_OUT_OF_RANGE : return "22003";
+#endif
+#ifdef ER_WRONG_NAME_FOR_INDEX
+ case ER_WRONG_NAME_FOR_INDEX : return "42000";
+#endif
+#ifdef ER_WRONG_NAME_FOR_CATALOG
+ case ER_WRONG_NAME_FOR_CATALOG : return "42000";
+#endif
+#ifdef ER_UNKNOWN_STORAGE_ENGINE
+ case ER_UNKNOWN_STORAGE_ENGINE : return "42000";
+#endif
+#ifdef ER_TRUNCATED_WRONG_VALUE
+ case ER_TRUNCATED_WRONG_VALUE : return "22007";
+#endif
+#ifdef ER_SP_NO_RECURSIVE_CREATE
+ case ER_SP_NO_RECURSIVE_CREATE : return "2F003";
+#endif
+#ifdef ER_SP_ALREADY_EXISTS
+ case ER_SP_ALREADY_EXISTS : return "42000";
+#endif
+#ifdef ER_SP_DOES_NOT_EXIST
+ case ER_SP_DOES_NOT_EXIST : return "42000";
+#endif
+#ifdef ER_SP_LILABEL_MISMATCH
+ case ER_SP_LILABEL_MISMATCH : return "42000";
+#endif
+#ifdef ER_SP_LABEL_REDEFINE
+ case ER_SP_LABEL_REDEFINE : return "42000";
+#endif
+#ifdef ER_SP_LABEL_MISMATCH
+ case ER_SP_LABEL_MISMATCH : return "42000";
+#endif
+#ifdef ER_SP_UNINIT_VAR
+ case ER_SP_UNINIT_VAR : return "01000";
+#endif
+#ifdef ER_SP_BADSELECT
+ case ER_SP_BADSELECT : return "0A000";
+#endif
+#ifdef ER_SP_BADRETURN
+ case ER_SP_BADRETURN : return "42000";
+#endif
+#ifdef ER_SP_BADSTATEMENT
+ case ER_SP_BADSTATEMENT : return "0A000";
+#endif
+#ifdef ER_UPDATE_LOG_DEPRECATED_IGNORED
+ case ER_UPDATE_LOG_DEPRECATED_IGNORED : return "42000";
+#endif
+#ifdef ER_UPDATE_LOG_DEPRECATED_TRANSLATED
+ case ER_UPDATE_LOG_DEPRECATED_TRANSLATED : return "42000";
+#endif
+#ifdef ER_QUERY_INTERRUPTED
+ case ER_QUERY_INTERRUPTED : return "70100";
+#endif
+#ifdef ER_SP_WRONG_NO_OF_ARGS
+ case ER_SP_WRONG_NO_OF_ARGS : return "42000";
+#endif
+#ifdef ER_SP_COND_MISMATCH
+ case ER_SP_COND_MISMATCH : return "42000";
+#endif
+#ifdef ER_SP_NORETURN
+ case ER_SP_NORETURN : return "42000";
+#endif
+#ifdef ER_SP_NORETURNEND
+ case ER_SP_NORETURNEND : return "2F005";
+#endif
+#ifdef ER_SP_BAD_CURSOR_QUERY
+ case ER_SP_BAD_CURSOR_QUERY : return "42000";
+#endif
+#ifdef ER_SP_BAD_CURSOR_SELECT
+ case ER_SP_BAD_CURSOR_SELECT : return "42000";
+#endif
+#ifdef ER_SP_CURSOR_MISMATCH
+ case ER_SP_CURSOR_MISMATCH : return "42000";
+#endif
+#ifdef ER_SP_CURSOR_ALREADY_OPEN
+ case ER_SP_CURSOR_ALREADY_OPEN : return "24000";
+#endif
+#ifdef ER_SP_CURSOR_NOT_OPEN
+ case ER_SP_CURSOR_NOT_OPEN : return "24000";
+#endif
+#ifdef ER_SP_UNDECLARED_VAR
+ case ER_SP_UNDECLARED_VAR : return "42000";
+#endif
+#ifdef ER_SP_FETCH_NO_DATA
+ case ER_SP_FETCH_NO_DATA : return "02000";
+#endif
+#ifdef ER_SP_DUP_PARAM
+ case ER_SP_DUP_PARAM : return "42000";
+#endif
+#ifdef ER_SP_DUP_VAR
+ case ER_SP_DUP_VAR : return "42000";
+#endif
+#ifdef ER_SP_DUP_COND
+ case ER_SP_DUP_COND : return "42000";
+#endif
+#ifdef ER_SP_DUP_CURS
+ case ER_SP_DUP_CURS : return "42000";
+#endif
+#ifdef ER_SP_SUBSELECT_NYI
+ case ER_SP_SUBSELECT_NYI : return "0A000";
+#endif
+#ifdef ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+ case ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG : return "0A000";
+#endif
+#ifdef ER_SP_VARCOND_AFTER_CURSHNDLR
+ case ER_SP_VARCOND_AFTER_CURSHNDLR : return "42000";
+#endif
+#ifdef ER_SP_CURSOR_AFTER_HANDLER
+ case ER_SP_CURSOR_AFTER_HANDLER : return "42000";
+#endif
+#ifdef ER_SP_CASE_NOT_FOUND
+ case ER_SP_CASE_NOT_FOUND : return "20000";
+#endif
+#ifdef ER_DIVISION_BY_ZERO
+ case ER_DIVISION_BY_ZERO : return "22012";
+#endif
+#ifdef ER_ILLEGAL_VALUE_FOR_TYPE
+ case ER_ILLEGAL_VALUE_FOR_TYPE : return "22007";
+#endif
+#ifdef ER_PROCACCESS_DENIED_ERROR
+ case ER_PROCACCESS_DENIED_ERROR : return "42000";
+#endif
+#ifdef ER_XAER_NOTA
+ case ER_XAER_NOTA : return "XAE04";
+#endif
+#ifdef ER_XAER_INVAL
+ case ER_XAER_INVAL : return "XAE05";
+#endif
+#ifdef ER_XAER_RMFAIL
+ case ER_XAER_RMFAIL : return "XAE07";
+#endif
+#ifdef ER_XAER_OUTSIDE
+ case ER_XAER_OUTSIDE : return "XAE09";
+#endif
+#ifdef ER_XAER_RMERR
+ case ER_XAER_RMERR : return "XAE03";
+#endif
+#ifdef ER_XA_RBROLLBACK
+ case ER_XA_RBROLLBACK : return "XA100";
+#endif
+#ifdef ER_NONEXISTING_PROC_GRANT
+ case ER_NONEXISTING_PROC_GRANT : return "42000";
+#endif
+#ifdef ER_DATA_TOO_LONG
+ case ER_DATA_TOO_LONG : return "22001";
+#endif
+#ifdef ER_SP_BAD_SQLSTATE
+ case ER_SP_BAD_SQLSTATE : return "42000";
+#endif
+#ifdef ER_CANT_CREATE_USER_WITH_GRANT
+ case ER_CANT_CREATE_USER_WITH_GRANT : return "42000";
+#endif
+#ifdef ER_SP_DUP_HANDLER
+ case ER_SP_DUP_HANDLER : return "42000";
+#endif
+#ifdef ER_SP_NOT_VAR_ARG
+ case ER_SP_NOT_VAR_ARG : return "42000";
+#endif
+#ifdef ER_SP_NO_RETSET
+ case ER_SP_NO_RETSET : return "0A000";
+#endif
+#ifdef ER_CANT_CREATE_GEOMETRY_OBJECT
+ case ER_CANT_CREATE_GEOMETRY_OBJECT : return "22003";
+#endif
+#ifdef ER_TOO_BIG_SCALE
+ case ER_TOO_BIG_SCALE : return "42000";
+#endif
+#ifdef ER_TOO_BIG_PRECISION
+ case ER_TOO_BIG_PRECISION : return "42000";
+#endif
+#ifdef ER_M_BIGGER_THAN_D
+ case ER_M_BIGGER_THAN_D : return "42000";
+#endif
+#ifdef ER_TOO_LONG_BODY
+ case ER_TOO_LONG_BODY : return "42000";
+#endif
+#ifdef ER_TOO_BIG_DISPLAYWIDTH
+ case ER_TOO_BIG_DISPLAYWIDTH : return "42000";
+#endif
+#ifdef ER_XAER_DUPID
+ case ER_XAER_DUPID : return "XAE08";
+#endif
+#ifdef ER_DATETIME_FUNCTION_OVERFLOW
+ case ER_DATETIME_FUNCTION_OVERFLOW : return "22008";
+#endif
+#ifdef ER_ROW_IS_REFERENCED_2
+ case ER_ROW_IS_REFERENCED_2 : return "23000";
+#endif
+#ifdef ER_NO_REFERENCED_ROW_2
+ case ER_NO_REFERENCED_ROW_2 : return "23000";
+#endif
+#ifdef ER_SP_BAD_VAR_SHADOW
+ case ER_SP_BAD_VAR_SHADOW : return "42000";
+#endif
+#ifdef ER_SP_WRONG_NAME
+ case ER_SP_WRONG_NAME : return "42000";
+#endif
+#ifdef ER_SP_NO_AGGREGATE
+ case ER_SP_NO_AGGREGATE : return "42000";
+#endif
+#ifdef ER_MAX_PREPARED_STMT_COUNT_REACHED
+ case ER_MAX_PREPARED_STMT_COUNT_REACHED : return "42000";
+#endif
+#ifdef ER_NON_GROUPING_FIELD_USED
+ case ER_NON_GROUPING_FIELD_USED : return "42000";
+#endif
+#ifdef ER_FOREIGN_DUPLICATE_KEY
+ case ER_FOREIGN_DUPLICATE_KEY : return "23000";
+#endif
+#ifdef ER_CANT_CHANGE_TX_ISOLATION
+ case ER_CANT_CHANGE_TX_ISOLATION : return "25001";
+#endif
+#ifdef ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+ case ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT : return "42000";
+#endif
+#ifdef ER_WRONG_PARAMETERS_TO_NATIVE_FCT
+ case ER_WRONG_PARAMETERS_TO_NATIVE_FCT : return "42000";
+#endif
+#ifdef ER_WRONG_PARAMETERS_TO_STORED_FCT
+ case ER_WRONG_PARAMETERS_TO_STORED_FCT : return "42000";
+#endif
+#ifdef ER_DUP_ENTRY_WITH_KEY_NAME
+ case ER_DUP_ENTRY_WITH_KEY_NAME : return "23000";
+#endif
+#ifdef ER_XA_RBTIMEOUT
+ case ER_XA_RBTIMEOUT : return "XA106";
+#endif
+#ifdef ER_XA_RBDEADLOCK
+ case ER_XA_RBDEADLOCK : return "XA102";
+#endif
+#ifdef ER_FUNC_INEXISTENT_NAME_COLLISION
+ case ER_FUNC_INEXISTENT_NAME_COLLISION : return "42000";
+#endif
+#ifdef ER_DUP_SIGNAL_SET
+ case ER_DUP_SIGNAL_SET : return "42000";
+#endif
+#ifdef ER_SIGNAL_WARN
+ case ER_SIGNAL_WARN : return "01000";
+#endif
+#ifdef ER_SIGNAL_NOT_FOUND
+ case ER_SIGNAL_NOT_FOUND : return "02000";
+#endif
+#ifdef ER_SIGNAL_EXCEPTION
+ case ER_SIGNAL_EXCEPTION : return "HY000";
+#endif
+#ifdef ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
+ case ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER : return "0K000";
+#endif
+#ifdef ER_SPATIAL_MUST_HAVE_GEOM_COL
+ case ER_SPATIAL_MUST_HAVE_GEOM_COL : return "42000";
+#endif
+#ifdef ER_DATA_OUT_OF_RANGE
+ case ER_DATA_OUT_OF_RANGE : return "22003";
+#endif
diff --git a/ext/pdo_mysql/tests/README b/ext/pdo_mysql/tests/README
new file mode 100644
index 0000000..920f5ef
--- /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/bug41125.phpt b/ext/pdo_mysql/tests/bug41125.phpt
new file mode 100644
index 0000000..cd913bb
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug41125.phpt
@@ -0,0 +1,162 @@
+--TEST--
+Bug #41125 (PDO mysql + quote() + prepare() can result in seg fault)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$search = "o'";
+$sql = "SELECT 1 FROM DUAL WHERE 'o''riley' LIKE " . $db->quote('%' . $search . '%');
+$stmt = $db->prepare($sql);
+$stmt->execute();
+print implode(' - ', (($r = @$stmt->fetch(PDO::FETCH_NUM)) ? $r : array())) ."\n";
+print implode(' - ', $stmt->errorinfo()) ."\n";
+
+print "-------------------------------------------------------\n";
+
+$queries = array(
+ "SELECT 1 FROM DUAL WHERE 1 = '?\'\''",
+ "SELECT 'a\\'0' FROM DUAL WHERE 1 = ?",
+ "SELECT 'a', 'b\'' FROM DUAL WHERE '''' LIKE '\\'' AND ?",
+ "SELECT 'foo?bar', '', '''' FROM DUAL WHERE ?"
+);
+
+foreach ($queries as $k => $query) {
+ $stmt = $db->prepare($query);
+ $stmt->execute(array(1));
+ printf("[%d] Query: [[%s]]\n", $k + 1, $query);
+ print implode(' - ', (($r = @$stmt->fetch(PDO::FETCH_NUM)) ? $r : array())) ."\n";
+ print implode(' - ', $stmt->errorinfo()) ."\n";
+ print "--------\n";
+}
+
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
+$sql = "SELECT upper(:id) FROM DUAL WHERE '1'";
+$stmt = $db->prepare($sql);
+
+$id = 'o\'\0';
+$stmt->bindParam(':id', $id);
+$stmt->execute();
+printf("Query: [[%s]]\n", $sql);
+print implode(' - ', (($r = @$stmt->fetch(PDO::FETCH_NUM)) ? $r : array())) ."\n";
+print implode(' - ', $stmt->errorinfo()) ."\n";
+
+print "-------------------------------------------------------\n";
+
+$queries = array(
+ "SELECT 1, 'foo' FROM DUAL WHERE 1 = :id AND '\\0' IS NULL AND 2 <> :id",
+ "SELECT 1 FROM DUAL WHERE 1 = :id AND '' AND 2 <> :id",
+ "SELECT 1 FROM DUAL WHERE 1 = :id AND '\'\'' = '''' AND 2 <> :id",
+ "SELECT 1 FROM DUAL WHERE 1 = :id AND '\'' = '''' AND 2 <> :id",
+ "SELECT 'a', 'b\'' FROM DUAL WHERE '''' LIKE '\\'' AND 1",
+ "SELECT 'a''', '\'b\'' FROM DUAL WHERE '''' LIKE '\\'' AND 1",
+ "SELECT UPPER(:id) FROM DUAL WHERE '1'",
+ "SELECT 1 FROM DUAL WHERE '\''",
+ "SELECT 1 FROM DUAL WHERE :id AND '\\0' OR :id",
+ "SELECT 1 FROM DUAL WHERE 'a\\f\\n\\0' AND 1 >= :id",
+ "SELECT 1 FROM DUAL WHERE '\'' = ''''",
+ "SELECT '\\n' '1 FROM DUAL WHERE '''' and :id'",
+ "SELECT 1 'FROM DUAL WHERE :id AND '''' = '''' OR 1 = 1 AND ':id",
+);
+
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
+$id = 1;
+
+foreach ($queries as $k => $query) {
+ $stmt = $db->prepare($query);
+ $stmt->bindParam(':id', $id);
+ $stmt->execute();
+
+ printf("[%d] Query: [[%s]]\n", $k + 1, $query);
+ print implode(' - ', (($r = @$stmt->fetch(PDO::FETCH_NUM)) ? $r : array())) ."\n";
+ print implode(' - ', $stmt->errorinfo()) ."\n";
+ print "--------\n";
+}
+
+?>
+--EXPECT--
+1
+00000 - -
+-------------------------------------------------------
+[1] Query: [[SELECT 1 FROM DUAL WHERE 1 = '?\'\'']]
+
+00000 - -
+--------
+[2] Query: [[SELECT 'a\'0' FROM DUAL WHERE 1 = ?]]
+a'0
+00000 - -
+--------
+[3] Query: [[SELECT 'a', 'b\'' FROM DUAL WHERE '''' LIKE '\'' AND ?]]
+a - b'
+00000 - -
+--------
+[4] Query: [[SELECT 'foo?bar', '', '''' FROM DUAL WHERE ?]]
+foo?bar - - '
+00000 - -
+--------
+Query: [[SELECT upper(:id) FROM DUAL WHERE '1']]
+O'\0
+00000 - -
+-------------------------------------------------------
+[1] Query: [[SELECT 1, 'foo' FROM DUAL WHERE 1 = :id AND '\0' IS NULL AND 2 <> :id]]
+
+00000 - -
+--------
+[2] Query: [[SELECT 1 FROM DUAL WHERE 1 = :id AND '' AND 2 <> :id]]
+
+00000 - -
+--------
+[3] Query: [[SELECT 1 FROM DUAL WHERE 1 = :id AND '\'\'' = '''' AND 2 <> :id]]
+
+00000 - -
+--------
+[4] Query: [[SELECT 1 FROM DUAL WHERE 1 = :id AND '\'' = '''' AND 2 <> :id]]
+1
+00000 - -
+--------
+[5] Query: [[SELECT 'a', 'b\'' FROM DUAL WHERE '''' LIKE '\'' AND 1]]
+a - b'
+00000 - -
+--------
+[6] Query: [[SELECT 'a''', '\'b\'' FROM DUAL WHERE '''' LIKE '\'' AND 1]]
+a' - 'b'
+00000 - -
+--------
+[7] Query: [[SELECT UPPER(:id) FROM DUAL WHERE '1']]
+1
+00000 - -
+--------
+[8] Query: [[SELECT 1 FROM DUAL WHERE '\'']]
+
+00000 - -
+--------
+[9] Query: [[SELECT 1 FROM DUAL WHERE :id AND '\0' OR :id]]
+1
+00000 - -
+--------
+[10] Query: [[SELECT 1 FROM DUAL WHERE 'a\f\n\0' AND 1 >= :id]]
+
+00000 - -
+--------
+[11] Query: [[SELECT 1 FROM DUAL WHERE '\'' = '''']]
+1
+00000 - -
+--------
+[12] Query: [[SELECT '\n' '1 FROM DUAL WHERE '''' and :id']]
+
+1 FROM DUAL WHERE '' and :id
+00000 - -
+--------
+[13] Query: [[SELECT 1 'FROM DUAL WHERE :id AND '''' = '''' OR 1 = 1 AND ':id]]
+1
+00000 - -
+--------
diff --git a/ext/pdo_mysql/tests/bug44327.phpt b/ext/pdo_mysql/tests/bug44327.phpt
new file mode 100644
index 0000000..f82c430
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug44327.phpt
@@ -0,0 +1,64 @@
+--TEST--
+Bug #44327 (PDORow::queryString property & numeric offsets / Crash)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $stmt = $db->prepare("SELECT 1 AS \"one\"");
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_LAZY);
+ var_dump($row);
+ var_dump($row->{0});
+ var_dump($row->one);
+ var_dump($row->queryString);
+
+ print "----------------------------------\n";
+
+ @$db->exec("DROP TABLE test");
+ $db->exec("CREATE TABLE test (id INT)");
+ $db->exec("INSERT INTO test(id) VALUES (1)");
+ $stmt = $db->prepare("SELECT id FROM test");
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_LAZY);
+ var_dump($row);
+ var_dump($row->queryString);
+ @$db->exec("DROP TABLE test");
+
+ print "----------------------------------\n";
+
+ $stmt = $db->prepare('foo');
+ @$stmt->execute();
+ $row = $stmt->fetch();
+ var_dump($row->queryString);
+
+?>
+--EXPECTF--
+object(PDORow)#%d (2) {
+ [%u|b%"queryString"]=>
+ %unicode|string%(17) "SELECT 1 AS "one""
+ [%u|b%"one"]=>
+ %unicode|string%(1) "1"
+}
+%unicode|string%(1) "1"
+%unicode|string%(1) "1"
+%unicode|string%(17) "SELECT 1 AS "one""
+----------------------------------
+object(PDORow)#%d (2) {
+ [%u|b%"queryString"]=>
+ %unicode|string%(19) "SELECT id FROM test"
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+}
+%unicode|string%(19) "SELECT id FROM test"
+----------------------------------
+
+Notice: Trying to get property of non-object in %s on line %d
+NULL
diff --git a/ext/pdo_mysql/tests/bug46292.phpt b/ext/pdo_mysql/tests/bug46292.phpt
new file mode 100644
index 0000000..a0f9716
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug46292.phpt
@@ -0,0 +1,84 @@
+--TEST--
+Bug #46292 (PDO::setFetchMode() shouldn't requires the 2nd arg when using FETCH_CLASSTYPE)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (version_compare(PHP_VERSION, '5.1.0', '<'))
+ die("skip Needs 5.1.0 and Interface Serializable");
+?>
+--FILE--
+<?php
+
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $pdoDb = MySQLPDOTest::factory();
+
+
+ class myclass implements Serializable {
+ public function __construct() {
+ printf("%s()\n", __METHOD__);
+ }
+
+ public function serialize() {
+ printf("%s()\n", __METHOD__);
+ return "any data from serialize()";
+ }
+
+ public function unserialize($dat) {
+ printf("%s(%s)\n", __METHOD__, var_export($dat, true));
+ return $dat;
+ }
+ }
+
+ class myclass2 extends myclass { }
+
+ $pdoDb->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
+ $pdoDb->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
+
+ $pdoDb->query('DROP TABLE IF EXISTS testz');
+
+ $pdoDb->query('CREATE TABLE testz (name VARCHAR(20) NOT NULL, value INT)');
+
+ $pdoDb->query("INSERT INTO testz VALUES ('myclass', 1), ('myclass2', 2), ('myclass', NULL), ('myclass3', NULL)");
+
+ $stmt = $pdoDb->prepare("SELECT * FROM testz");
+
+ var_dump($stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE | PDO::FETCH_GROUP));
+ $stmt->execute();
+
+ var_dump($stmt->fetch());
+ var_dump($stmt->fetch());
+ var_dump($stmt->fetchAll());
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS testz');
+?>
+--EXPECTF--
+bool(true)
+myclass::__construct()
+object(myclass)#%d (1) {
+ [%u|b%"value"]=>
+ %unicode|string%(1) "1"
+}
+myclass::__construct()
+object(myclass2)#%d (1) {
+ [%u|b%"value"]=>
+ %unicode|string%(1) "2"
+}
+myclass::__construct()
+array(2) {
+ [0]=>
+ object(myclass)#%d (1) {
+ [%u|b%"value"]=>
+ NULL
+ }
+ [1]=>
+ object(stdClass)#%d (1) {
+ [%u|b%"value"]=>
+ NULL
+ }
+}
diff --git a/ext/pdo_mysql/tests/bug53551.phpt b/ext/pdo_mysql/tests/bug53551.phpt
new file mode 100644
index 0000000..865dcea
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug53551.phpt
@@ -0,0 +1,73 @@
+--TEST--
+Bug #44327 (PDORow::queryString property & numeric offsets / Crash)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+include __DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
+
+$createSql = "CREATE TABLE `bug53551` (
+ `count` bigint(20) unsigned NOT NULL DEFAULT '0'
+)";
+
+$db->exec('drop table if exists bug53551');
+$db->exec($createSql);
+$db->exec("insert into bug53551 set `count` = 1 ");
+$db->exec("SET sql_mode = 'Traditional'");
+$sql = 'UPDATE bug53551 SET `count` = :count';
+$stmt = $db->prepare($sql);
+
+$values = array (
+ 'count' => NULL,
+);
+
+echo "1\n";
+$stmt->execute($values);
+var_dump($stmt->errorInfo());
+
+echo "2\n";
+$stmt->execute($values);
+var_dump($stmt->errorInfo());
+
+echo "\ndone\n";
+
+?>
+--CLEAN--
+<?php
+include __DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS bug53551');
+?>
+--EXPECTF--
+1
+
+Warning: PDOStatement::execute(): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'count' cannot be null in %s on line %d
+array(3) {
+ [0]=>
+ string(5) "23000"
+ [1]=>
+ int(1048)
+ [2]=>
+ string(29) "Column 'count' cannot be null"
+}
+2
+
+Warning: PDOStatement::execute(): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'count' cannot be null in %s on line %d
+array(3) {
+ [0]=>
+ string(5) "23000"
+ [1]=>
+ int(1048)
+ [2]=>
+ string(29) "Column 'count' cannot be null"
+}
+
+done
diff --git a/ext/pdo_mysql/tests/bug53782.phpt b/ext/pdo_mysql/tests/bug53782.phpt
new file mode 100644
index 0000000..4f81cce
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug53782.phpt
@@ -0,0 +1,40 @@
+--TEST--
+PDO MySQL Bug #53782 (foreach throws irrelevant exception)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$conn = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$res = $conn->query('SELECT 0');
+
+try {
+ $conn->query('ERROR');
+} catch (PDOException $e) {
+ echo "Caught: ".$e->getMessage()."\n";
+}
+
+foreach ($res as $k => $v) {
+ echo "Value: $v[0]\n";
+}
+
+echo "DONE";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Caught: SQLSTATE[42000]: %s
+Value: 0
+DONE
diff --git a/ext/pdo_mysql/tests/bug54929.phpt b/ext/pdo_mysql/tests/bug54929.phpt
new file mode 100644
index 0000000..29fb441
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug54929.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Bug #54929 (Parse error with single quote in sql comment (pdo-mysql))
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+$pdodb = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+
+function testQuery($query) {
+ global $pdodb;
+ $stmt = $pdodb->prepare($query);
+
+ if (!$stmt->execute(array("foo"))) {
+ var_dump($stmt->errorInfo());
+ } else{
+ var_dump($stmt->fetch(PDO::FETCH_ASSOC));
+ }
+}
+
+testQuery("/* ' */ select ? as f1 /* ' */");
+testQuery("/* '-- */ select ? as f1 /* *' */");
+testQuery("/* ' */ select ? as f1 --';");
+testQuery("/* ' */ select ? as f1 -- 'a;");
+testQuery("/*'**/ select ? as f1 /* ' */");
+testQuery("/*'***/ select ? as f1 /* ' */");
+testQuery("/*'**a ***b / ****
+******
+**/ select ? as f1 /* ' */");
+
+?>
+--EXPECTF--
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
+
+Warning: PDOStatement::execute(): 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 '--'' at line 1 in %s on line %d
+array(3) {
+ [0]=>
+ string(5) "42000"
+ [1]=>
+ int(1064)
+ [2]=>
+ string(149) "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 '--'' at line 1"
+}
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
+array(1) {
+ ["f1"]=>
+ string(3) "foo"
+}
diff --git a/ext/pdo_mysql/tests/bug_33689.phpt b/ext/pdo_mysql/tests/bug_33689.phpt
new file mode 100644
index 0000000..5969cae
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_33689.phpt
@@ -0,0 +1,64 @@
+--TEST--
+PDO MySQL Bug #33689 (query() execute() and fetch() return false on valid select queries)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$db->exec('CREATE TABLE test (bar INT NOT NULL)');
+$db->exec('INSERT INTO test VALUES(1)');
+
+var_dump($db->query('SELECT * from test'));
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+$stmt = $db->prepare('SELECT * from test');
+print_r($stmt->getColumnMeta(0));
+$stmt->execute();
+$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);
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+object(PDOStatement)#%d (1) {
+ [%u|b%"queryString"]=>
+ %unicode|string%(18) "SELECT * from test"
+}
+Array
+(
+ [bar] => 1
+ [0] => 1
+)
+Array
+(
+ [native_type] => LONG
+ [flags] => Array
+ (
+ [0] => not_null
+ )
+
+ [table] => test
+ [name] => bar
+ [len] => 11
+ [precision] => 0
+)
diff --git a/ext/pdo_mysql/tests/bug_37445.phpt b/ext/pdo_mysql/tests/bug_37445.phpt
new file mode 100644
index 0000000..8e91533
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_37445.phpt
@@ -0,0 +1,20 @@
+--TEST--
+PDO MySQL Bug #37445 (Premature stmt object destruction)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$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 %sbug_37445.php on line %d
diff --git a/ext/pdo_mysql/tests/bug_39483.phpt b/ext/pdo_mysql/tests/bug_39483.phpt
new file mode 100644
index 0000000..c20f4a3
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_39483.phpt
Binary files differ
diff --git a/ext/pdo_mysql/tests/bug_39858.phpt b/ext/pdo_mysql/tests/bug_39858.phpt
new file mode 100644
index 0000000..4745718
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_39858.phpt
@@ -0,0 +1,102 @@
+--TEST--
+Bug #39858 (Lost connection to MySQL server during query by a repeated call stored proced)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+
+$row = $db->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--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+$db->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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec("DROP PROCEDURE IF EXISTS p");
+?>
+--EXPECTF--
+Emulated Prepared Statements...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"2 * 2"]=>
+ %unicode|string%(1) "4"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"2 * 2"]=>
+ %unicode|string%(1) "4"
+ }
+}
+Native Prepared Statements...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"2 * 2"]=>
+ %unicode|string%(1) "4"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"2 * 2"]=>
+ %unicode|string%(1) "4"
+ }
+}
+done!
diff --git a/ext/pdo_mysql/tests/bug_41125.phpt b/ext/pdo_mysql/tests/bug_41125.phpt
new file mode 100644
index 0000000..a1d8dd1
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_41125.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Bug #41125 (PDO mysql + quote() + prepare() can result in segfault)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+$db = MySQLPDOTest::factory();
+$row = $db->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--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+$db->exec("DROP TABLE IF EXISTS test");
+
+// And now allow the evil to do his work
+$db->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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec("DROP TABLE IF EXISTS test");
+?>
+--EXPECTF--
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
+array(0) {
+}
+done!
diff --git a/ext/pdo_mysql/tests/bug_41698.phpt b/ext/pdo_mysql/tests/bug_41698.phpt
new file mode 100644
index 0000000..890ba77
--- /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--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+setlocale(LC_ALL, "de","de_DE","de_DE.ISO8859-1","de_DE.ISO_8859-1","de_DE.UTF-8");
+
+$db->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));
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ array(1) {
+ [%u|b%"floatval"]=>
+ %unicode|string%(8) "2.340000"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"floatval"]=>
+ %unicode|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 0000000..38d55a0
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_41997.phpt
@@ -0,0 +1,70 @@
+--TEST--
+PDO MySQL Bug #41997 (stored procedure call returning single rowset blocks future queries)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+$db = MySQLPDOTest::factory();
+$row = $db->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--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+
+$db->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!";
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"one"]=>
+ %unicode|string%(1) "1"
+ }
+}
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"two"]=>
+ %unicode|string%(1) "2"
+ }
+}
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+done!
diff --git a/ext/pdo_mysql/tests/bug_42499.phpt b/ext/pdo_mysql/tests/bug_42499.phpt
new file mode 100644
index 0000000..4ce497e
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_42499.phpt
@@ -0,0 +1,80 @@
+--TEST--
+Bug #42499 (Multi-statement execution via PDO::exec() makes connection unusable)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+MySQLPDOTest::skip();
+
+$db = MySQLPDOTest::factory();
+$stmt = $db->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--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+
+function bug_42499($db) {
+
+ $db->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) {
+ [%u|b%"_id"]=>
+ %unicode|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) {
+ [%u|b%"_id"]=>
+ %unicode|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!
diff --git a/ext/pdo_mysql/tests/bug_43371.phpt b/ext/pdo_mysql/tests/bug_43371.phpt
new file mode 100644
index 0000000..88c3a9b
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_43371.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #43371 (Memory errors in PDO constructor)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+$dsn = MySQLPDOTest::getDSN();
+$db = new PDO($dsn, PDO_MYSQL_TEST_USER, PDO_MYSQL_TEST_PASS, array(PDO::ATTR_PERSISTENT => true));
+
+print "done!";
+?>
+--EXPECT--
+done!
diff --git a/ext/pdo_mysql/tests/bug_44454.phpt b/ext/pdo_mysql/tests/bug_44454.phpt
new file mode 100644
index 0000000..89a4e2a
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_44454.phpt
@@ -0,0 +1,114 @@
+--TEST--
+Bug #44454 (Unexpected exception thrown in foreach() statement)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+function bug_44454($db) {
+
+ try {
+
+ $db->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));
+ }
+ }
+
+ $db->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)');
+
+ } 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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--XFAIL--
+For some reason the exception gets thrown at the wrong place
+--EXPECTF--
+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 %s',
+)
+... SELECT has returned 1 row...
+... INSERT should fail...
+... STMT - '00000'
+... PDO - array (
+ 0 => '23000',
+ 1 => 1062,
+ 2 => 'Duplicate entry \'1-1\' for key %s',
+)
+
+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 %s',
+)
+... SELECT has returned 1 row...
+... INSERT should fail...
+... STMT - '00000'
+... PDO - array (
+ 0 => '23000',
+ 1 => 1062,
+ 2 => 'Duplicate entry \'1-1\' for key %s',
+)
+done!
diff --git a/ext/pdo_mysql/tests/bug_44707.phpt b/ext/pdo_mysql/tests/bug_44707.phpt
new file mode 100644
index 0000000..18c8104
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_44707.phpt
@@ -0,0 +1,92 @@
+--TEST--
+Bug #44707 (The MySQL PDO driver resets variable content after bindParam on tinyint field)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+MySQLPDOTest::skip();
+
+$db = MySQLPDOTest::factory();
+$stmt = $db->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--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+function bug_44707($db) {
+
+ $db->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!";
+?>
+--EXPECTF--
+Native Prepared Statements
+bool(false)
+bool(false)
+bool(false)
+array(0) {
+}
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"mybool"]=>
+ %unicode|string%(1) "0"
+ }
+}
+done!
diff --git a/ext/pdo_mysql/tests/bug_45120.phpt b/ext/pdo_mysql/tests/bug_45120.phpt
new file mode 100644
index 0000000..db5da82
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_45120.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Bug #45120 (PDOStatement->execute() returns true then false for same statement)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+function bug_45120($db) {
+
+ $stmt = $db->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!";
+?>
+--EXPECT--
+Emulated Prepared Statements
+Native Prepared Statements
+done!
diff --git a/ext/pdo_mysql/tests/bug_50323.phpt b/ext/pdo_mysql/tests/bug_50323.phpt
new file mode 100644
index 0000000..02050fa
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_50323.phpt
@@ -0,0 +1,61 @@
+--TEST--
+Bug #50323 (No ability to connect to database named 't;', no chance to escape semicolon)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+ function changeDSN($original, $new_options) {
+ $old_options = array();
+ $dsn = substr($original,
+ strpos($original, ':') + 1,
+ strlen($original));
+
+ // 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);
+
+ $dsn = substr($dsn, 0, strlen($dsn) -1);
+
+ return $dsn;
+ }
+
+
+if (1 === @$db->exec('CREATE DATABASE `crazy;dbname`')) {
+ $dsn = changeDSN(getenv('PDOTEST_DSN'), array('dbname' => 'crazy;;dbname'));
+ $user = getenv('PDOTEST_USER');
+ $pass = getenv('PDOTEST_PASS');
+
+ new PDO($dsn, $user, $pass);
+}
+echo 'done!';
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+@$db->exec('DROP DATABASE IF EXISTS `crazy;dbname`');
+?>
+--EXPECTF--
+done!
+
diff --git a/ext/pdo_mysql/tests/bug_51670.phpt b/ext/pdo_mysql/tests/bug_51670.phpt
new file mode 100644
index 0000000..d5387e6
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_51670.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #51670 (getColumnMeta causes segfault when re-executing query after calling nextRowset)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+$query = $db->prepare('SELECT 1 AS num');
+$query->execute();
+if(!is_array($query->getColumnMeta(0))) die('FAIL!');
+$query->nextRowset();
+$query->execute();
+if(!is_array($query->getColumnMeta(0))) die('FAIL!');
+echo 'done!';
+?>
+--EXPECTF--
+done!
+
diff --git a/ext/pdo_mysql/tests/bug_61207.phpt b/ext/pdo_mysql/tests/bug_61207.phpt
new file mode 100644
index 0000000..411b39a
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_61207.phpt
@@ -0,0 +1,108 @@
+--TEST--
+PDO MySQL Bug #61207 (PDO::nextRowset() after a multi-statement query doesn't always work)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+
+$db->query('DROP TABLE IF EXISTS test');
+$db->query('create table `test`( `id` int )');
+
+$handle1 = $db->prepare('insert into test(id) values(1);
+ select * from test where id = ?;
+ update test set id = 2 where id = ?;');
+
+$handle1->bindValue('1', '1');
+$handle1->bindValue('2', '1');
+
+$handle1->execute();
+$i = 1;
+print("Handle 1:\n");
+do {
+ print('Rowset ' . $i++ . "\n");
+ if ($handle1->columnCount() > 0)
+ print("Results detected\n");
+} while($handle1->nextRowset());
+
+$handle2 = $db->prepare('select * from test where id = ?;
+ update test set id = 1 where id = ?;');
+
+$handle2->bindValue('1', '2');
+$handle2->bindValue('2', '2');
+
+$handle2->execute();
+
+$i = 1;
+print("Handle 2:\n");
+do {
+ print('Rowset ' . $i++ . "\n");
+ if ($handle2->columnCount() > 0)
+ print("Results detected\n");
+} while($handle2->nextRowset());
+
+$handle3 = $db->prepare('update test set id = 2 where id = ?;
+ select * from test where id = ?;');
+
+$handle3->bindValue('1', '1');
+$handle3->bindValue('2', '2');
+
+$handle3->execute();
+
+$i = 1;
+print("Handle 3:\n");
+do {
+ print('Rowset ' . $i++ . "\n");
+ if ($handle3->columnCount() > 0)
+ print("Results detected\n");
+} while($handle3->nextRowset());
+
+$handle4 = $db->prepare('insert into test(id) values(3);
+ update test set id = 2 where id = ?;
+ select * from test where id = ?;');
+
+$handle4->bindValue('1', '3');
+$handle4->bindValue('2', '2');
+
+$handle4->execute();
+
+$i = 1;
+print("Handle 4:\n");
+do {
+ print('Rowset ' . $i++ . "\n");
+ if ($handle1->columnCount() > 0)
+ print("Results detected\n");
+} while($handle1->nextRowset());
+
+$db->query("DROP TABLE test");
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECT--
+Handle 1:
+Rowset 1
+Rowset 2
+Results detected
+Rowset 3
+Handle 2:
+Rowset 1
+Results detected
+Rowset 2
+Handle 3:
+Rowset 1
+Rowset 2
+Results detected
+Handle 4:
+Rowset 1
+Rowset 2
+Rowset 3
+Results detected
diff --git a/ext/pdo_mysql/tests/bug_61411.phpt b/ext/pdo_mysql/tests/bug_61411.phpt
new file mode 100644
index 0000000..794d307
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_61411.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Bug #61411 (PDO Segfaults with PERSISTENT == TRUE && EMULATE_PREPARES == FALSE)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+
+$row = $db->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 < 40106)
+ die(sprintf("skip Need MySQL Server 4.1.6+, found %d.%02d.%02d (%d)\n",
+ $matches[0], $matches[1], $matches[2], $version));
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+$attr = getenv('PDOTEST_ATTR');
+if (!$attr) {
+ $attr = array();
+} else {
+ $attr = unserialize($attr);
+}
+$attr[PDO::ATTR_PERSISTENT] = true;
+$attr[PDO::ATTR_EMULATE_PREPARES] = false;
+putenv('PDOTEST_ATTR='.serialize($attr));
+
+$db = MySQLPDOTest::factory();
+
+$stmt = $db->prepare("SELECT 1");
+$stmt->execute();
+
+foreach ($stmt as $line) {
+ var_dump($line);
+}
+
+print "done!";
+?>
+--EXPECTF--
+array(2) {
+ [1]=>
+ int(1)
+ [2]=>
+ int(1)
+}
+done!
diff --git a/ext/pdo_mysql/tests/bug_61755.phpt b/ext/pdo_mysql/tests/bug_61755.phpt
new file mode 100644
index 0000000..1d2b968
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_61755.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Bug #61755 (A parsing bug in the prepared statements can lead to access violations)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+echo "NULL-Byte before first placeholder:\n";
+$s = $db->prepare("SELECT \"a\0b\", ?");
+$s->bindValue(1,"c");
+$s->execute();
+$r = $s->fetch();
+echo "Length of item 0: ".strlen($r[0]).", Value of item 1: ".$r[1]."\n";
+
+echo "\nOpen comment:\n";
+try {
+ $s = $db->prepare("SELECT /*");
+ $s->execute();
+} catch (Exception $e) {
+ echo "Error code: ".$e->getCode()."\n";
+}
+
+echo "\ndone!\n";
+?>
+--EXPECTF--
+NULL-Byte before first placeholder:
+Length of item 0: 3, Value of item 1: c
+
+Open comment:
+Error code: 42000
+
+done!
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 0000000..dc6933d
--- /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--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+
+function bug_pecl_1295($db) {
+
+ $db->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!";
+?>
+--EXPECTF--
+Emulated...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "c"
+ }
+}
+Native...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|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 0000000..5f585bd
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_pecl_7976.phpt
@@ -0,0 +1,92 @@
+--TEST--
+PECL Bug #7976 (Calling stored procedure several times)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+
+$row = $db->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--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+
+function bug_pecl_7976($db) {
+
+ $db->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);
+
+print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP PROCEDURE IF EXISTS p');
+?>
+--XFAIL--
+Works with mysqlnd. It is not supported by libmysql. For libmysql is good enough to see no crash.
+--EXPECTF--
+Emulated...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_one"]=>
+ %unicode|string%(1) "1"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_one"]=>
+ %unicode|string%(1) "1"
+ }
+}
+Native...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_one"]=>
+ %unicode|string%(1) "1"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_one"]=>
+ %unicode|string%(1) "1"
+ }
+}
+done!
diff --git a/ext/pdo_mysql/tests/common.phpt b/ext/pdo_mysql/tests/common.phpt
new file mode 100644
index 0000000..f55d1f8
--- /dev/null
+++ b/ext/pdo_mysql/tests/common.phpt
@@ -0,0 +1,28 @@
+--TEST--
+MySQL
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) print 'skip not loaded';
+?>
+--REDIRECTTEST--
+# magic auto-configuration
+
+$config = array(
+ 'TESTS' => __DIR__.'/ext/pdo/tests'
+);
+
+if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+}
+
+return $config;
diff --git a/ext/pdo_mysql/tests/config.inc b/ext/pdo_mysql/tests/config.inc
new file mode 100644
index 0000000..2530442
--- /dev/null
+++ b/ext/pdo_mysql/tests/config.inc
@@ -0,0 +1,52 @@
+<?php
+/* Overrule global settings, if need be */
+
+if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+}
+
+foreach ($config['ENV'] as $k => $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
new file mode 100644
index 0000000..c819efe
--- /dev/null
+++ b/ext/pdo_mysql/tests/last_insert_id.phpt
@@ -0,0 +1,35 @@
+--TEST--
+PDO MySQL auto_increment / last insert id
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+print_r($db->query("CREATE TABLE test (id int auto_increment primary key, num int)"));
+
+print_r($db->query("INSERT INTO test (id, num) VALUES (23, 42)"));
+
+print_r($db->query("INSERT INTO test (num) VALUES (451)"));
+
+print_r($db->lastInsertId());
+--EXPECT--
+PDOStatement Object
+(
+ [queryString] => CREATE TABLE test (id int auto_increment primary key, num int)
+)
+PDOStatement Object
+(
+ [queryString] => INSERT INTO test (id, num) VALUES (23, 42)
+)
+PDOStatement Object
+(
+ [queryString] => INSERT INTO test (num) VALUES (451)
+)
+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 0000000..115aead
--- /dev/null
+++ b/ext/pdo_mysql/tests/mysql_pdo_test.inc
@@ -0,0 +1,177 @@
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc');
+require_once(dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc');
+
+class MySQLPDOTest extends PDOTest {
+
+ static function factory($classname = 'PDO', $drop_test_tables = false, $myattr = null, $mydsn = null) {
+
+ $dsn = self::getDSN($mydsn);
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+ $attr = getenv('PDOTEST_ATTR');
+
+ if (is_string($attr) && strlen($attr)) {
+ $attr = unserialize($attr);
+ } else {
+ $attr = null;
+ }
+ if ($user === false)
+ $user = NULL;
+ if ($pass === false)
+ $pass = NULL;
+
+ $db = new $classname($dsn, $user, $pass, $attr);
+ if (!$db) {
+ die("Could not create PDO object (DSN=$dsn, user=$user)\n");
+ }
+
+ $db->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 (!empty($row) && $row[1] == 'YES' && ($row[0] == 'have_innodb' || $row[0] == 'have_bdb')) {
+ return str_replace("have_", "", $row[0]);
+ }
+ }
+ /* MySQL 5.6.1+ */
+ foreach ($db->query("SHOW ENGINES") as $row) {
+ if (isset($row['engine']) && isset($row['support'])) {
+ if ('InnoDB' == $row['engine'] && ('YES' == $row['support'] || 'DEFAULT' == $row['support']))
+ return 'innodb';
+ }
+ }
+ return false;
+ }
+
+ static function isPDOMySQLnd() {
+ ob_start();
+ phpinfo();
+ $tmp = ob_get_contents();
+ ob_end_clean();
+ $tmp = stristr($tmp, "PDO Driver for MySQL => enabled");
+ return (bool)preg_match('/Client API version.*mysqlnd/', $tmp);
+ }
+
+ static function dropTestTable($db = NULL) {
+ if (is_null($db))
+ $db = self::factory();
+
+ $db->exec('DROP TABLE IF EXISTS test');
+ }
+
+}
+?> \ No newline at end of file
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 0000000..c3f12df
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql___construct.phpt
@@ -0,0 +1,300 @@
+--TEST--
+MySQL PDO->__construct() - Generic + DSN
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function tryandcatch($offset, $code) {
+
+ try {
+ eval($code);
+ assert(sprintf("[%03d] Should have failed\n", $offset) != '');
+ } catch (PDOException $e) {
+ return sprintf("[%03d] %s, [%s] %s\n",
+ $offset,
+ $e->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 ...
+ // hostnames > 1024 chars break on some NIS-enabled FreeBSD...
+ $dsn = MySQLPDOTest::getDSN(array('host' => str_repeat('0123456789', 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! */
+ } 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[%s] [1045] Access denied for user 'dont%s'@'%s' (using password: YES), [n/a] n/a
+[010] SQLSTATE[%s] [1045] Access denied for user 'dont%s'@'%s' (using password: YES), [n/a] n/a
+[017] DSN=%s, SQLSTATE[%s] [%d] %s
+done!
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 0000000..14f81a6
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql___construct_ini.phpt
@@ -0,0 +1,56 @@
+--TEST--
+MySQL PDO->__construct() - URI
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+/* TODO - fix this limitation */
+if (getenv('PDO_MYSQL_TEST_DSN') !== "mysql:dbname=phptest;unix_socket=/tmp/mysql.sock")
+ die("skip Fix test to run in other environments as well!");
+?>
+--INI--
+pdo.dsn.mysql="mysql:dbname=phptest;socket=/tmp/mysql.sock"
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ $found = false;
+ $values = ini_get_all();
+ foreach ($values as $name => $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 0000000..29b3c11
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt
@@ -0,0 +1,184 @@
+--TEST--
+MySQL PDO->__construct(), options
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function set_option_and_check($offset, $option, $value, $option_desc) {
+
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+
+ try {
+ $db = new PDO($dsn, $user, $pass, array($option => $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',
+
+ PDO::MYSQL_ATTR_INIT_COMMAND => 'PDO::MYSQL_ATTR_INIT_COMMAND',
+ );
+
+ $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,
+ PDO::MYSQL_ATTR_INIT_COMMAND => '',
+ );
+
+ 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(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(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
+[003a] Expecting default value for 'PDO::MYSQL_ATTR_INIT_COMMAND' of ''/string, 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'
+[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 '%s.nonexistent' doesn't exist
+done!
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 0000000..20add5a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options_libmysql.phpt
@@ -0,0 +1,83 @@
+--TEST--
+MySQL PDO->__construct(), libmysql only options
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (MySQLPDOTest::isPDOMySQLnd())
+ die("skip libmysql only options")
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function set_option_and_check($offset, $option, $value, $option_desc, $ignore_diff = false) {
+
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+
+ try {
+ $db = new PDO($dsn, $user, $pass, array($option => $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(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
+[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 0000000..7e92ff2
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql___construct_uri.phpt
@@ -0,0 +1,76 @@
+--TEST--
+MySQL PDO->__construct() - URI
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ try {
+
+ if ($tmp = MySQLPDOTest::getTempDir()) {
+
+ $file = $tmp . DIRECTORY_SEPARATOR . 'pdomuri.tst';
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+ $uri = sprintf('uri:file:%s', $file);
+
+ if ($fp = @fopen($file, 'w')) {
+ // ok, great we can create a file with a DSN in it
+ fwrite($fp, $dsn);
+ fclose($fp);
+ clearstatcache();
+ assert(file_exists($file));
+ try {
+ $db = new PDO($uri, $user, $pass);
+ } catch (PDOException $e) {
+ printf("[002] URI=%s, DSN=%s, File=%s (%d bytes, '%s'), %s\n",
+ $uri, $dsn,
+ $file, filesize($file), file_get_contents($file),
+ $e->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 0000000..9a73f1b
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_autocommit.phpt
@@ -0,0 +1,98 @@
+--TEST--
+PDO::ATTR_AUTOCOMMIT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ // autocommit should be on by default
+ if (1 !== ($tmp = $db->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");
+
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..8ce8af4
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_case.phpt
@@ -0,0 +1,224 @@
+--TEST--
+PDO::ATTR_CASE
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $default = $db->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));
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+%unicode|string%(15) "PDO::CASE_LOWER"
+array(2) {
+ [0]=>
+ array(6) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [0]=>
+ %unicode|string%(1) "1"
+ [%u|b%"id_upper"]=>
+ %unicode|string%(1) "1"
+ [1]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ [2]=>
+ %unicode|string%(1) "a"
+ }
+ [1]=>
+ array(6) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [0]=>
+ %unicode|string%(1) "2"
+ [%u|b%"id_upper"]=>
+ %unicode|string%(1) "2"
+ [1]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ [2]=>
+ %unicode|string%(1) "b"
+ }
+}
+array(2) {
+ [0]=>
+ array(10) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [0]=>
+ %unicode|string%(1) "1"
+ [%u|b%"id_upper"]=>
+ %unicode|string%(1) "1"
+ [1]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ [2]=>
+ %unicode|string%(1) "a"
+ [%u|b%"mixed"]=>
+ NULL
+ [3]=>
+ NULL
+ [%u|b%"myupper"]=>
+ NULL
+ [4]=>
+ NULL
+ }
+ [1]=>
+ array(10) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [0]=>
+ %unicode|string%(1) "2"
+ [%u|b%"id_upper"]=>
+ %unicode|string%(1) "2"
+ [1]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ [2]=>
+ %unicode|string%(1) "b"
+ [%u|b%"mixed"]=>
+ NULL
+ [3]=>
+ NULL
+ [%u|b%"myupper"]=>
+ NULL
+ [4]=>
+ NULL
+ }
+}
+array(1) {
+ [0]=>
+ array(10) {
+ [%u|b%"ID"]=>
+ %unicode|string%(1) "1"
+ [0]=>
+ %unicode|string%(1) "1"
+ [%u|b%"LABEL"]=>
+ %unicode|string%(1) "a"
+ [1]=>
+ %unicode|string%(1) "a"
+ [%u|b%"MIXED"]=>
+ NULL
+ [2]=>
+ NULL
+ [%u|b%"MYUPPER"]=>
+ NULL
+ [3]=>
+ NULL
+ [%u|b%"LOWER"]=>
+ NULL
+ [4]=>
+ NULL
+ }
+}
+array(1) {
+ [0]=>
+ array(10) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [0]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ [1]=>
+ %unicode|string%(1) "a"
+ [%u|b%"MiXeD"]=>
+ NULL
+ [2]=>
+ NULL
+ [%u|b%"MYUPPER"]=>
+ NULL
+ [3]=>
+ NULL
+ [%u|b%"ID"]=>
+ %unicode|string%(1) "1"
+ [4]=>
+ %unicode|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 0000000..2d93963
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_client_version.phpt
@@ -0,0 +1,37 @@
+--TEST--
+PDO::ATTR_CLIENT_VERSION
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ assert(('' == $db->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 0000000..187f9ec
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_connection_status.phpt
@@ -0,0 +1,37 @@
+--TEST--
+PDO::ATTR_CONNECTION_STATUS
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $status = $db->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 0000000..8661dda
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_driver_name.phpt
@@ -0,0 +1,31 @@
+--TEST--
+PDO::ATTR_DRIVER_NAME
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ assert(('' == $db->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--
+%unicode|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 0000000..b037089
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt
@@ -0,0 +1,166 @@
+--TEST--
+PDO::ATTR_ERRMODE
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--INI--
+error_reporting=E_ALL
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $valid = array(PDO::ERRMODE_SILENT, PDO::ERRMODE_WARNING, PDO::ERRMODE_EXCEPTION);
+ do {
+ $invalid = mt_rand(-1000, 1000);
+ } while (in_array($invalid, $valid));
+
+
+ $tmp = array();
+ if (false != @$db->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 0000000..b9a4fc9
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_fetch_table_names.phpt
@@ -0,0 +1,42 @@
+--TEST--
+PDO::ATTR_FETCH_TABLE_NAMES
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $db->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) {
+ [%u|b%"test.label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|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 0000000..89e6f38
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_init_command.phpt
@@ -0,0 +1,48 @@
+--TEST--
+PDO::MYSQL_ATTR_INIT_COMMAND
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--INI--
+error_reporting=E_ALL
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+
+ $table = sprintf("test_%s", md5(mt_rand(0, PHP_INT_MAX)));
+ $db = new PDO($dsn, $user, $pass);
+ $db->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));
+
+ $info = $db->errorInfo();
+ var_dump($info[0]);
+
+ $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!";
+?>
+--EXPECTF--
+%unicode|string%(58) "CREATE TABLE test_%s(id INT)"
+%unicode|string%(5) "00000"
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+}
+done!
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 0000000..115103d
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_max_buffer_size.phpt
@@ -0,0 +1,77 @@
+--TEST--
+MySQL PDO->__construct(), PDO::MYSQL_ATTR_MAX_BUFFER_SIZE
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (MySQLPDOTest::isPDOMySQLnd())
+ die("skip PDO::MYSQL_ATTR_MAX_BUFFER_SIZE not supported with mysqlnd");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function try_buffer_size($offset, $buffer_size) {
+
+ try {
+
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+
+ /* unsigned overflow possible ? */
+ $db = new PDO($dsn, $user, $pass,
+ array(
+ PDO::MYSQL_ATTR_MAX_BUFFER_SIZE => $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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..1820804
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_oracle_nulls.phpt
@@ -0,0 +1,121 @@
+--TEST--
+PDO::ATTR_ORACLE_NULLS
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $tmp = array();
+ if (false !== @$db->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) {
+ [%u|b%"z"]=>
+ NULL
+ [%u|b%"a"]=>
+ NULL
+ [%u|b%"b"]=>
+ %unicode|string%(1) " "
+ [%u|b%"c"]=>
+ NULL
+ [%u|b%"d"]=>
+ %unicode|string%(2) " d"
+ [%u|b%"e"]=>
+ %unicode|string%(3) "%se"
+ }
+}
+array(1) {
+ [0]=>
+ array(6) {
+ [%u|b%"z"]=>
+ NULL
+ [%u|b%"a"]=>
+ %unicode|string%(0) ""
+ [%u|b%"b"]=>
+ %unicode|string%(1) " "
+ [%u|b%"c"]=>
+ %unicode|string%(0) ""
+ [%u|b%"d"]=>
+ %unicode|string%(2) " d"
+ [%u|b%"e"]=>
+ %unicode|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 0000000..456a796
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_prefetch.phpt
@@ -0,0 +1,21 @@
+--TEST--
+PDO::ATTR_PREFETCH
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ var_dump($db->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 0000000..4d0868a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_server_info.phpt
@@ -0,0 +1,48 @@
+--TEST--
+PDO::ATTR_SERVER_INFO
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ assert(('' == $db->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 (soundex($new_info) != soundex($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 0000000..a59a6b0
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_server_version.phpt
@@ -0,0 +1,65 @@
+--TEST--
+PDO::ATTR_SERVER_VERSION
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ assert(('' == $db->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 0000000..631a918
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt
@@ -0,0 +1,155 @@
+--TEST--
+PDO::ATTR_STATEMENT_CLASS
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $default = $db->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]=>
+ %unicode|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]=>
+ %unicode|string%(12) "mystatement4"
+ [1]=>
+ array(1) {
+ [0]=>
+ %unicode|string%(6) "param1"
+ }
+}
+mystatement4
+%unicode|string%(6) "param1"
+mystatement5
+%unicode|string%(12) "mystatement5"
+%unicode|string%(10) "no data :)"
+array(1) {
+ [0]=>
+ array(4) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [0]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ [1]=>
+ %unicode|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 0000000..ac9f8a3
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_begintransaction.phpt
@@ -0,0 +1,206 @@
+--TEST--
+PDO->beginTransaction()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (false == MySQLPDOTest::detect_transactional_mysql_engine($db))
+ die("skip Transactional engine not found");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
+
+ if (1 !== $db->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(), trim(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");
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+}
+bool(false)
+array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|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?
+%unicode|string%(5) "00000"
+done!
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 0000000..899231a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_bit.phpt
@@ -0,0 +1,64 @@
+--TEST--
+MySQL PDO->exec(), BIT columns - remove after fix!
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (MySQLPDOTest::isPDOMySQLnd())
+ die("skip Known bug - mysqlnd handles BIT incorrectly!");
+?>
+--FILE--
+<?php
+ /* TODO: remove this test after fix and enable the BIT test in pdo_mysql_types.phpt again */
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function test_type(&$db, $offset, $sql_type, $value, $ret_value = NULL, $pattern = NULL) {
+
+ $db->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";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(2) "20"
+ [%u|b%"label"]=>
+ %unicode|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 0000000..17fa5d6
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt
@@ -0,0 +1,85 @@
+--TEST--
+PDO MySQL specific class constants
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ $expected = array(
+ 'MYSQL_ATTR_USE_BUFFERED_QUERY' => true,
+ 'MYSQL_ATTR_LOCAL_INFILE' => true,
+ 'MYSQL_ATTR_DIRECT_QUERY' => true,
+ 'MYSQL_ATTR_FOUND_ROWS' => true,
+ 'MYSQL_ATTR_IGNORE_SPACE' => true,
+ 'MYSQL_ATTR_INIT_COMMAND' => true,
+ "MYSQL_ATTR_SSL_KEY" => true,
+ "MYSQL_ATTR_SSL_CERT" => true,
+ "MYSQL_ATTR_SSL_CA" => true,
+ "MYSQL_ATTR_SSL_CAPATH" => true,
+ "MYSQL_ATTR_SSL_CIPHER" => true,
+ "MYSQL_ATTR_COMPRESS" => true,
+ );
+
+ if (!MySQLPDOTest::isPDOMySQLnd()) {
+ $expected['MYSQL_ATTR_MAX_BUFFER_SIZE'] = 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=<charset_name>
+ ^ 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!
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 0000000..f3cd530
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_commit.phpt
@@ -0,0 +1,90 @@
+--TEST--
+MySQL PDO->commit()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (false == MySQLPDOTest::detect_transactional_mysql_engine($db))
+ die("skip Transactional engine not found");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
+
+ try {
+ if (true !== ($tmp = $db->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()));
+ }
+
+ print "done!";
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test_commit');
+MySQLPDOTest::dropTestTable($db);
+?>
+--EXPECT--
+done! \ No newline at end of file
diff --git a/ext/pdo_mysql/tests/pdo_mysql_connect_charset.phpt b/ext/pdo_mysql/tests/pdo_mysql_connect_charset.phpt
new file mode 100644
index 0000000..22d3618
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_connect_charset.phpt
@@ -0,0 +1,33 @@
+--TEST--
+PDO_MYSQL: Defining a connection charset in the DSN
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ /* Connect to mysql to determine the current charset so we can diffinate it */
+ $link = MySQLPDOTest::factory();
+ $charset = $link->query("SHOW VARIABLES LIKE 'character_set_connection'")->fetchObject()->value;
+
+ /* Make sure that we don't attempt to set the current character set to make this case useful */
+ $new_charset = ($charset == 'latin1' ? 'ascii' : 'latin1');
+
+ /* Done with the original connection, create a second link to test the character set being defined */
+ unset($link);
+
+ $link = MySQLPDOTest::factory('PDO', false, null, Array('charset' => $new_charset));
+ $conn_charset = $link->query("SHOW VARIABLES LIKE 'character_set_connection'")->fetchObject()->value;
+
+ if ($charset !== $conn_charset) {
+ echo "done!\n";
+ } else {
+ echo "failed!\n";
+ }
+?>
+--EXPECTF--
+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 0000000..b970c4e
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_errorcode.phpt
@@ -0,0 +1,86 @@
+--TEST--
+MySQL PDO->errorCode()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ function check_error($offset, &$obj, $expected = '00000') {
+
+ $code = $obj->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..93e1fbf
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_errorinfo.phpt
@@ -0,0 +1,111 @@
+--TEST--
+MySQL PDO->errorInfo()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ function check_error($offset, &$obj, $expected = '00000') {
+ $info = $obj->errorInfo();
+ $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);
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Emulated Prepared Statements...
+[015] Driver-specific error code not set
+[015] Driver-specific error message.not set
+[016] Driver-specific error code not set
+[016] Driver-specific error message.not set
+Native Prepared Statements...
+done!
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 0000000..2a0f527
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_exec.phpt
@@ -0,0 +1,185 @@
+--TEST--
+MySQL PDO->exec(), affected rows
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ function exec_and_count($offset, &$db, $sql, $exp = NULL) {
+
+ try {
+
+ $ret = $db->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);
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+@$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..2e80053
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_exec_ddl.phpt
@@ -0,0 +1,92 @@
+--TEST--
+MySQL PDO->exec(), affected rows
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ function exec_and_count($offset, &$db, $sql, $exp, $suppress_warning = false) {
+
+ try {
+
+ if ($suppress_warning)
+ $ret = @$db->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
+ */
+
+ } catch (PDOException $e) {
+ printf("[001] %s, [%s] %s\n",
+ $e->getMessage(),
+ $db->errorCode(), implode(' ', $db->errorInfo()));
+ }
+
+ print "done!";
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+MySQLPDOTest::dropTestTable($db);
+// 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');
+?>
+--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 0000000..b550cb8
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_exec_load_data.phpt
@@ -0,0 +1,108 @@
+--TEST--
+MySQL PDO->exec(), affected rows
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+// Run test only locally - not against remote hosts
+$db = MySQLPDOTest::factory();
+$stmt = $db->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--
+<?php
+ function exec_and_count($offset, &$db, $sql, $exp) {
+
+ try {
+
+ $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) {
+
+ 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 LOCAL 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+done!
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 0000000..d0e0ffc
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_exec_select.phpt
@@ -0,0 +1,65 @@
+--TEST--
+MySQL PDO->exec(), SELECT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ function exec_and_count($offset, &$db, $sql, $exp) {
+
+ try {
+
+ $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 {
+
+ 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+@$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..da88639
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt
@@ -0,0 +1,88 @@
+--TEST--
+MySQL PDOStatement->fetch()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ function fetch($offset, &$db, $query, $expect = null) {
+
+ try {
+ $stmt = $db->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()));
+ }
+
+ print "done!";
+?>
+--EXPECTF--
+[002] Suspicious FETCH_BOTH result, dumping
+array(2) {
+ [0]=>
+ %unicode|string%(1) "1"
+ [1]=>
+ %unicode|string%(1) "1"
+}
+array(2) {
+ [1]=>
+ %unicode|string%(1) "1"
+ [2]=>
+ %unicode|string%(1) "1"
+}
+[002] Expected differes from returned data, dumping
+array(2) {
+ [0]=>
+ %unicode|string%(1) "1"
+ [1]=>
+ %unicode|string%(1) "1"
+}
+array(2) {
+ [1]=>
+ %unicode|string%(1) "1"
+ [2]=>
+ %unicode|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 0000000..c992d3a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_get_attribute.phpt
@@ -0,0 +1,106 @@
+--TEST--
+MySQL PDO->getAttribute()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (false == MySQLPDOTest::detect_transactional_mysql_engine($db))
+ die("skip Transactional engine not found");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
+
+ function find_invalid_int($valid_options) {
+ do {
+ $invalid = mt_rand(-10000, 10000);
+ } while (in_array($invalid, $valid_options));
+ return $invalid;
+ }
+
+ function set_and_get($offset, $db, $attribute, $value) {
+
+ $value_type = gettype($value);
+ try {
+
+ if (!$db->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
+*/
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..24b19ae
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt
@@ -0,0 +1,60 @@
+--TEST--
+MySQL PDO class interface
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (false == MySQLPDOTest::detect_transactional_mysql_engine($db))
+ die("skip Transactional engine not found");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $expected = array(
+ '__construct' => 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,
+ 'inTransaction' => 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!
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 0000000..313b20d
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_last_insert_id.phpt
@@ -0,0 +1,119 @@
+--TEST--
+MySQL PDO->lastInsertId()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+ if ('0' !== ($tmp = $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..eb0fff1
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt
@@ -0,0 +1,97 @@
+--TEST--
+MySQL PDO->__construct(), PDO::ATTR_PERSISTENT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ try {
+
+ $dsn = MySQLPDOTest::getDSN();
+ $user = PDO_MYSQL_TEST_USER;
+ $pass = PDO_MYSQL_TEST_PASS;
+
+ $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => 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!";
+?>
+--EXPECTF--
+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 0000000..a570c1f
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_phpinfo.phpt
@@ -0,0 +1,31 @@
+--TEST--
+MySQL PDO phpinfo() output
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ ob_start();
+ phpinfo();
+ $tmp = ob_get_contents();
+ ob_end_clean();
+
+ /* PDO Driver for MySQL, client library version => 6.0.3-alpha */
+ $expected = sprintf('Client API 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!
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 0000000..b2db0d9
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt
@@ -0,0 +1,419 @@
+--TEST--
+MySQL PDO->prepare(), emulated PS
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ function prepex($offset, &$db, $query, $input_params = null, $error_info = null) {
+
+ try {
+
+ if (is_array($error_info) && isset($error_info['prepare']))
+ $stmt = @$db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--XFAIL--
+PDO's PS parser has some problems with invalid SQL and crashes from time to time
+(check with valgrind...)
+--EXPECTF--
+array(1) {
+ [%u|b%"one"]=>
+ %unicode|string%(1) "1"
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(12) ":placeholder"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(12) ":placeholder"
+ }
+}
+array(2) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(9) "first row"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(10) "second row"
+ }
+}
+array(2) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(3) "row"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(3) "row"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(1) "?"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(1) "?"
+ }
+}
+array(2) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(9) "first row"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(10) "second row"
+ }
+}
+array(2) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(3) "row"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|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 0000000..c6b299e
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt
@@ -0,0 +1,85 @@
+--TEST--
+MySQL PDO->prepare(), emulated PS, anonymous placeholder
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ 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");
+
+ $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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "?"
+ }
+}
+now the same with native PS
+[005] Execute has failed, 'HY093' array (
+ 0 => 'HY093',
+ 1 => NULL,
+ 2 => NULL,
+)
+array(0) {
+}
+done!
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 0000000..3a77d5e
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt
@@ -0,0 +1,84 @@
+--TEST--
+MySQL PDO->prepare(), emulated PS, anonymous placeholder
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+// TODO: This test is MySQL version specific - for whatever reason
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+ // native PS
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d
+[003] Execute has failed, 'HY093' array (
+ 0 => 'HY093',
+ 1 => NULL,
+ 2 => NULL,
+)
+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',
+ 1 => NULL,
+ 2 => NULL,
+)
+array(0) {
+}
+done!
diff --git a/ext/pdo_mysql/tests/pdo_mysql_prepare_load_data.phpt b/ext/pdo_mysql/tests/pdo_mysql_prepare_load_data.phpt
new file mode 100644
index 0000000..37d9cbd
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_load_data.phpt
@@ -0,0 +1,118 @@
+--TEST--
+MySQL PDO->prepare() and 1295 (ER_UNSUPPORTED_PS)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+// Run test only locally - not against remote hosts
+$db = MySQLPDOTest::factory();
+$stmt = $db->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--
+<?php
+ function exec_and_count($offset, &$db, $sql, $exp) {
+
+ try {
+
+ $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) {
+
+ 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();
+ // Run with native PS.
+ // The test is about checking the fallback to emulation
+ $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
+ 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);
+
+ // This should fail, the PS protocol should not support it.
+ // mysqlnd will give 2014 as a follow-up of the fallback logic
+ // libmysql will give a little more precise 2030 error code
+ // However, you get an error and the big question is what happens to the line
+ $stmt = $db->prepare(sprintf("LOAD DATA INFILE %s INTO TABLE test FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'", $db->quote($filename)));
+ if (!$stmt->execute()) {
+ printf("[004] [%d] %s\n", $stmt->errorCode(), var_export($stmt->errorInfo(), true));
+ }
+
+ // Check the line
+ $stmt = $db->query("SELECT 1 as 'one'");
+ if ($stmt->errorCode() != '0000') {
+ printf("[005] [%d] %s\n", $stmt->errorCode(), var_export($stmt->errorInfo(), true));
+ } else {
+ $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ if (!isset($rows[0]['one']) || $rows[0]['one'] != 1)
+ printf("[006] [%d] %s\n", $stmt->errorCode(), var_export($stmt->errorInfo(), true));
+ }
+
+ unlink($filename);
+
+ } catch (PDOException $e) {
+ printf("[001] %s, [%s] %s (%s)\n",
+ $e->getMessage(),
+ $db->errorCode(),
+ implode(' ', $db->errorInfo()),
+ (isset($stmt)) ? implode(' ', $stmt->errorInfo()) : 'N/A');
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Warning: PDOStatement::execute(): SQLSTATE[HY000]: General error: %s in %s on line %d
+[004] [0] array (
+ 0 => 'HY000',
+ 1 => %d,
+ 2 => %s,
+)
+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 0000000..ba5142a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_match_against.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Bug #41876 (bindParam() and bindValue() do not work with MySQL MATCH () AGAINST ())
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ $db->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()));
+
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+done!
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 0000000..43006c6
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native.phpt
@@ -0,0 +1,385 @@
+--TEST--
+MySQL PDO->prepare(), native PS
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ function prepex($offset, &$db, $query, $input_params = null, $error_info = null, $suppress_warning = false) {
+
+ try {
+
+ if ($suppress_warning || (is_array($error_info) && isset($error_info['prepare'])))
+ $stmt = @$db->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), var_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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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!
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 0000000..42c3d07
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_clear_error.phpt
@@ -0,0 +1,96 @@
+--TEST--
+MySQL PDO->prepare(), native PS, clear line after error
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..57a4529
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_column.phpt
@@ -0,0 +1,49 @@
+--TEST--
+MySQL PDO->prepare(), native PS, named placeholder
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $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");
+
+ $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));
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..e382dff
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_dup_named_placeholder.phpt
@@ -0,0 +1,145 @@
+--TEST--
+MySQL PDO->prepare(), native PS, named placeholder II
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+Native...
+
+Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d
+[003] Execute has failed, 'HY093' array (
+ 0 => 'HY093',
+ 1 => NULL,
+ 2 => NULL,
+)
+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',
+ 1 => NULL,
+ 2 => NULL,
+)
+array(0) {
+}
+done!
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 0000000..90cedef
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_mixed_style.phpt
@@ -0,0 +1,39 @@
+--TEST--
+MySQL PDO->prepare(), native PS, mixed, wired style
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ $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");
+
+ $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));
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..530170d
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt
@@ -0,0 +1,92 @@
+--TEST--
+MySQL PDO->prepare(), native PS, named placeholder
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ $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->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+[003] Execute has failed, 'HY093' array (
+ 0 => 'HY093',
+ 1 => NULL,
+ 2 => NULL,
+)
+array(0) {
+}
+array(1) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(3) "101"
+ ["label"]=>
+ string(12) ":placeholder"
+ }
+}
+done!
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 0000000..dbff9c4
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_prepare_native_placeholder_everywhere.phpt
@@ -0,0 +1,90 @@
+--TEST--
+MySQL PDO->prepare(),native PS, anonymous placeholder
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ try {
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..c0737b2
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_rollback.phpt
@@ -0,0 +1,96 @@
+--TEST--
+PDO::rollBack()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (false == MySQLPDOTest::detect_transactional_mysql_engine($db))
+ die("skip Transactional engine not found");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
+
+ $db->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));
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+$db->exec('DROP TABLE IF EXISTS test2');
+?>
+--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 0000000..dd4920e
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindcolumn.phpt
@@ -0,0 +1,115 @@
+--TEST--
+MySQL PDOStatement->bindColumn()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ 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, 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..70b2f3f
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam.phpt
@@ -0,0 +1,159 @@
+--TEST--
+MySQL PDOStatement->bindParam()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..9421f62
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindparam_types.phpt
@@ -0,0 +1,173 @@
+--TEST--
+MySQL PDOStatement->bindParam() - SQL column types
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ function pdo_mysql_stmt_bindparam_types_do($db, $offset, $native, $sql_type, $value) {
+
+ if ($native)
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..1c62d77
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_bindvalue.phpt
@@ -0,0 +1,336 @@
+--TEST--
+MySQL PDOStatement->bindValue()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ printf("Testing native PS...\n");
+ 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");
+
+ 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..ae7e7fc
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobfromsteam.phpt
@@ -0,0 +1,148 @@
+--TEST--
+MySQL PDOStatement - inserting BLOB from stream
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+
+$tmp = MySQLPDOTest::getTempDir();
+if (!$tmp)
+ die("skip Can't create temporary file");
+
+$file = $tmp . DIRECTORY_SEPARATOR . 'pdoblob.tst';
+$fp = fopen($file, 'w');
+if (!$fp)
+ die("skip Can't create temporary file");
+
+if (4 != fwrite($fp, 'test')) {
+ die("skip Can't create temporary file");
+}
+fclose($fp);
+clearstatcache();
+
+if (!file_exists($file))
+ die("skip Can't create temporary file");
+
+unlink($file);
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function blob_from_stream($offset, $db, $file, $blob) {
+
+ @unlink($file);
+ clearstatcache();
+ if (file_exists($file)) {
+ printf("[%03d + 1] Cannot remove old test file\n", $offset);
+ return false;
+ }
+
+ $fp = fopen($file, 'w');
+ if (!$fp || !fwrite($fp, $blob)) {
+ printf("[%03d + 2] Cannot create test file '%s'\n", $offset, $file);
+ return false;
+ }
+
+ fclose($fp);
+ clearstatcache();
+ if (!file_exists($file)) {
+ printf("[%03d + 3] Failed to create test file '%s'\n", $offset, $file);
+ return false;
+ }
+
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+@unlink(MySQLPDOTest::getTempDir() . DIRECTORY_SEPARATOR . 'pdoblob.tst');
+?>
+--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 0000000..96489ef
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt
@@ -0,0 +1,98 @@
+--TEST--
+MySQL Prepared Statements and BLOBs
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $blobs = array(
+ 'TINYBLOB' => 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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..455b17d
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor.phpt
@@ -0,0 +1,176 @@
+--TEST--
+MySQL PDOStatement->closeCursor()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ /* TODO the results look wrong, why do we get 2014 with buffered AND unbuffered queries */
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ function pdo_mysql_stmt_closecursor($db) {
+
+ // This one should fail. I let it fail to prove that closeCursor() makes a difference.
+ // If no error messages gets printed do not know if proper usage of closeCursor() makes any
+ // difference or not. That's why we need to cause an error here.
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..aea272b
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_closecursor_empty.phpt
@@ -0,0 +1,74 @@
+--TEST--
+MySQL PDOStatement->closeCursor()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+
+ 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");
+
+ $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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..54b433f
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_columncount.phpt
@@ -0,0 +1,69 @@
+--TEST--
+MySQL PDOStatement->columnCount()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ // The only purpose of this is to check if emulated and native PS
+ // return the same. If it works for one, it should work for all.
+ // Internal data structures should be the same in both cases.
+ 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, 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..4d59e8c
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorcode.phpt
@@ -0,0 +1,59 @@
+--TEST--
+MySQL PDOStatement->errorCode();
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ $db->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 0000000..d5a3489
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_errorinfo.phpt
@@ -0,0 +1,136 @@
+--TEST--
+MySQL PDOStatement->errorInfo();
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ 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');
+ 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!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Testing emulated PS...
+array(3) {
+ [0]=>
+ %unicode|string%(0) ""
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+
+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]=>
+ %unicode|string%(5) "42S02"
+ [1]=>
+ int(1146)
+ [2]=>
+ %unicode|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]=>
+ %unicode|string%(5) "42S02"
+ [1]=>
+ int(1146)
+ [2]=>
+ %unicode|string%(%d) "Table '%s.test' doesn't exist"
+}
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+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(3) {
+ [0]=>
+ %unicode|string%(0) ""
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+
+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]=>
+ %unicode|string%(5) "42S02"
+ [1]=>
+ int(1146)
+ [2]=>
+ %unicode|string%(%d) "Table '%s.test' doesn't exist"
+}
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ int(1146)
+ [2]=>
+ %unicode|string%(%d) "Table '%s.ihopeitdoesnotexist' doesn't exist"
+}
+done!
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 0000000..fa4b122
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_non_select.phpt
@@ -0,0 +1,191 @@
+--TEST--
+MySQL PDOStatement->execute()/fetch(), Non-SELECT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ try {
+
+ // Emulated PS first
+ $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");
+
+ 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()));
+ }
+
+ print "done!\n";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..18ef17b
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize.phpt
@@ -0,0 +1,153 @@
+--TEST--
+MySQL PDOStatement->fetch(), PDO::FETCH_SERIALIZE
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (version_compare(PHP_VERSION, '5.1.0', '<'))
+ die("skip Needs 5.1.0 and Interface Serializable");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ class myclass implements Serializable {
+
+ private static $instance = null;
+ protected $myprotected = 'a protected propery';
+
+ // Good old magic stuff
+ private function __construct($caller = NULL) {
+ printf("%s(%s)\n", __METHOD__, $caller);
+ }
+
+
+ public function __destruct() {
+ // printf("%s()\n", __METHOD__);
+ }
+
+ public function __sleep() {
+ printf("%s()\n", __METHOD__);
+ }
+
+ public function __wakeup() {
+ printf("%s()\n", __METHOD__);
+ }
+
+ public function __call($method, $params) {
+ printf("%s(%s, %s)\n", __METHOD__, $method, var_export($params, true));
+ }
+
+ public function __set($prop, $value) {
+ printf("%s(%s, %s)\n", __METHOD__, $prop, var_export($value, true));
+ $this->{$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()));
+ }
+
+ print "done!\n";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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) {
+ [%u|b%"myprotected":protected]=>
+ %unicode|string%(19) "a protected propery"
+}
+
+Using PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE to fetch the object from DB and unserialize it...
+myclass::unserialize('C:7:"myclass":19:{Data from serialize}')
+object(myclass)#%d (1) {
+ [%u|b%"myprotected":protected]=>
+ %unicode|string%(19) "a protected propery"
+}
+
+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) {
+ [%u|b%"myprotected":protected]=>
+ %unicode|string%(19) "a protected propery"
+ [%u|b%"myobj"]=>
+ %unicode|string%(38) "C:7:"myclass":19:{Data from serialize}"
+}
+done!
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 0000000..a935e1a
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetch_serialize_simple.phpt
@@ -0,0 +1,93 @@
+--TEST--
+MySQL PDOStatement->fetch(), PDO::FETCH_SERIALIZE
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (version_compare(PHP_VERSION, '5.1.0', '<'))
+ die("skip Needs 5.1.0 and Interface Serializable");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ class myclass implements Serializable {
+
+ public function __construct($caller = null) {
+ printf("%s(%s) - note that it must not be called when unserializing\n", __METHOD__, var_export($caller, true));
+ }
+
+ public function __set($prop, $value) {
+ printf("%s(%s, %s)\n", __METHOD__, var_export($prop, true), var_export($value, true));
+ $this->{$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()')
+object(myclass)#%d (0) {
+}
+myclass::unserialize('Data fetched from DB to be given to unserialize()')
+object(myclass)#%d (0) {
+}
+
+And now PDO using setFetchMode(PDO::FETCH:CLASS|PDO::FETCH_SERIALIZE) + fetch()...
+myclass::unserialize('Data fetched from DB to be given to unserialize()')
+object(myclass)#%d (0) {
+}
+done!
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 0000000..2e278b1
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt
@@ -0,0 +1,109 @@
+--TEST--
+MySQL PDO: PDOStatement->fetchObject()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+
+try {
+ $query = "SELECT '', NULL, \"\" FROM DUAL";
+ $stmt = $db->prepare($query);
+ $ok = @$stmt->execute();
+} catch (PDOException $e) {
+ die("skip: Test cannot be run with SQL mode ANSI");
+}
+if (!$ok)
+ die("skip: Test cannot be run with SQL mode ANSI");
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+MySQLPDOTest::createTestTable($db);
+
+try {
+
+ $query = "SELECT id, '', NULL, \"\" FROM test ORDER BY id ASC LIMIT 3";
+ $stmt = $db->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);
+ if ("" != $prop)
+ $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()));
+}
+
+print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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) {
+ [%u|b%"set_calls":"myclass":private]=>
+ int(4)
+ [%u|b%"grp":protected]=>
+ NULL
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ [%u|b%"null"]=>
+ NULL
+}
+done!
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 0000000..0fef334
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt
@@ -0,0 +1,314 @@
+--TEST--
+MySQL: PDOStatement->getColumnMeta()
+--SKIPIF--
+<?php # vim:ft=php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+// Too many differences among MySQL version - run only with a recent one
+$db = MySQLPDOTest::factory();
+$stmt = $db->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--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+$db = MySQLPDOTest::factory();
+$db->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;
+ }
+
+ $stmt = $db->prepare('SELECT @@sql_mode AS _mode');
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ $real_as_float = (false === stristr($row['_mode'], "REAL_AS_FLOAT")) ? false : 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, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
+ test_meta($db, 140, 'REAL UNSIGNED', 1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
+ test_meta($db, 150, 'REAL ZEROFILL', -1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
+ test_meta($db, 160, 'REAL UNSIGNED ZEROFILL', 1.01, ($real_as_float) ? 'FLOAT' : '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 0000000..91b5237
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_multiquery.phpt
@@ -0,0 +1,102 @@
+--TEST--
+PDOStatements and multi query
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function mysql_stmt_multiquery_wrong_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');
+ 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Emulated Prepared Statements...
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(3) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"label"]=>
+ %unicode|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 '%SSELECT 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
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 0000000..7996245
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset.phpt
@@ -0,0 +1,313 @@
+--TEST--
+MySQL PDOStatement->nextRowSet()
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+$row = $db->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--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Emulated PS...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_version"]=>
+ %unicode|string%(%d) "%s"
+ }
+}
+bool(false)
+array(3) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ }
+ [2]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ }
+}
+array(3) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "c"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ }
+ [2]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+bool(false)
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_version"]=>
+ %unicode|string%(%d) "%s"
+ }
+}
+bool(false)
+array(3) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ }
+ [2]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ }
+}
+array(3) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "c"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ }
+ [2]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+bool(false)
+Native PS...
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_version"]=>
+ %unicode|string%(%d) "%s"
+ }
+}
+bool(false)
+array(3) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ }
+ [2]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ }
+}
+array(3) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "c"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ }
+ [2]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+bool(false)
+array(1) {
+ [0]=>
+ array(1) {
+ [%u|b%"_version"]=>
+ %unicode|string%(%d) "%s"
+ }
+}
+bool(false)
+array(3) {
+ [0]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ }
+ [2]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ }
+}
+array(3) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "3"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "c"
+ }
+ [1]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "2"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "b"
+ }
+ [2]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|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 0000000..17e2412
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_rowcount.phpt
@@ -0,0 +1,36 @@
+--TEST--
+MySQL PDOStatement->rowCount() @ SELECT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+ MySQLPDOTest::createTestTable($db);
+
+ try {
+
+ if (0 !== ($tmp = $db->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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--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 0000000..f051403
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt
@@ -0,0 +1,184 @@
+--TEST--
+MySQL PDO:query() vs. PDO::prepare() and MySQL error 2050
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+if (MYSQLPDOTest::isPDOMySQLnd())
+ die("skip libmysql only test");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ try {
+
+ 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);
+ $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(<some 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(<some query>)
+ PDO::query(<some query>)
+ mysql_stmt_prepare
+ mysql_stmt_execute
+
+ PHP continues at the left side of the expression:
+
+ $stmt = PDO::query(<some 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()));
+ }
+
+ print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+Native PS...
+Buffered...
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|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) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|string%(1) "a"
+ }
+}
+array(1) {
+ [0]=>
+ array(2) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ [%u|b%"label"]=>
+ %unicode|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 0000000..c34f4a9
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_variable_columncount.phpt
@@ -0,0 +1,123 @@
+--TEST--
+MySQL Prepared Statements and different column counts
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+$db = MySQLPDOTest::factory();
+
+$row = $db->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--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+ $db = MySQLPDOTest::factory();
+
+ function check_result($offset, $stmt, $columns) {
+
+ do {
+ $row = $stmt->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 0000000..3595631
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_subclass.phpt
@@ -0,0 +1,105 @@
+--TEST--
+MySQL PDOStatement->execute()/fetch(), Non-SELECT
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+if (version_compare(PHP_VERSION, '5.0.0', '<'))
+ die("skip Requires PHP 5.0+");
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ // No silly strict mode warnings, please!
+ error_reporting(E_ALL^E_STRICT);
+ ini_set('display_errors', false);
+
+ try {
+
+ class MyPDO extends PDO {
+
+ public function __construct() {
+ $this->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(".var_export($method,true).", ".var_export($args, true).")\n";
+ // $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";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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) {
+ [%u|b%"id"]=>
+ %unicode|string%(1) "1"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"id"]=>
+ %unicode|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 0000000..22c6b66
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_types.phpt
@@ -0,0 +1,184 @@
+--TEST--
+MySQL PDO->exec(), native types wo ZEROFILL
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function test_type(&$db, $offset, $sql_type, $value, $ret_value = NULL, $pattern = NULL, $alternative_type = NULL) {
+
+ $db->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 && !is_null($alternative_type) && gettype($row['label']) != $alternative_type) {
+ printf("[%03d + 4] %s - input = %s/%s, output = %s/%s (alternative type: %s)\n", $offset,
+ $sql_type, var_export($exp, true), gettype($exp),
+ var_export($row['label'], true), gettype($row['label']),
+ $alternative_type);
+ 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) ? ((PHP_INT_SIZE > 4) ? (int)-2147483648 : (double)-2147483648) : '-2147483648',
+ NULL, ($is_mysqlnd) ? 'integer' : NULL);
+
+ test_type($db, 110, 'INT UNSIGNED', 4294967295, ($is_mysqlnd) ? ((PHP_INT_SIZE > 4) ? 4294967295 : '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";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..f97bd1b
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_types_zerofill.phpt
@@ -0,0 +1,128 @@
+--TEST--
+MySQL PDO->exec(), native types - ZEROFILL
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+ function test_type(&$db, $offset, $sql_type, $value, $ret_value = NULL, $pattern = NULL) {
+
+ $db->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);
+
+ $stmt = $db->prepare('SELECT @@sql_mode AS _mode');
+ $stmt->execute();
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ $real_as_float = (false === stristr($row['_mode'], "REAL_AS_FLOAT")) ? false : true;
+
+ test_type($db, 100, 'REAL ZEROFILL', -1.01, NULL, '/^[0]*0$/');
+ test_type($db, 110, 'REAL ZEROFILL', 1.01, NULL, ($real_as_float) ? '/^[0]*1\.0.*$/' : '/^[0]*1\.01$/');
+ test_type($db, 120, 'REAL UNSIGNED ZEROFILL', 1.01, NULL, ($real_as_float) ? '/^[0]*1\..*$/' : '/^[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";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..ff5b0e4
--- /dev/null
+++ b/ext/pdo_mysql/tests/pecl_bug_5200.phpt
@@ -0,0 +1,36 @@
+--TEST--
+PDO MySQL PECL Bug #5200 (Describe table gives unexpected result mysql and type enum)
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__). '/common.phpt');
+
+$db->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);
+}
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--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 0000000..5984284
--- /dev/null
+++ b/ext/pdo_mysql/tests/pecl_bug_5780.phpt
@@ -0,0 +1,49 @@
+--TEST--
+PDO MySQL PECL Bug #5780 (Failure to produce an error when one is expected)
+--SKIPIF--
+<?php # vim:ft=php:
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__). '/common.phpt');
+
+$db->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);
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+$db->exec('DROP TABLE IF EXISTS test2');
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ %unicode|string%(7) "testing"
+ [1]=>
+ %unicode|string%(7) "testing"
+}
+bool(true)
+array(2) {
+ [0]=>
+ %unicode|string%(5) "00000"
+ [1]=>
+ NULL
+}
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 0000000..04aa2c9
--- /dev/null
+++ b/ext/pdo_mysql/tests/pecl_bug_5802.phpt
@@ -0,0 +1,61 @@
+--TEST--
+PDO MySQL PECL Bug #5802 (bindParam/bindValue retain the is_null flag)
+--SKIPIF--
+<?php # vim:ft=php:
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__). '/common.phpt');
+
+$db->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));
+
+print "done!";
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP TABLE IF EXISTS test');
+?>
+--EXPECTF--
+array(3) {
+ [0]=>
+ array(1) {
+ [%u|b%"bar"]=>
+ %unicode|string%(3) "foo"
+ }
+ [1]=>
+ array(1) {
+ [%u|b%"bar"]=>
+ NULL
+ }
+ [2]=>
+ array(1) {
+ [%u|b%"bar"]=>
+ %unicode|string%(3) "qaz"
+ }
+}
+done! \ No newline at end of file
diff --git a/ext/pdo_mysql/tests/show_tables.phpt b/ext/pdo_mysql/tests/show_tables.phpt
new file mode 100644
index 0000000..1b1ef67
--- /dev/null
+++ b/ext/pdo_mysql/tests/show_tables.phpt
@@ -0,0 +1,20 @@
+--TEST--
+PDO MySQL SHOW TABLES
+--SKIPIF--
+<?php
+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';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+print_r($db->query('SHOW TABLES'));
+--EXPECT--
+PDOStatement Object
+(
+ [queryString] => SHOW TABLES
+)
diff --git a/ext/pdo_mysql/tests/skipif.inc b/ext/pdo_mysql/tests/skipif.inc
new file mode 100644
index 0000000..d48670a
--- /dev/null
+++ b/ext/pdo_mysql/tests/skipif.inc
@@ -0,0 +1,7 @@
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql'))
+ die('skip PDO_MySQL driver not loaded');
+
+if (version_compare(PHP_VERSION, '5.1.0') < 0)
+ die('skip Most tests assume PHP 5.1+');
+?> \ 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 0000000..c7bb9cc
--- /dev/null
+++ b/ext/pdo_mysql/tests/table.inc
@@ -0,0 +1,9 @@
+<?php
+if (!$db) {
+ require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+ $db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+}
+// $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")');
+?> \ No newline at end of file