Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP PDO in Exception Mode - Still need to check return of execute function (and others)?

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.

like image 900
dnag Avatar asked Feb 20 '17 20:02

dnag


People also ask

What is returned by the exec () method of the PDO class?

Return Values ¶ PDO::exec() returns the number of rows that were modified or deleted by the SQL statement you issued. If no rows were affected, PDO::exec() returns 0 . This function may return Boolean false , but may also return a non-Boolean value which evaluates to false .

Which function is used to execute the query in PDO?

To prepare and execute a single SQL statement that accepts no input parameters, use the PDO::exec or PDO::query method. Use the PDO::exec method to execute a statement that returns no result set.

How check PDO query is successful in PHP?

How check PDO query is successful in PHP? To determine if the PDO::exec method failed (returned FALSE or 0), use the === operator to strictly test the returned value against FALSE.

What PHP PDO function do you use to get your PDO object ready to run a query?

PDO::query() prepares and executes an SQL statement in a single function call, returning the statement as a PDOStatement object.


1 Answers

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:

  • PHP PDO Postgres versus Sqlite column type for count(*)
  • PHP Postgres PDO driver does not support prepared statement?
like image 54
CXJ Avatar answered Oct 20 '22 23:10

CXJ