$node->uid should come from node table not node_revisions

rcorsaro - December 5, 2007 - 21:20
Project:Drupal
Version:6.x-dev
Component:node.module
Category:bug report
Priority:critical
Assigned:Unassigned
Status:closed
Description

I am using Drupal 6.x beta3. When a user creates a blog entry, and then an administrator edits the blog entry, the breadcrumb for the blog shows the creaters name, but links to the administrators blog page. I have confirmed that this bug exists in a fresh beta3 install. Here is the code responsible for creating the breadcrumb, starting at line 104 of blog.module:

/**
* Implementation of hook_view().
*/
function blog_view($node, $teaser = FALSE, $page = FALSE) {
  if ($page) {
    // Breadcrumb navigation
    drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("@name's blog", array('@name' => $node->name)), 'blog/'. $node->uid)));
  }
  return node_prepare($node, $teaser);
}

I will research this further and hopefully supply a patch.

#1

rcorsaro - December 6, 2007 - 04:07

The problem is the uid is set to the last node_revision uid and not the node uid because the node_revision uid overwrites the node. This patch changes the order of the fields in the query. I'm not sure what kind of side effects it might have. Perhaps a better solution would be to add a named field like creator_uid or something and assign the uid from the node table to that. Maybe someone who knows the system better can comment on this.

Index: node.module
===================================================================
--- node.module (revision 1051)
+++ node.module (working copy)
@@ -650,8 +650,8 @@
   }

   // Retrieve a field list based on the site's schema.
-  $fields = drupal_schema_fields_sql('node', 'n');
-  $fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r'));
+  $fields = drupal_schema_fields_sql('node_revisions', 'r');
+  $fields = array_merge($fields, drupal_schema_fields_sql('node', 'n'));
   $fields = array_merge($fields, array('u.name', 'u.picture', 'u.data'));
   $fields = implode(', ', $fields);
   // rename timestamp field for clarity.

AttachmentSize
node-owneruid.patch659 bytes

#2

catch - December 6, 2007 - 09:41
Status:active» patch (code needs review)

#3

Rob Loach - December 6, 2007 - 13:44
Version:6.0-beta3» 6.x-dev

You might want to have a look at the Ugly Breadcrumbs issue as it might fix what you're experiencing.

#4

rcorsaro - December 6, 2007 - 16:31

The last patch was from my repo. Here is a patch from head:

Index: node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.916
diff -u -r1.916 node.module
--- node.module 6 Dec 2007 09:58:32 -0000 1.916
+++ node.module 6 Dec 2007 16:29:56 -0000
@@ -685,8 +685,10 @@
   }

   // Retrieve a field list based on the site's schema.
-  $fields = drupal_schema_fields_sql('node', 'n');
-  $fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r'));
+  // NOTE: If node_revisions comes after head then uid is the last person
+  // to edit the node rather then the person who created the node.
+  $fields = drupal_schema_fields_sql('node_revisions', 'r');
+  $fields = array_merge($fields, drupal_schema_fields_sql('node', 'n'));
   $fields = array_merge($fields, array('u.name', 'u.picture', 'u.data'));
   $fields = implode(', ', $fields);
   // rename timestamp field for clarity.

AttachmentSize
node-owneruid-1.patch940 bytes

#5

Lynn - December 6, 2007 - 21:09
Title:Individual Blog Page Breadcrumbs Problem» $node->uid should come from node table not node_revisions
Component:blog.module» node.module

Changing title and component because this issue seems larger than the blog module. The patch may fix the problem in the blog module but not sure how this change will affect other modules. If $node->name is the original author of the node, it seems as though $node->uid should match that rather than coming from node_revisions table.

#6

Lynn - December 6, 2007 - 21:27

New patch. This one replaces r.uid with u.uid in the node_load query.

AttachmentSize
node-load-uid.patch943 bytes

#7

catch - December 7, 2007 - 00:19
Status:patch (code needs review)» patch (code needs work)

Lynn, your patch manually changes the CVS id tag.

I have a weird feeling this was introduced in Drupal 5 as a feature. I agree it's a bug though, authoring information is there for a reason, nodes shouldn't change ownership just because someone fixes a typo.

