php-src/ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt
Nikita Popov c18b1aea28 PDO MySQL: Use native types for results
Previously, PDO MySQL only fetched data as native int/float if
native prepared statements were used. This patch updates PDO to
have the same behavior for emulated prepared statements, and thus
removes the largest remaining discrepancy between these two modes.

Note that PDO already has a ATTR_STRINGIFY_FETCHES option to control
whether native types are desired or not. The previous output can
be restored by enabling this option.

Most of the tests make use of that option, because this allows the
tests to work under libmysqlclient as well, which currently always
returns string results (independently of whether native or emulated
PS are used).
2020-12-17 10:54:34 +01:00

414 lines
16 KiB
PHP

--TEST--
MySQL PDO->prepare(), emulated PS
--SKIPIF--
<?php
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
MySQLPDOTest::skip();
$db = MySQLPDOTest::factory();
?>
--FILE--
<?php
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
$db = MySQLPDOTest::factory();
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
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");
try {
prepex(3, $db, '', [], ['execute' => ['sqlstate' => '42000']]);
} catch (\ValueError $e) {
echo $e->getMessage(), \PHP_EOL;
}
// 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 __DIR__ . '/mysql_pdo_test.inc';
$db = MySQLPDOTest::factory();
$db->exec('DROP TABLE IF EXISTS test');
?>
--EXPECTF--
PDO::prepare(): Argument #1 ($query) cannot be empty
array(1) {
["one"]=>
string(1) "1"
}
array(1) {
[0]=>
array(1) {
["label"]=>
string(12) ":placeholder"
}
}
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
array(0) {
}
array(2) {
[0]=>
array(1) {
["label"]=>
string(9) "first row"
}
[1]=>
array(1) {
["label"]=>
string(10) "second row"
}
}
array(2) {
[0]=>
array(2) {
["id"]=>
string(1) "1"
["label"]=>
string(3) "row"
}
[1]=>
array(2) {
["id"]=>
string(1) "2"
["label"]=>
string(3) "row"
}
}
array(1) {
[0]=>
array(1) {
["label"]=>
string(1) "?"
}
}
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
array(0) {
}
array(2) {
[0]=>
array(1) {
["label"]=>
string(9) "first row"
}
[1]=>
array(1) {
["label"]=>
string(10) "second row"
}
}
array(2) {
[0]=>
array(2) {
["id"]=>
string(1) "1"
["label"]=>
string(3) "row"
}
[1]=>
array(2) {
["id"]=>
string(1) "2"
["label"]=>
string(3) "row"
}
}
done!