diff options
Diffstat (limited to 'ext/pdo_mysql')
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, ¶m->parameter); + if (stm) { + SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + Z_TYPE_P(param->parameter) = IS_STRING; + Z_STRLEN_P(param->parameter) = php_stream_copy_to_mem(stm, + &Z_STRVAL_P(param->parameter), PHP_STREAM_COPY_ALL, 0); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource" TSRMLS_CC); + return 0; + } + } + /* fall through */ + + default: + ; + } + +#if PDO_USE_MYSQLND + /* Is it really correct to check the zval's type? - But well, that's what the old code below does, too */ + PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE_P(param->parameter)); + switch (Z_TYPE_P(param->parameter)) { + case IS_STRING: + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_VAR_STRING); + break; + case IS_LONG: +#if SIZEOF_LONG==8 + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_LONGLONG); +#elif SIZEOF_LONG==4 + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_LONG); +#endif /* SIZEOF_LONG */ + break; + case IS_DOUBLE: + mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, param->parameter, MYSQL_TYPE_DOUBLE); + break; + default: + PDO_DBG_RETURN(0); + } + + PDO_DBG_RETURN(1); +#else + PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE_P(param->parameter)); + switch (Z_TYPE_P(param->parameter)) { + case IS_STRING: + b->buffer_type = MYSQL_TYPE_STRING; + b->buffer = Z_STRVAL_P(param->parameter); + b->buffer_length = Z_STRLEN_P(param->parameter); + *b->length = Z_STRLEN_P(param->parameter); + 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 Binary files differnew file mode 100644 index 0000000..c20f4a3 --- /dev/null +++ b/ext/pdo_mysql/tests/bug_39483.phpt 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 |