-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Description
Summary
If a query binds many (i.e. around 1000) parameters, the method PDOStatement::execute()
fails and returns false
. Even though PDO::ERRMODE_EXCEPTION
is set as part of the PDO options no exception is thrown. PDOStatement::execute()
neither sets PDOStatement::errorCode()
. However, a subsequent fetchAll
returns an empty array with zero query results.
Note, that the bug can be worked around, if the binding are resolved manually. This mean if all placeholders '?'
inside the query string are replaced by their actual value before the query is passed to PDOStatement::prepare()
, then PDOStatement::execute()
does not fail and a subsequent PDOStatement::fetchAll()
returns the expected number of results.
The issue has been reported by a couple of our users of https://github.com/LycheeOrg/Lychee. We have tracked it down to the PDO layer, i.e. it is not a bug in our code nor in the Laravel framework. A MWE which triggers the bug is located in https://github.com/LycheeOrg/MySQL1000Bug. The bug only occurs for a couple of users not everyone. But if the bug occurs for a specific setup, then the bug occurs deterministically. So far, we have seen bug reports from users with PHP 8.1.2 and 8.1.5 – 8.1.7. We haven't seen any bug reports for 8.0.x, but this doesn't mean that the bug might not exist there as well. Among the core developer team @qwerty287 has been able to reproduce the faulty behaviour on his development machine.
As the bug only occurs for many query parameters and only for specific setups it smells like a memory corruption bug in the MySQL PDO code which is responsible for parameter binding.
Steps to reproduce the problem
- Checkout the repository https://github.com/LycheeOrg/MySQL1000Bug
- Create a new MySQL/Maria DB
- Load the included MySQL dump into the DB.
- Make a copy
config.php
of the provided config.php.example and adopt it to your DB settings. - Run the test script ./mysql_1000_bug.
Output if the bug occurs
Test #1 - Query without bindings
bind_values() : bound 0 values in total
select_query() : $statement->fetchAll returned 7380 results
check_db_result() : Everything seems fine :-(
Test #2 - Query with bindings
bind_values() : binding value __OcYDO3i7Bm9MaCx5SWJj_D as string to placeholder 1 succeeded
...
bind_values() : binding value Zzik4C_-V-WhyyYcn2QLlaEG as string to placeholder 1074 succeeded
bind_values() : bound 1074 values in total
select_query() : $statement->execute() failed without exception, oh, oh
select_query() : $statement->execute() returned error: 00000
select_query() : $statement->execute() returned error info:
array(3) {
[0]=>
string(5) "00000"
[1]=>
NULL
[2]=>
NULL
}
select_query() : $statement->fetchAll returned 0 results
check_db_result : fetched wrong number of size variants; got 0, expected 7380
Expected output if the bug does not occur
Test #1 - Query without bindings
bind_values() : bound 0 values in total
select_query() : $statement->fetchAll returned 7380 results
check_db_result() : Everything seems fine :-(
Test #2 - Query with bindings
bind_values() : binding value __OcYDO3i7Bm9MaCx5SWJj_D as string to placeholder 1 succeeded
...
bind_values() : binding value Zzik4C_-V-WhyyYcn2QLlaEG as string to placeholder 1074 succeeded
bind_values() : bound 1074 values in total
select_query() : $statement->fetchAll returned 7380 results
check_db_result() : Everything seems fine :-(
Further information
PHP version for which the bug has been observed: | 8.1.2 and 8.1.5 – 8.1.7 |
Downstream issue: | LycheeOrg/Lychee#1326 |
MWE repo: | https://github.com/LycheeOrg/MySQL1000Bug |