#8

Lynn - December 7, 2007 - 01:00

Sorry about that. Is this patch correct now? (I am trying to get the hang of modifying Textmate's diff's to make patches.)

Comparing node_load in Drupal 5 and 6, it just looks like the way the query is formed was rewritten and that it was an oversight that the uid ends up now coming from node_revisions. I doubt it was on purpose. In drupal 5 the query is

<?php
SELECT n
.nid, r.vid, n.type, n.status, n.created, n.changed, n.comment, n.promote, n.sticky, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid ...
?>

and in drupal 6 we first make an array of fields to grab and then right the query
<?php
$fields
= drupal_schema_fields_sql('node', 'n');
$fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r'));
$fields = array_merge($fields, array('u.name', 'u.picture', 'u.data'));
?>

AttachmentSize
node-load-uid.patch797 bytes

#9

mcarbone - December 7, 2007 - 02:27
Status:patch (code needs work)» patch (code needs review)

For some reason the patch was malformed and wouldn't apply as attached, so I removed a few of the contextual lines to get it to apply. I've attached this version.

AttachmentSize
node-load-uid_1.patch756 bytes

#10

mcarbone - December 7, 2007 - 02:33

As for reviewing it, it works as advertised. However, I'm new to D6 and the drupal_schema_fields_sql function, so I'm not sure the string replace solution is good practice. The other solution would be to rewrite the node_revisions call to add all needed fields explicitly, and to add u.uid explicitly. I've attached a patch that does it this way.

AttachmentSize
node-load-uid_2.patch1.14 KB

#11

moshe weitzman - December 7, 2007 - 02:44
Status:patch (code needs review)» patch (code needs work)

i think it is better to preserve the revison uid so add this instead:

$fields = str_replace('r.uid', 'r.uid AS revision_uid', $fields);

this will avoid clobbering the node uid ... my proposal needs testing.

#12

moshe weitzman - December 7, 2007 - 02:44
Priority:normal» critical

we cannot ship a broken node_load.

#13

Lynn - December 7, 2007 - 16:40
Status:patch (code needs work)» patch (code needs review)

I did a print $fields inside node_load and realized it actually was calling all kinds of duplicate fields:

