PDO not throwing exception with unbound parameters (and no variables in query)
That behavior is reproducible with the current PHP (5.6.13), and the query is not even sent to the server.
Your case is described in the doc as:
You cannot bind more values than specified; if more keys exist in input_parameters than in the SQL specified in the PDO::prepare(), then the statement will fail and an error is emitted.
0 value is expected, 1 value is given, and the statement fails, false
being returned. So far, works as documented.
You may argue that "an error is emitted" would imply that when ERRMODE_EXCEPTION
is on, an exception would be thrown. That's an argument, but it's not obvious that the PDO developers would agree with it.
Update:
Why is SQLCode
not set?
Looking at PDO source code, specifically static PHP_METHOD(PDOStatement, execute)
that handles PDO::execute(), you can see that all errors are handled by a macro: PDO_HANDLE_STMT_ERR()
#define PDO_HANDLE_STMT_ERR() if (strcmp(stmt->error_code, PDO_ERR_NONE)) { pdo_handle_error(stmt->dbh, stmt TSRMLS_CC); }
The point is that, when passing a bound parameter when PDO expected none, the query never makes it to the SQL engine, so the SQL engine never has the opportunity to report an error accompanied with an SQLSTATE
PDO itself does not create a fake SQLSTATE
on its own, at least no in that case, sostmt->error_code
stays at PDO_ERR_NONE
which is "00000"
.
It's understandable that you would prefer an exception to be raised, but then you should suggest that to https://bugs.php.net
Is it the same with MySQL ?
Yes, the root behavior is the same except that with the MySQL driver, the prepare
is sent immediately to the SQL engine so if it's incorrect because of a bad column, it fails earlier and with a real SQL error. On the other hand, the PgSQL driver has a different implementation that makes it defer the server-side prepare
. This particular behavior is discussed in detail at PHP Postgres PDO driver does not support prepared statement?
Anyway, here's a case with MySQL that demonstrates my explanation, that is:
- the query expects 0 parameter, 1 is given
$stmt->execute
returns false- no exception gets raised
- PDO::errorCode is
00000
Code:
$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$stmt = $link->prepare("SELECT 1");
$rc=$stmt->execute(array(1));
if ($rc===false)
echo "query failed, errorCode=", $link->errorCode(), "\n";
else
echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
print "A PDOException has occurred";
print $e->getMessage();
}
Result:
query failed, errorCode=00000
What happens under the hood is that the prepare
is sent to the server and succeeds, but the execute
step is cancelled by PDO due to the mismatch in parameters.
Here's a case that differs in the fact that the query refers to a non-existing column. I'm adding a print to show that $stmt->execute
is not even called, as the exception is raised by $stmt->prepare
Code:
$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$stmt = $link->prepare("SELECT nonexisting");
echo "Executing query\n";
$rc=$stmt->execute(array(1));
if ($rc===false)
echo "query failed, errorCode=", $link->errorCode(), "\n";
else
echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
print "A PDOException has occurred";
print $e->getMessage();
}
Result:
A PDOException has occurredSQLSTATE[42S22]: Column not found: 1054 Unknown column 'nonexisting' in 'field list'
Note how the "Executing query" step never happens, because it's the prepare
that fails, server-side.
Conclusion
when the query is sent to the server, be it in prepare() or execute(), and it's the server that generates an error, then we can expect a PDOException to be raised.
when the query is not sent to the server for an execution step, then PDO execute() can fail (returns false) but no exception is thrown and
errorCode()
stays at00000
This was a bug in PDO and NikiC has recently fixed it. See bugs #72368 and #79131.
The problem was that PDO was not checking for errors coming from EVT_ALLOC
. This was one of a number of issues related to error reporting that got fixed in the past few months.
If any of the methods in PDO return false without throwing an exception in the exception mode, then it is a bug. Please report any future bugs to https://bugs.php.net/ and if possible suggest a GitHub PR to fix it.