db_distinct_field does not produce valid MySQL 4.1 or MySQL 5.0 statements when the field to be made distinct is not the first one in the query. It should produce a statment of the form "SELECT DISTINCT t2.f2, t1.f1, t2.f3 ... FROM ...". Instead it produces statements of the form "SELECT t1.f1, DISTINCT(t2.f2), t3.f3 ... FROM ..."
I do not know whether it is supposed to always be called with $table and $field containing values that must be those for the first field in a SELECT statement; the documentation suggests not.
This is manifesting for me in several ways. Enabling access control in organic groups on a 5.1, restricting access to group member for group content, then navigating to a node for group that the user is a member of, leads to the following error:
user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT(n.nid), n.changed FROM node n INNER JOIN term_node tn ON n.nid = tn.nid' at line 1 query: eval SELECT n.title, DISTINCT(n.nid), n.changed FROM node n INNER JOIN term_node tn ON n.nid = tn.nid WHERE n.type = 'page' AND tn.tid = 14 AND n.status = 1 in /home/.marf/prideweb/pride.dreamhosters.com/drupal/includes/database.mysql.inc on line 172.
user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT(n.nid), n.changed FROM node n INNER JOIN term_node tn ON n.nid = tn.nid' at line 1 query: eval SELECT n.title, DISTINCT(n.nid), n.changed FROM node n INNER JOIN term_node tn ON n.nid = tn.nid WHERE n.type = 'promotion' AND tn.tid = 14 AND n.status = 1 in /home/.marf/prideweb/pride.dreamhosters.com/drupal/includes/database.mysql.inc on line 172.
A 4.7 site produces the following error when taxonomy_access 4.7-x.1 is activated (it's trying to display flexinode data in a block):
user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT(n.nid) FROM node AS n INNER JOIN flexinode_data AS f USING (nid) WHERE' at line 1 query: eval SELECT f.numeric_data, n.title, DISTINCT(n.nid) FROM node AS n INNER JOIN flexinode_data AS f USING (nid) WHERE n.type = 'flexinode-2' AND f.field_id=3 ORDER BY f.numeric_data DESC LIMIT 5; in /var/www/virtual/ondp.ca/webroot/htdocs/includes/database.mysql.inc on line 120.
There is no need in MySQL to insert () around the field. As there are a large number of posts in the forum about errors with DISTINCT(), and some effort seems to have been spent on the ordering of the JOIN tables and fields in SELECTS, I thought I'd post an issue rather than try to change the code. From my simple perspective looking only at MySQL, the fix would involve just inserting the DISTINCT after the SELECT. If this involves creating separate versions of the function for different databases, so be it. But I haven't really figured out all the optimization issues, etc.
| Comment | File | Size | Author |
|---|---|---|---|
| #16 | distinct_fix.patch | 2.57 KB | colan |
| #12 | 130242.patch | 2.57 KB | roychri |
Comments
Comment #1
dan3h commentedMaybe the original code works with Postgres, but it definitely doesn't work for MySQL. The following function needs to be changed in
On a 5.1 site, I fixed it like this (tested with MySQL 5.0.24):
The original version in 5.1 looked like this:
On a 4.7 site (which I tested with MySQL v4.1.20), the change is nearly exactly the same, but is in an un-refactored version of the functions above. It is in
in the function
db_rewrite_sql.Before:
After:
Comment #2
dan3h commentedIt was pointed out to me that in MySQL, the DISTINCT keyword appears only right after the SELECT statement, and it applies to the entire row. Thus, there is no need to parse a single field out of the select list and pull it to the front. Hence, the fix is *much* simpler.
5.1 version:
4.7 version (in the
db_rewrite_sqlfunction):Comment #3
q_man commentedI am a new Drupaller and have no real understanding of this issue raised here but would like to thank you for taking the time to report it and offer a fix. Since you have provided code have changed the status to patch(code needs review) so this should get some faster attention rom the maintainers.
Comment #4
dan3h commentedYet another improvement to this code. We found a piece of code where
select count(DISTINCT(thingy))...was being passed into this function, and it was coming out asselect DISTINCT count(DISTINCT(thingy))..., which is too many DISTINCTs.So here is the yet-again revised 5.1 version (put in database.mysql.inc and database.mysqli.inc):
...and the yet-again revised 4.7 version (still in the
db_rewrite_sqlfunction):BTW, thank you, q_man, for setting this node to "patch (code needs review)"-- I'm also a new drupaller and didn't realise I could do that.
Comment #5
q_man commentedYou're welcome.
BTW. I came across your post when I was searching for a solution that would enable me to make page with a number of tables which list images on a site, by author, associated project and keyword but only giving one sample image/ result per author, project or keyword respectively.
I have created a view which does something like this but have yet to find a way of making it turn out the single example. Would the DISTINCT command be useful here? can I code it into my views? It seems you have a good grap of PHP mysql, any ideas how I could code it? I'm afraid I'm no programmer...
to give you a better idea of what I'm trying to do my work in progress is here:
http://www.disscamera.org.uk/galleries
Comment #6
markdionne commentedThanks for the fix. In my case
SELECT node_data_field_person.field_person_nid AS nid FROM {node}was incorrectly getting changed toSELECT node_data_field_person.field_person_DISTINCT(node.nid) AS nid FROM {node}. The incorrect query was being generated by the Views Module but only started failing after I started using the Organic Groups (og) module.My site is Drupal 4.7.6, but the code looks like the code above that is marked as 5.1, and that's what I used successfully.
Comment #7
bdragon commentedCan't review a nonexistent patch.
Comment #8
cburschkaI know a bit about MySQL, but I'm fuzzy on what this function is meant to do. It states its objective only in terms of invalid syntax ("wrapping fields in distinct()" is as mentioned not possible in MySQL), and it doesn't explain what this is actually meant to accomplish in the results.
If a select query returns this:
What should the equivalent of "wrapping the field in distinct()" do when done to "Last Name" - this?
DISTINCT can only be used on the entire set of selected values. In our example "SELECT DISTINCT" wouldn't have an effect because no rows are exactly alike. It would only work when selecting only a subset of those fields that has duplicates. If the result above is what is intended, this is done with "GROUP BY".
However, DISTINCT can also be used inside an aggregate function like COUNT, and then it will only apply to the fields in the COUNT function.
I'd be happy to patch this, but I don't know what the function should actually do.
Comment #9
cburschkaDid this ever get solved?
Comment #10
Shaney commentedFurther example:
When fed the following $query, db_distinct_field() works fine:
However when fed:
It spits out
The table and field parameters are node and nid for both examples. The example comes from views_fastsearch, the difference being the working query is 'lorem ipsum' and the broken one is 'lorem -ipsum'
Comment #11
Shaney commentedOK, this is happening because of the second FROM, which allows a match on the the node.nid in the JOIN (and could allow it on the one in the AND).
Comment #12
roychri commentedHere is a patch that works on Drupal 5.7
Comment #13
drummThe same code still exists in the current development version of Drupal, so moving forward for a thorough review.
Comment #14
woc_art commentedThank you, this fixed a distinct error I was getting with the alpha pager module!
Comment #15
emok commentedSome further examples of where the MySQL versions of db_distinct_field('node', 'nid', $query) in Drupal 6.2 fails:
SELECT DISTINCT(node.nid) nid, node.type FROMbecomesSELECT DISTINCT(node.nid) DISTINCT(node.nid), node.type FROM.SELECT DISTINCT(node.nid) AS nid, node.type FROMbecomesSELECT DISTINCT(node.nid) AS DISTINCT(node.nid), node.type FROM.I expected the queries with alias to be unchanged. Or are these input-queries already invalid in MySQL if the DISTINCT keyword is supposed to be only at the start of query (not around a field)?
The example is based on a query from Views when showing uploaded files for nodes, and enbabling the "distinct" option to avoid showing a node as many times as there are attached files to it.
You may want to check the other DISTINCT-issue http://drupal.org/node/206224, in case you haven't found it.
Comment #16
colan#12 works really well for me on 5.7, and I've got multiple levels of nested selects. Marking this as RTBC. I'm rerolling the patch, but all I changed was the bad spelling of the word "DISTINCT".
Comment #17
roychri commentedcolan: Is this new patch rerolled for 7.x ?
Comment #18
colanNo, I suppose it's not. Good point. According to #13, the code is the same, but this should definitely be tested.
Comment #19
roychri commentedThis patch needs more than review. It needs to be changed/rerolled for 7.x.
I am changing the status.
colan: Thanks for testing the one that was there though! :)
Comment #20
moonray commentedHopefully this will also be back-ported for D5 and D6.
Comment #21
Crell commentedThis function no longer exists in Drupal 7. Downgrading to Drupal 6, in case someone wants to work on it there.
Although, could this be in some way related to #284392: db_rewrite_sql causing issues with DISTINCT? Perhaps a dupe?
Comment #22
joe.murray commented