summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCameron Porter <camporter1@gmail.com>2019-03-06 00:33:40 -0600
committerNikita Popov <nikita.ppv@gmail.com>2019-06-07 09:48:43 +0200
commit7d1aa7534d756477d45f8fa63b5467589ccca031 (patch)
treea244dc5aba7319137cc5ca776e7e3eed4dfb0a51
parent2df32942d3ec9b70e791225aeee70a857b9b292e (diff)
downloadphp-git-7d1aa7534d756477d45f8fa63b5467589ccca031.tar.gz
Fixed bug #38546
Properly support binding boolean parameters with emulated prepared statements disabled. Also add the necessary mysqlnd support for MYSQL_TYPE_TINY.
-rw-r--r--NEWS2
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c9
-rw-r--r--ext/pdo_mysql/mysql_statement.c4
-rw-r--r--ext/pdo_mysql/tests/bug_38546.phpt282
-rw-r--r--ext/pdo_mysql/tests/bug_44707.phpt20
5 files changed, 312 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 438c962759..c065a39b8d 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ PHP NEWS
- MySQLi:
. Fixed bug #77956 (When mysqli.allow_local_infile = Off, use a meaningful
error message). (Sjon Hortensius)
+ . Fixed bug #38546 (bindParam incorrect processing of bool types).
+ (camporter)
- OpenSSL:
. Fixed bug #78079 (openssl_encrypt_ccm.phpt fails with OpenSSL 1.1.1c).
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index 24f6886723..e884ad7461 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -581,7 +581,7 @@ mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copie
zval *parameter = &stmt->param_bind[i].zv;
ZVAL_DEREF(parameter);
- if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG || current_type == MYSQL_TYPE_TINY)) {
/* always copy the var, because we do many conversions */
if (Z_TYPE_P(parameter) != IS_LONG &&
PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
@@ -798,6 +798,13 @@ mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies,
int4store(*p, Z_LVAL_P(data));
(*p) += 4;
break;
+ case MYSQL_TYPE_TINY:
+ if (Z_TYPE_P(data) == IS_STRING) {
+ goto send_string;
+ }
+ int1store(*p, Z_LVAL_P(data));
+ (*p)++;
+ break;
case MYSQL_TYPE_LONG_BLOB:
if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c
index 2777f3ccc4..e230cfbf05 100644
--- a/ext/pdo_mysql/mysql_statement.c
+++ b/ext/pdo_mysql/mysql_statement.c
@@ -560,6 +560,10 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_LONG);
#endif /* SIZEOF_LONG */
break;
+ case IS_TRUE:
+ case IS_FALSE:
+ mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_TINY);
+ break;
case IS_DOUBLE:
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_DOUBLE);
break;
diff --git a/ext/pdo_mysql/tests/bug_38546.phpt b/ext/pdo_mysql/tests/bug_38546.phpt
new file mode 100644
index 0000000000..962a404fc0
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug_38546.phpt
@@ -0,0 +1,282 @@
+--TEST--
+PDO MySQL Bug #38546 (bindParam incorrect processing of bool types)
+--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->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
+
+$db->exec("DROP TABLE IF EXISTS test");
+
+$query = "CREATE TABLE test(
+ uid MEDIUMINT UNSIGNED NOT NULL,
+ some_bool_1 BOOL NOT NULL,
+ some_bool_2 BOOL NOT NULL,
+ some_int TINYINT NOT NULL
+ )";
+$db->exec($query);
+
+$st = $db->prepare("INSERT INTO test (uid, some_bool_1, some_bool_2, some_int) VALUES (?, ?, ?, ?)");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => false,
+ 'some_bool_2' => true,
+ 'some_int' => -23
+];
+$st->bindParam(1, $values['uid'], PDO::PARAM_INT);
+$st->bindParam(2, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(4, $values['some_int'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok insert\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+$st = $db->prepare("UPDATE test SET some_bool_1=?, some_bool_2=?, some_int=? WHERE uid=?");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => (bool) 1,
+ 'some_bool_2' => (bool) 0,
+ 'some_int' => 1,
+];
+
+$st->bindParam(1, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(2, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_int'], PDO::PARAM_INT);
+$st->bindParam(4, $values['uid'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok prepare 1\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+$st = $db->prepare("UPDATE test SET some_bool_1=?, some_bool_2=?, some_int=? WHERE uid=?");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => (bool) 0,
+ 'some_bool_2' => (bool) 1,
+ 'some_int' => 2,
+];
+
+$st->bindParam(1, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(2, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_int'], PDO::PARAM_INT);
+$st->bindParam(4, $values['uid'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok prepare 2\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+// String true and false should fail
+$st = $db->prepare("UPDATE test SET some_bool_1=?, some_bool_2=?, some_int=? WHERE uid=?");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => 'true',
+ 'some_bool_2' => 'false',
+ 'some_int' => 3,
+];
+
+$st->bindParam(1, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(2, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_int'], PDO::PARAM_INT);
+$st->bindParam(4, $values['uid'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok prepare 3\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+// Null should not be treated as false
+$st = $db->prepare("UPDATE test SET some_bool_1=?, some_bool_2=?, some_int=? WHERE uid=?");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => true,
+ 'some_bool_2' => null,
+ 'some_int' => 4,
+];
+
+$st->bindParam(1, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(2, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_int'], PDO::PARAM_INT);
+$st->bindParam(4, $values['uid'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok prepare 4\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+// Integers converted correctly
+$st = $db->prepare("UPDATE test SET some_bool_1=?, some_bool_2=?, some_int=? WHERE uid=?");
+
+$values = [
+ 'uid' => 6,
+ 'some_bool_1' => 256,
+ 'some_bool_2' => 0,
+ 'some_int' => 5,
+];
+
+$st->bindParam(1, $values['some_bool_1'], PDO::PARAM_BOOL);
+$st->bindParam(2, $values['some_bool_2'], PDO::PARAM_BOOL);
+$st->bindParam(3, $values['some_int'], PDO::PARAM_INT);
+$st->bindParam(4, $values['uid'], PDO::PARAM_INT);
+
+$result = $st->execute();
+
+if ($result === false) {
+ var_dump($st->errorInfo());
+} else {
+ print("ok prepare 5\n");
+}
+
+foreach ($db->query('SELECT * from test') as $row) {
+ print_r($row);
+}
+
+?>
+--CLEAN--
+<?php
+require dirname(__FILE__) . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECTF--
+ok insert
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 0
+ [1] => 0
+ [some_bool_2] => 1
+ [2] => 1
+ [some_int] => -23
+ [3] => -23
+)
+ok prepare 1
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 1
+ [1] => 1
+ [some_bool_2] => 0
+ [2] => 0
+ [some_int] => 1
+ [3] => 1
+)
+ok prepare 2
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 0
+ [1] => 0
+ [some_bool_2] => 1
+ [2] => 1
+ [some_int] => 2
+ [3] => 2
+)
+
+Warning: PDOStatement::execute(): SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'true' for column 'some_bool_1' at row 1 in %s
+array(3) {
+ [0]=>
+ string(5) "HY000"
+ [1]=>
+ int(1366)
+ [2]=>
+ string(65) "Incorrect integer value: 'true' for column 'some_bool_1' at row 1"
+}
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 0
+ [1] => 0
+ [some_bool_2] => 1
+ [2] => 1
+ [some_int] => 2
+ [3] => 2
+)
+
+Warning: PDOStatement::execute(): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'some_bool_2' cannot be null in %s
+array(3) {
+ [0]=>
+ string(5) "23000"
+ [1]=>
+ int(1048)
+ [2]=>
+ string(35) "Column 'some_bool_2' cannot be null"
+}
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 0
+ [1] => 0
+ [some_bool_2] => 1
+ [2] => 1
+ [some_int] => 2
+ [3] => 2
+)
+ok prepare 5
+Array
+(
+ [uid] => 6
+ [0] => 6
+ [some_bool_1] => 1
+ [1] => 1
+ [some_bool_2] => 0
+ [2] => 0
+ [some_int] => 5
+ [3] => 5
+) \ No newline at end of file
diff --git a/ext/pdo_mysql/tests/bug_44707.phpt b/ext/pdo_mysql/tests/bug_44707.phpt
index 90dc4c20f4..5f6ee021b9 100644
--- a/ext/pdo_mysql/tests/bug_44707.phpt
+++ b/ext/pdo_mysql/tests/bug_44707.phpt
@@ -37,8 +37,6 @@ function bug_44707($db) {
$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);
@@ -78,8 +76,6 @@ Native Prepared Statements
bool(false)
bool(false)
bool(false)
-array(0) {
-}
array(1) {
[0]=>
array(2) {
@@ -89,4 +85,20 @@ array(1) {
string(1) "0"
}
}
+array(2) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["mybool"]=>
+ string(1) "0"
+ }
+ [1]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["mybool"]=>
+ string(1) "0"
+ }
+}
done!