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.
Comment | File | Size | Author |
---|---|---|---|
#25 | 718540-25.patch | 31.65 KB | jhodgdon |
#19 | 718540-19.patch | 31.48 KB | jhodgdon |
#15 | 718540-morefixes.patch | 33.1 KB | jhodgdon |
#14 | 718540.patch | 14.8 KB | freelock |
#1 | 718540.patch | 1.58 KB | jhodgdon |
Comments
Comment #1
jhodgdonHere's the patch that adds a (failing) test for this.
Comment #2
jhodgdonI 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.
Comment #3
Crell CreditAttribution: Crell commentedThis 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?
Comment #4
jhodgdonWell, 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.
Comment #5
Damien Tournoud CreditAttribution: Damien Tournoud commentedTo be more precise: this is not at all a bug. The affected number of rows is what god intended the UPDATE queries to return.
Comment #6
Crell CreditAttribution: Crell commentedAll 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.
Comment #7
jhodgdonIt'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
Comment #8
jhodgdonLooking 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():
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?
Comment #9
Crell CreditAttribution: Crell commentedThat's... wrong. I'm not sure what it should be doing there, but not that. :-) That should be filed as a separate issue, methinks.
Comment #10
jhodgdonRE: #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...
Comment #11
quicksketch/me raises hand. Doh. Thanks for raising this issue.
Comment #12
jhodgdonThis needs to be postponed until coding standards for classes have been defined. See #7 above.
Comment #13
jhodgdonDoc standards are now defined:
http://drupal.org/node/1354#classes
Comment #14
freelockCreated initial stab/patch at documenting the query objects.
Comment #15
jhodgdonThat was an excellent start. I went through the file and found some more things needing fixes. Here's a new patch.
Comment #16
jhodgdon#15: 718540-morefixes.patch queued for re-testing.
Comment #17
jhodgdon#15: 718540-morefixes.patch queued for re-testing.
Comment #19
jhodgdonHere's a rerolled patch.
Comment #21
jhodgdonHmph. 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...
Comment #22
jhodgdon#19: 718540-19.patch queued for re-testing.
Comment #24
Crell CreditAttribution: Crell commentedWe shouldn't remove this bit. It's a weird syntax people may well forget if it's not mentioned explicitly.
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."
Is the comma there really necessary?
Otherwise this looks great.
Powered by Dreditor.
Comment #25
jhodgdonGood nitpicks, thanks for the review! :)
Here's a slightly altered patch.
Comment #26
Crell CreditAttribution: Crell commentedComing from you, that's high praise. Especially in a thread that is all about nitpicking. :-)
Comment #27
Dries CreditAttribution: Dries commentedCommitted to CVS HEAD. Thanks.