An issue came up in D6:
#638702: insertion errors
The crux of that issue was that in D6, after an update query that asked to update a row to the same values previously in the DB, depending on your db/driver, db_affected_rows() might return 0 (see that issue's comments for details).

So the question is: In D7 do we have a similar problem?

Berdir, davereid, and I were discussing it in IRC. I wrote a test, which currently fails (at least on my machine), which shows that if you do an update in D7, the return value has the same issue as db_affected_rows(). That is, if you update a db row with the same values it had before, the return value of db_update()->execute() is not 1. If you update a db row with different values from what it had before, the return value is 1.

Now. Since (sigh) the UpdateQuery class is almost entirely lacking in documentation, there is no doc that says what the return value should be (number of rows actually changed vs. number of rows found). But I bet that somewhere in the code base, someone is doing similar logic to what was in the above issue in D7. I'll check for that shortly, and add a comment.

So I'm not sure if this is a bug or not, but I'm filing the issue so it can be discussed.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jhodgdon’s picture

FileSize
1.58 KB

Here's the patch that adds a (failing) test for this.

jhodgdon’s picture

I looked through the entire code base to find how db_update()->execute() return values were being used (usually they are ignored):
1) drupal_write_record() seems to be assuming it's returning the ID of the record affected? That code sure looks weird and bad.
2) lock_acquire() -- I'm not sure, probably not a problem. I think every time it updates it should be a new value, so probably OK.
3) comment_operations() - not sure if this is an issue or not, but it's returning update query objects (unexecuted) for other functions to use, apparently?
4) node_type_update_nodes() - this one is currently OK, as it explicitly says it returns the number of nodes whose types were changed. But if we change the return value to pass my test above, then it will probably break this function's return value?
5) There are quite a few tests in database_test.test that use db_update's return value, but as they are all passing now, they are probably testing actually changing rows in the DB.
6) SystemQueue::claimItem() and releaseItem() -- not sure if these are OK or not.

I am pretty sure these are the only places in core where the return value of db_update()->execute() is used.

Crell’s picture

This is an issue with the underlying PDO behavior that was discovered during the original DBTNG implementation, and was one of the main reasons we added MergeQuery in the first place. UpdateQuery::execute() will always return the number of items affected, not matched.

If anyone is using the return value of UpdateQuery to determine the number of records matched (eg, if a given record exists), then they're doing it wrong and have a bug they need to fix.

That is documented here: http://drupal.org/node/310080 and in the docblock of DatabaseConnection::query().

I dispute that update queries are "almost entirely undocumented". :-) What specific piece of documentation is missing or needs to be repeated somewhere else?

jhodgdon’s picture

Well, most of the query classes and interfaces are missing doc on the methods, for instance. Where's the return value of db_update()->execute() documented?

Anyway, I this this is a doc issue, assuming that none of those places in the code in #2 is doing something wrong. Need to check that first, then move this to the doc component.

Damien Tournoud’s picture

To be more precise: this is not at all a bug. The affected number of rows is what god intended the UPDATE queries to return.

Crell’s picture

Title: Update queries return value » Better document built query return values in the query builders themselves
Category: bug » task

All methods are documented in their interface, rather than in the class, since they are used multiple times by design. I will agree with you, though, that we should probably add additional documentation to the execute() methods to specify which of the possible return types from query() we are expecting to use.

jhodgdon’s picture

Component: database system » documentation
Category: task » bug

It's a doc bug then, not a database task. :)

There is a meta-issue now for documenting classes in Drupal in general:
#718576: [Meta] API documentation for classes and interfaces is incomplete

There is also an issue for discussing standards for documenting classes and methods:
#718596: Lacking standards for documenting classes, interfaces, methods

jhodgdon’s picture

Looking through the functions that are using the return value of db_update() queries to make sure no one is making an error of logic like the one that was there in search.module for D6...

They all look fine to me, except drupal_write_record():

if( something) {
   $query = db_insert($table, $options)->fields($fields);
}
else {
   $query = db_update($table)->fields($fields);
}
// ...
  if ($last_insert_id = $query->execute()) {

That looks wrong. If the db_update succeeds in updating a row, this if() will be true and it will think it's the last inserted ID? WTF?

Crell’s picture

That's... wrong. I'm not sure what it should be doing there, but not that. :-) That should be filed as a separate issue, methinks.

jhodgdon’s picture

RE: #8: #719468: drupal_write_record() code is written in a confusing way

So can anyone interested in this issue as a doc bug please go comment on #718596: Lacking standards for documenting classes, interfaces, methods, because I would like us to have standards on what exactly needs to have docblocks in classes. Thanks...

quicksketch’s picture

But I bet that somewhere in the code base, someone is doing similar logic to what was in the above issue in D7. I'll check for that shortly, and add a comment.

/me raises hand. Doh. Thanks for raising this issue.

jhodgdon’s picture

Status: Active » Postponed

This needs to be postponed until coding standards for classes have been defined. See #7 above.

jhodgdon’s picture

Status: Postponed » Active

Doc standards are now defined:
http://drupal.org/node/1354#classes

freelock’s picture

Status: Active » Needs review
FileSize
14.8 KB

Created initial stab/patch at documenting the query objects.

jhodgdon’s picture

FileSize
33.1 KB

That was an excellent start. I went through the file and found some more things needing fixes. Here's a new patch.

jhodgdon’s picture

#15: 718540-morefixes.patch queued for re-testing.

jhodgdon’s picture

#15: 718540-morefixes.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 718540-morefixes.patch, failed testing.

jhodgdon’s picture

Title: Better document built query return values in the query builders themselves » Better documentation in query.inc
Status: Needs work » Needs review
FileSize
31.48 KB

Here's a rerolled patch.

Status: Needs review » Needs work

The last submitted patch, 718540-19.patch, failed testing.

jhodgdon’s picture

Hmph. The failed test is:

The allowed text format appears as an option when adding a new node.
filter.test 482
FilterFormatAccessTestCase->testFormatPermissions()

This obviously has nothing to do with this patch. Just recording here for posterity...

jhodgdon’s picture

Status: Needs work » Needs review

#19: 718540-19.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 718540-19.patch, failed testing.

Crell’s picture

+++ includes/database/query.inc	22 Oct 2010 14:44:55 -0000
@@ -296,12 +325,6 @@
    *
-   * Note that this method must be called by reference as well:
-   *
-   * @code
-   * $comments =& $query->getComments();
-   * @endcode
-   *

We shouldn't remove this bit. It's a weird syntax people may well forget if it's not mentioned explicitly.

+++ includes/database/query.inc	22 Oct 2010 14:44:55 -0000
@@ -664,26 +763,51 @@
-   * The table from which to delete.
+   * The table from which to truncate.

I think technically one "deletes from" a table, or one "truncates" a table. One doesn't "truncate from", since the object of the action is the table, not the rows in it. So if we're changing to truncate here, then the line should read "The table to truncate."

+++ includes/database/query.inc	22 Oct 2010 14:44:55 -0000
@@ -747,44 +884,66 @@
    *   An associative array of fields to write into the database. The array keys
-   *   are the field names while the values are the values to which to set them.
+   *   are the field names, and the values are the values to which to set them.

Is the comma there really necessary?

Otherwise this looks great.

Powered by Dreditor.

jhodgdon’s picture

Status: Needs work » Needs review
FileSize
31.65 KB

Good nitpicks, thanks for the review! :)

Here's a slightly altered patch.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

Coming from you, that's high praise. Especially in a thread that is all about nitpicking. :-)

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.