Bug #79132 [Ver]: PDO re-uses parameter values from earlier calls to execute()
| From: | cmb@php.net | Date: | Fri, 17 Jan 2020 12:41:39 +0000 |
| Subject: | Bug #79132 [Ver]: PDO re-uses parameter values from earlier calls to execute() | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-224959@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=79132&edit=1
ID: 79132
Updated by: cmb@php.net
Reported by: love at sickpeople dot se
Summary: PDO re-uses parameter values from earlier calls to
execute()
Status: Verified
Type: Bug
Package: PDO related
PHP Version: 7.4.1
Block user comment: N
Private report: N
New Comment:
> [â¦] we could just use the number of elements in the
>
bound_params hashtable to check for a sufficient number of
> parameters.
That would not solve the issue reported as bug #79131, though.
Previous Comments:
------------------------------------------------------------------------
[2020-01-17 12:25:58] cmb@php.net
I can confirm the reported behavior for PDO_MySQL (using mysqlnd).
If emulated prepares are active (these are not supported by all
drivers), the behavior seems to be correct; however, executing the
prepared statement again without passing $input_parameters for
consecutive calls, will also reuse the parameters which have been
bound for the first call. It's not totally clear to me, whether
this is done deliberately. If it is, this issue would not be an
issue of PDO_Core.
Anyhow, the problem with PDO_MySQL native prepares is that
params_given is increased for each parameter, but never reset.
Actually, that counter might not be needed at all, since we could
just use the number of elements in the bound_params hashtable to
check for a sufficient number of parameters[1].
For PDO_SQLite, which does not support emulated prepares, we would
have to call sqlite3_clear_bindings() before re-binding (i.e. on
PDO_PARAM_EVT_FREE); however, the behavior of SQLite3 wrt. unbound
parameters is somewhat special anyway, since it assumes NULL has
been bound. To be consistent with (most?) other drivers, the case
of unbound parameters would have to be caught explicitly.
While I agree that these inconsistencies should be fixed, this
might better be postponed to PHP 8 for BC reasons. After all, a
lot of code might deliberately use the automatic rebinding of
formerly passed values.
[1] <https://github.com/php/php-src/blob/php-7.3.13/ext/pdo_mysql/mysql_statement.c#L483>
------------------------------------------------------------------------
[2020-01-16 15:34:09] love at sickpeople dot se
Description:
------------
When executing the same statement, missing parameter values are filled in with values from earlier
executions instead of throwing an error.
This requires emulated PREPARE to be disabled.
Test script:
---------------
$host = '';
$db = '';
$user = '';
$pass = '';
$options = [
PDO::ATTR_EMULATE_PREPARES => false, /* required */
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$pdo = new PDO("mysql:host=$host; dbname=$db; charset=utf8mb4", $user, $pass, $options);
$stmt = $pdo->prepare('select ? a, ? b');
$set = [
['a', 'b'],
[1 => 'y'], /* first parameter is missing. Note the array key */
['x'], /* second parameter is missing */
];
foreach ($set as $params) {
try {
var_dump($stmt->execute($params), $stmt->fetchAll(PDO::FETCH_ASSOC));
}
catch (Throwable $error) {
echo $error->getMessage() . "\n";
}
}
Expected result:
----------------
When emulated PREPARE is enabled, an error "SQLSTATE[HY093]: Invalid parameter number: number
of bound variables does not match number of tokens" is thrown. The same error should be thrown
instead of values being re-used.
Actual result:
--------------
bool(true)
array(1) {
[0]=>
array(2) {
["a"]=>
string(1) "a"
["b"]=>
string(1) "b"
}
}
bool(true)
array(1) {
[0]=>
array(2) {
["a"]=>
string(1) "a"
["b"]=>
string(1) "y"
}
}
bool(true)
array(1) {
[0]=>
array(2) {
["a"]=>
string(1) "x"
["b"]=>
string(1) "y"
}
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=79132&edit=1