<?php
n
.nid, n.vid, n.type, n.language, n.title, n.uid, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, n.tnid, n.translate, r.nid, r.vid, r.uid, r.title, r.body, r.teaser, r.log, r.timestamp AS revision_timestamp, r.format, u.name, u.picture, u.data
?>
It seems the best thing to do is specify the actual fields we want called rather than using drupal_schema_fields_sql (mcarbone's point extended). I also included 'r.uid as revision_uid' ala moche since I didn't think that could do any harm to include that field. Patch needs testing.

AttachmentSize
node-load-specific-fields.patch1.29 KB

#14

catch - December 7, 2007 - 17:02
Status:patch (code needs review)» patch (code needs work)

node_load is designed to incorporate fields from contrib modules, so explicitly can't specify each field individually.

edit: I don't see any duplicate fields in your example, and would array_unique solve it if there was?

#15

mcarbone - December 7, 2007 - 17:26

catch, it already is specifiying each field individually, just by virtue of the drupal_schema_fields_sql function, which includes every field in the table. Contrib content is added in elsewhere.

Some of the "duplicate" fields Lynn points out aren't a problem, because they have the same value, but it comes off to me as ambiguous to include duplicate fields just because we have a function that conveniently dumps all fields in.

The Drupal 5 query specifically uses the title and body from the node_revisions table, not the node table, and HEAD is now including both.

In other words, I think this is a bigger problem than just uid, if not in effect than definitely in clarity.

#16

Lynn - December 7, 2007 - 17:45
Status:patch (code needs work)» patch (code needs review)

I agree with mcarbone. I guess 'duplicate' was a poor wordchoice of mine. PHP sees n.title and r.title as two separate things but running the query like that is just confusion, since only one will survive. I think specifying the fields explicitly as in Drupal 5 is the clearest method.

#17

rcorsaro - December 7, 2007 - 17:46
Status:patch (code needs review)» patch (code needs work)

Wouldn't changing the fields returned be an api change and potentially effect many, many things? If something relies on uid being the last editor, and something else relies on uid being the creator, then an api change is what is needed. In the short term, adding revision_uid might help, but ultimately, the data structure is at at fault. It seems that there should be a latest_revision field that holds all the revision fields, or even an array of revision structures.

#19

Gábor Hojtsy - December 7, 2007 - 18:06

rcorsaro: We should do what Drupal 5 did. If we don't do that, we are in error (unless someone knows about any intention of changing this behavior). To me this seems like a bug introduced with the schema columns automatically being used.

#20

mcarbone - December 7, 2007 - 18:30

I agree. Here's a patch that brings it back to what Drupal 5 did. Moshe's idea of adding revision_uid is a good one, but probably should be dealt with elsewhere, as it is a (minor) change of functionality.

This still needs to be tested, though, because it's possible that in the course of D6 development someone came to rely on one of the extra fields, or something similarly erroneous.

AttachmentSize
node-load.patch2.17 KB

#21

mcarbone - December 7, 2007 - 18:31
Status:patch (code needs work)» patch (code needs review)

#22

moshe weitzman - December 7, 2007 - 19:52
Status:patch (code needs review)» patch (code needs work)

no, we should not revert to D5 behavior of specifying fields. It is desirable that the resulting SQL is the same as before, but we mustn't get there by specifying individual fields. This change is deliberate. The intent is that one can add fields to the node table and declare those fields with schema API and they will automatically get loaded by this code and automatically saved by drupal_write_record().

#23

mcarbone - December 7, 2007 - 20:33
Status:patch (code needs work)» patch (code needs review)

Ah, now I understand why drupal_schema_fields_sql is used -- as I said in my first comment above, I'm new to this function. I've added a patch that removes/renames the conflicting fields depending on the case.

And in that case, since we're adding all fields, it seems like there's no good reason to throw away the additional fields that Drupal 5 never included. Plus, moshe's addition of revision_uid now makes sense. So I left in all fields except for those that could conflict (n.title) or are redundant (n.vid, r.nid).

Here are the new fields that get added via the drupal_schema_fields_sql call, and moshe's suggested addition:

n.language
n.moderate
n.tnid
n.translate
r.uid AS revision_uid

I see no harm in these being added, but I can remove them explicitly if there's a good argument against them being there.

AttachmentSize
node-load_1.patch1.17 KB

#24

Lynn - December 7, 2007 - 20:41
Status:patch (code needs review)» patch (code needs work)

I just did the same kind of thing mcarbone, but I think using array_diff makes it a little more concise.

AttachmentSize
node-load-array-diff.patch895 bytes

#25

mcarbone - December 7, 2007 - 20:55
Status:patch (code needs work)» patch (code needs review)

True dat, Lynn, that's much more concise. Here it is with your concision, and moshe's suggestion to keep revision_uid.

AttachmentSize
node-load_2.patch1.16 KB

#26

Lynn - December 9, 2007 - 23:39
Status:patch (code needs review)» patch (reviewed & tested by the community)

Patch tested and works- solves original problem and still allows for schema changes.

#27

Gábor Hojtsy - December 10, 2007 - 09:00
Status:patch (reviewed & tested by the community)» patch (code needs work)

IMHO the comment should include info on what happens after all. This one is not clear to me:

// Remove conflicts between node and node_revisions fields.

So what happens then? Which value is used from which table after this? Document this please in the code.

#28

mcarbone - December 10, 2007 - 09:55
Status:patch (code needs work)» patch (code needs review)

Rewrote comment for clarity.

AttachmentSize
node-load_3.patch1.29 KB

#29

catch - December 10, 2007 - 09:58
Status:patch (code needs review)» patch (code needs work)

mcarbone: comments have to wrap at line 79/80

#30

Gábor Hojtsy - December 10, 2007 - 10:53
Status:patch (code needs work)» fixed

I re-wrapped the comments and committed. Thanks!

#31

Anonymous - December 24, 2007 - 11:01
Status:fixed» closed

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

 
 

Drupal is a registered trademark of Dries Buytaert.