10

TLDR: Does anyone know of specific cases, besides the one shown below, where PDO::exec(), PDO::query(), PDO::prepare(), or PDOStatement::execute() can return false without an exception being thrown, despite PDO::ATTR_ERRMODE being set to PDO::ERRMODE_EXCEPTION?

I'm trying to decide if I want to add a check for false to every database query I write going forward, or if that is redundant.

Edit: I currently use MySQL exclusively, but if portability is a factor, that might be enough to base the decision on. I use PDO::ATTR_EMULATE_PREPARES => FALSE if that matters.


This is the specific case I mentioned. If you prepare a statement with 0 or more placeholders (of either type) and then supply an array of arguments to PDOStatement::execute() with more elements than there are placeholders, false is returned without an exception being thrown. Note that the execution succeeds (and only the extra binding fails) if PDOStatement::bindValue() is used instead. Using fewer parameters than placeholders does throw an exception, whether the parameters were supplied to the execute function via an array or bound using PDOStatement::bindValue()/PDOStatement::bindParam().

// Execute returns false, no exception thrown
$so = $rh->pdo->prepare("SELECT * FROM config");
if($so->execute(['Test']) === FALSE) echo '1. False returned <br />';   

// Execute does not return false, no exception thrown
$so = $rh->pdo->prepare("SELECT * FROM config");
if($so->bindValue(1, 'Test') === FALSE) echo '2. Binding failed <br />';
if($so->execute() === FALSE) echo '2. False not returned <br />';   

// Execute returns false, no exception thrown
$so = $rh->pdo->prepare("SELECT * FROM config WHERE webmaster_name = ?");
if($so->execute(['Test', 'Wee']) === FALSE) echo '3. False returned <br />';    

// Execute does not return false, no exception thrown
$so = $rh->pdo->prepare("SELECT * FROM config WHERE webmaster_name = ?");
$so->bindValue(1, 'Test');
if($so->bindValue(2, 'Wee') === FALSE) echo '4. Binding failed <br />';
if($so->execute() === FALSE) echo '4. False not returned <br />';   

Outputs:
1. False returned
2. Binding failed
3. False returned
4. Binding failed

On a select statement, it isn't particularly dangerous in this case to rely on exceptions, as an error would occur anyway if you tried to call a fetch method on FALSE instead of a PDOStatement object. But there are queries like INSERTS or UPDATES that could silently fail on you if you don't do the false check.


I have been learning PDO and I am trying to decide on general practice for error handling going forward.

I definitely prefer exceptions, as I already have a good site wide handler that can be configured for different scenarios. And it seems using exceptions will mean less typing, since you don't have to check the returns of many PDO functions explicitly.

OR DO YOU? (cue dramatic music)

When I was reading up, I came across more than one mention of PDO functions (not the fetching variety) returning false without an exception being thrown.

My question is whether or not it is considered best practice to also check the return of those functions, or if most people consider that overkill.

I have seen a lot of conflicting statements on SO. I have seen numerous statements on one side: "PDO exceptions are reliable"; "PDO exceptions will always be thrown when FALSE would have been returned" (upvoted by people saying "I think is true").

These are some comments that are leading me to wonder, though I have yet to see a specific example besides the one I mentioned.


From Can PDO methods fail and not throw PDOException?:

I could not seem to replicate this scenario from a few test cases (I have emulation mode off).

  1. "(Ryan Vincent) when emulates is false and ... binding types are incorrect. It may not throw an exception sometimes."

This one seems to have been disproved? I'm not sure how to test this.

  1. "(Xorifelse) Now for committing to fail, I believe there is 1 scenario that would cause it to return false without throwing an exception and that is when the connection to the server drops after connecting to the database and before calling PDO::commit, quite good to know if you have a remote database server. So to answer your question, yes it can fail without throwing an exception, but its timing has to be very specific even more so if you have a local database."

From will a false returned PDO execute() the same as the exception it thrown?:

This is the only specific scenario I have come across that I have been able to duplicate (see above).

  1. "(Niksac) I have seen execute() returning false without throwing an exception which is kind of unexpected / bad behaviour in my opinion. This means that we basically have to do both - error and exception handling in parallel. In my case: if I prepared an insert query without any parameters and then executed with a parameter. Execute will not throw an exception but return false."

From Should I check the return value of an execute operation in pdo php:

  1. "(castis) $stmt->execute() can absolutely return false without throwing an exception."

I think I found at least one other non-specific mention of it being possible (false returned, no exception), though I can't track them down again.


Looking through PDO tutorials online, most of them don't check the returns whenever exception mode is used. But I did come across a couple people recommending it.

The case I described is not something I am likely to screw up in everyday use. Or if I do screw it up, I should find out immediately. And if I ever dynamically built the number of placeholders or parameters in the query, I would know to make sure the count matches and/or check for false in this one case.

I just don't want to check the return of every use of execute, query, etc. if it isn't really necessary. If the example I mentioned is the only known case, I would feel comfortable leaving the false check out of most queries, which would allow me to do more method chaining:

$user_info = $rh->pdo->query("SELECT * FROM users WHERE user_id = 1")->fetch();

// vs

$so = $rh->pdo->query("SELECT * FROM users WHERE user_id = 1");
if($so === FALSE) // Throw an exception
$user_info = $so->fetch();

I guess what I'm looking for is some reassurance from more experienced developers that it's okay to bypass this check as I'm seeing people do. Either that, or people telling me how they have been burned by bypassing that check.

Community
  • 1
  • 1
dnag
  • 335
  • 1
  • 9
  • Regarding ATTR_EMULATE_PREPARES - I have seen the behavior where PDO doesn't throw an exception if you provide params to a statement that should have none. I recall verifying that issue with another user just last week or so. I don't know if it makes a difference to explicitly `bindParam()` vs pass an array to `execute()` as you have in your false-returning example, but I've definitely seen PDO not throw an exception with the incorrect param array. – Michael Berkowski Feb 20 '17 at 20:23
  • A lot of this behavior depends on the underlying DBMS driver. PDO tries to standardize things as much as possible, but there are still variations. What database are you using here? MySQL, SQL Server, etc? – Jeff Puckett Feb 20 '17 at 21:23
  • Thanks for the responses guys. Exclusively MySQL at the moment. I don't see myself using another database any time soon. But if there are potential gotchas only with other databases that I might someday want to migrate to (or be forced to work with), that could still be a factor. I guess if portability is an issue in this case, then that might be enough to base the decision on. – dnag Feb 20 '17 at 21:41

1 Answers1

1

There are definitely different gotchas depending on which database you are using. If portability is a concern, I'd not rely on some of what might be MySQL idiosyncrasies that you seem to be finding, above.

For example, there are these:

Community
  • 1
  • 1
CXJ
  • 4,301
  • 3
  • 32
  • 62
  • Thanks for the reply. I have not been given much confidence by replies that it is OK to bypass the false checks. For now, I'm erring on the safe side and always checking, even though I have yet to see a different specific case where an exception is not thrown and false is returned. I disable emulated prepares if that matters. – dnag Apr 29 '17 at 16:59