From the Postgres manual (at the bottom in the 'Namespace Available to GROUP BY and ORDER BY' section):
PostgreSQL also allows [GROUP BY] to specify arbitrary expressions. Note that names appearing in an expression will always be taken as input-column names, not as result-column names.
The problem with this is that Views 2 builds its queries with the alias of the base table's unique ID being the same as the field name. For example: a View with a base table of {users} will include users.uid AS uid
in its SELECT statement. Instead of doing this, Views should generate an alias with the same form of other fields, table_field. For example: users.uid AS users_uid
.
To reproduce, using a clean checkout of Drupal 6/Views 2.6:
- Ensure you Drupal/Views are setup to use a Postgres database ;)
- import the following View:
$view = new view; $view->name = 'postgres_test_02'; $view->description = ''; $view->tag = ''; $view->view_php = ''; $view->base_table = 'node'; $view->is_cacheable = FALSE; $view->api_version = 2; $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ $handler = $view->new_display('default', 'Defaults', 'default'); $handler->override_option('fields', array( 'nid' => array( 'label' => 'Nid', 'alter' => array( 'alter_text' => 0, 'text' => '', 'make_link' => 0, 'path' => '', 'link_class' => '', 'alt' => '', 'prefix' => '', 'suffix' => '', 'help' => '', 'trim' => 0, 'max_length' => '', 'word_boundary' => 1, 'ellipsis' => 1, 'strip_tags' => 0, 'html' => 0, ), 'link_to_node' => 0, 'exclude' => 0, 'id' => 'nid', 'table' => 'node', 'field' => 'nid', 'relationship' => 'nid', ), 'body' => array( 'label' => 'Body', 'alter' => array( 'alter_text' => 0, 'text' => '', 'make_link' => 0, 'path' => '', 'link_class' => '', 'alt' => '', 'prefix' => '', 'suffix' => '', 'help' => '', 'trim' => 0, 'max_length' => '', 'word_boundary' => 1, 'ellipsis' => 1, 'strip_tags' => 0, 'html' => 0, ), 'exclude' => 0, 'id' => 'body', 'table' => 'node_revisions', 'field' => 'body', 'relationship' => 'none', ), )); $handler->override_option('filters', array( 'keys' => array( 'operator' => 'optional', 'value' => '', 'group' => '0', 'exposed' => TRUE, 'expose' => array( 'use_operator' => 0, 'operator' => 'keys_op', 'identifier' => 'keys', 'label' => 'Search: Search Terms', 'optional' => 1, 'remember' => 0, ), 'id' => 'keys', 'table' => 'search_index', 'field' => 'keys', 'relationship' => 'none', ), )); $handler->override_option('access', array( 'type' => 'none', )); $handler->override_option('cache', array( 'type' => 'none', )); $handler->override_option('distinct', 0); $handler->override_option('style_plugin', 'table'); $handler->override_option('style_options', array( 'grouping' => 'nid', 'override' => 1, 'sticky' => 0, 'order' => 'asc', 'columns' => array( 'nid' => 'nid', 'name' => 'name', 'name_1' => 'name_1', 'body' => 'body', 'last_comment_name' => 'last_comment_name', ), 'info' => array( 'nid' => array( 'sortable' => 0, 'separator' => '', ), 'name' => array( 'sortable' => 0, 'separator' => '', ), 'name_1' => array( 'sortable' => 0, 'separator' => '', ), 'body' => array( 'separator' => '', ), 'last_comment_name' => array( 'sortable' => 0, 'separator' => '', ), ), 'default' => '-1', )); $handler->override_option('row_options', array( 'inline' => array(), 'separator' => '', ));
- Save and go back to the Views list, select 'Edit' on 'postgres_test_02'
- Press 'Preview' on the edit page
- Enter some text into the search box, 'test' will do
- Press 'Apply'
- See this error:
warning: pg_query() [function.pg-query]: Query failed: ERROR: column reference "nid" is ambiguous LINE 10: GROUP BY search_index.sid, nid, node_revisions_body, node_r... ^ in /home/liam/public_html/drupal6/includes/database.pgsql.inc on line 139.
The SQL generated by Views is:
SELECT node.nid AS nid,
SUM(search_index.score * search_total.count) AS score,
node_revisions.body AS node_revisions_body,
node_revisions.format AS node_revisions_format
FROM node node
LEFT JOIN search_index search_index ON node.nid = search_index.sid
LEFT JOIN search_total search_total ON search_index.word = search_total.word
LEFT JOIN node_revisions node_revisions ON node.vid = node_revisions.vid
WHERE (search_index.word = 'test') AND (search_index.type = 'node')
GROUP BY search_index.sid, nid, node_revisions_body, node_revisions_format
HAVING COUNT(*) >= 1
Notice the first line: SELECT node.nid AS nid
, then where Postgres gets confused because it believes 'nid' refers to the field, and finds two of them: GROUP BY [...] nid, [...]
Comment | File | Size | Author |
---|---|---|---|
#42 | 506818_postgres_group_by_alias.patch | 867 bytes | Island Usurper |
#38 | 0002-fix-problem-with-the-distinct-option-on-posgres-serv.patch | 996 bytes | greenrover33 |
#32 | views-506818-32.patch | 913 bytes | Boobaa |
#26 | query.inc_.diff | 3.24 KB | bartl |
#25 | query.inc_.diff | 3.07 KB | bartl |
Comments
Comment #1
Shiny CreditAttribution: Shiny commentedtagging for next Postgres Code Spring
Comment #2
phayes CreditAttribution: phayes commentedSubscribing
Comment #3
phayes CreditAttribution: phayes commentedHi two questions:
1. Does anyone know when the next code sprint for postgres will be? This bug is breaking a large portion of my views, so i'd like to make some plans around what to do. :-)
2. Does anyone know if this bug is also present in 3.x / 2.5 ? I could just upgrade / downgrade if not.
Thanks!
Patrick
Comment #4
Shiny CreditAttribution: Shiny commented@phayes the New Zealand based team will likely have another mini-code sprint a couple weeks after Drupalcon.
Comment #5
osopolarI can confirm that this error also exists for 2.7 ... i assume its also in the current 2.x-dev version.
Comment #6
osopolarWith the help of Damz (via irc) and fuerst we where able to fix the problem. Patch attached!
Comment #7
merlinofchaos CreditAttribution: merlinofchaos commented#6: That patch only works if the field is a real field. If it is a formula, I think that patch will cause errors. Try a date field, for example.
Comment #8
osopolarI extended the above view with a date field (field_event_date) and got the below listed query. At least there is no error. Is there a way to check if it's a formula or a real field? Shouldn't be $field['table'] (line 969) and $this->base_table (line 907) be empty, if it is a formula?
SELECT node.nid AS nid,
SUM(search_index.score * search_total.count) AS score,
node_revisions.body AS node_revisions_body,
node_revisions.format AS node_revisions_format,
node_data_field_event_date.field_event_date_value AS node_data_field_event_date_field_event_date_value,
node.type AS node_type,
node.vid AS node_vid
FROM node node
LEFT JOIN search_index search_index ON node.nid = search_index.sid
LEFT JOIN search_total search_total ON search_index.word = search_total.word
LEFT JOIN node_revisions node_revisions ON node.vid = node_revisions.vid
LEFT JOIN content_type_event node_data_field_event_date ON node.vid = node_data_field_event_date.vid
WHERE (search_index.word = 'lorem') AND (search_index.type = 'node')
GROUP BY search_index.sid, node.nid, node_revisions.body, node_revisions.format, node_data_field_event_date.field_event_date_value, node.type, node.vid
HAVING COUNT(*) >= 1
Comment #9
osopolarDamZ said " the first hunk is fine" and he suggested to check $field['table'] like:
$non_aggregates[] = (!empty($field['table']) ? $field['table'] . '.' : '') . $field['field'];
Patch changed.
Comment #10
Josh Waihi CreditAttribution: Josh Waihi commenteddoes #607418: Column 'nid' in field list is ambiguous query apply to this situation? if so, see the patch I attached over there.
Comment #11
merlinofchaos CreditAttribution: merlinofchaos commentedOk, here is what I want to do:
In Views 2.x, I'm ok with patch #9.
In Views 3.x, I'd like to fix the real problem. The real problem is that we add the base field without its table, so instead of 'node_nid' it's 'nid'. This is really just a holdover from Views 1, where it was acceptable to refer to the base field directly in the query. In Views 3 that is actually not acceptable, thanks to the addition of group by. Therefore, a patch for Views 3 should remove the special handling for the base field in the query object and make sure any references to the base field directly (i.e, ->cid ->nid etc) are caught and fixed. Those are all wrong anyway.
So I need 2 patches for this.
Comment #12
dawehnerIf i remove
// We check for this specifically because it gets a special alias.
if ($table == $this->base_table && $field == $this->base_field && empty($alias)) {
$alias = $this->base_field;
}
The code does brake the groupby support. I don't know why, yet
Comment #13
Josh Waihi CreditAttribution: Josh Waihi commentedbecause what you're grouping by is an ambiguous name. My guess is nid which is so very present in many tables.
Comment #14
crea CreditAttribution: crea commentedSubscribing
Comment #15
BoobaaSubscribing - anyway, the patch in #9 solves my particular problem, which has been described above already.
Comment #16
Kafu CreditAttribution: Kafu commentedsubscribe
Comment #17
halcyonCorsair CreditAttribution: halcyonCorsair commentedPatch in #9 works for me.
Comment #18
Kafu CreditAttribution: Kafu commentedPatch #9 works for me too. Can this issue marked as fixed and committed?
Comment #19
Shiny CreditAttribution: Shiny commentedPatch #9 works nicely for me.
Comment #20
merlinofchaos CreditAttribution: merlinofchaos commentedWithout a 3.x patch this is not RTBC.
Comment #21
interestingaftermath CreditAttribution: interestingaftermath commentedIs there any progress on a 3.x patch for this?
Comment #22
interestingaftermath CreditAttribution: interestingaftermath commentedI wanted to confirm that I am having the same problem as reported in this issue before opening a new issue.
I am using 3.x Views. I have my fields setup but when I add the Search Terms filter, expose it I receive the follow error:
Comment #23
interestingaftermath CreditAttribution: interestingaftermath commentedStill having this issue in 6.x-3.x-dev
Comment #24
interestingaftermath CreditAttribution: interestingaftermath commentedI hope the changes to the project options are correct. I changed this to version 3.x because the patch in #9 seems acceptable for those using 2.x. I consider this a major issue considering it halts the query from working at all.
I've tried altering the query and it does fix the error (in a very, very hacked sort of way) but it doesn't pass the nid value to the results so all titles are linked to "/node/" without the nid, etc.
EDIT
I was able to get the nid working for the results links by doing this
Comment #25
bartl CreditAttribution: bartl commentedI'm new to Drupal, and full of good intentions, I wanted it run on top of Postgres. Next, I decided to install Open Atrium, but this and related issues stopped it from running without errors. So I hacked on it for several hours and by the end of the day, I managed to get Open Atrium running without any warnings.
It is not perfect nor complete, but at least, it appears to be good enough for a start.
So here is the patch to query.inc (version 1.50.2.4 2009/12/02).
The issues:
That is, in principle, a matter of eliminating use of
$field['alias']
in the code on line 947. However, that is not enough to solve every problem.I like the idea of using the original column names as the alias for the columns from the base table. Because, if you are going to use fieldnames with the table name as a prefix for the base table too, as has been proposed, then to be consistent, you also have to do it if your query uses only one table. And that would seem to be rather silly, at least to me.
So what I decided on is to simply qualify (= prepend name of base table plus dot) the field names that are not:
***name***
", for example "***CURRENT_USER***
" or "***ATRIUM_ACTIVITY_TIMESTAMP***
"That's a lot of exceptions (although those latter cases would probably never end up in the "group by" list), so instead of looking at the format of the field, I decided instead to look up if the name is actually a column name for that particular base table, thus: look if the string as is, is in the list of column names. That simply eliminates every exception from the above list, and quite efficiently too.
Though one could actually retrieve the column names from the DB at runtime, it would be extremely inefficient (and silly) to do it for every single query, perhaps thousands of times an hour, because that list of column names will not just change overnight.
So the wiser thing to do was to fetch the list of column names for the problematic tables and stuff it into a PHP array, in source code. I used an associative array where the keys are the table names and the values are the arrays with column names.
For all other tables you ought to do the same, but as Open Atrium didn't require it, I decided against doing it for now.
I just took a few column names that would seem to be fairly common, for any other tables.
I think the proper solution would be to fetch the column names for all tables once, at installation time, and generate a PHP source file from them, and load that file from there on.
That way, the array of column names could be gotten without any conditionals.
A future enhancement could be that this list could be returned as a method from the query object.
$this->groupby
The user supplied fields in
$non_aggregates
are not the only problem. The same issue arises for the list of field names supplied through$this->groupby
. So I apply the same treatment to that list, using a temporary array in the variable$groupby
.BTW I know that some people might object to my reuse of the same variable as a temporary array, first, and then as the final string later. But I first had used a different variable name at first and the code didn't work, because that variable apparently was already used elsewhere.
$groupby
is safe, simply because it gets set to a new value a few lines down.I guess it it ought to be possible to already qualify the names before the method
groupby
returns them.That problem is caused by use of
FIRST(NULL)
, where plain and simpleNULL
will do — and do better.So I test if $string is "NULL", and if it is, not make it
"FIRST($string)"
.Probably other literals (including the magic placeholders) could handle the same treatment, however, Postgres doesn't seem to object for those.
That problem only seems to arise in UNION queries, where NULL is used in one branch as a column value, and the same column is a number in other branches. It would seem Postgress automatically treats a plain NULL as a string, and merging it with a column of numbers poses a problem. NULL ought to be treated as a number.
The solution is to typecast the NULL to a number (or more specifically, an integer), for the fields of which I assume to represent numbers, using
"NULL::integer"
, or the more SQL-Standard compliant"CAST(NULL AS integer)"
instead of plain"NULL"
. My rule for that guess is that if an alias name ends with "id", then it's a number. It seems to be good enough for Open Atrium.I think that about wraps it up.
Comment #26
bartl CreditAttribution: bartl commentedWell, there's still the same problem with NULL as in issue#6 in my previous post ("UNION types integer and text cannot be matched") for queries that do not use "
group by
", so I moved the code around a little and now always check if$string
is "NULL".That solves my immediate problem.
This patch replaces the patch from yesterday, and thus, is against the original file.
Comment #27
dawehnerIsn't there a more abstract way of fixing it? Views builds query against any kind of table schema not just only node and users.
Comment #28
bartl CreditAttribution: bartl commentedTo quote myself:
By that I mean execute a query like this once, and generate a PHP source file from it -- most definitely not write it by hand:
There is no reliable way to determine if a field name is a actually a column name, based on the format alone. Checking it against the list of column names of a table is as reliable as it gets, and fast.
There's only one problem: it's also big. There are currently 95 tables and a total of 577 column names in the database. That could be a rather large file. Although... not that large, to current standards...
Still, most of those tables are never used in views so it's a bit unnecessary.
Comment #29
bartl CreditAttribution: bartl commentedIt is possible to easily get the names of the columns from a single table, straight from the db, but I'm still not a fan of doing this every single time. The following code works for me (you'll have to change the db parameters) independently from Drupal:
which produces
Note that the query itself returns no rows, but that doesn't matter, you can still fetch the column names.
You could fetch this once the first time a base table is used when a view is run, and cache it (the text is valid PHP code), be it in one common file for all tables, or one tiny file per table.
And when using Drupal's database access functions, you can use its built in features to replace the table name with the actual table name, in case people use a table name prefix:
"select * from {node} where 1 = 0"
Comment #30
lljmitchell CreditAttribution: lljmitchell commentedsubscribe
Comment #31
seattlehimay CreditAttribution: seattlehimay commentedSubscribing.
I'm using D7 (Views 7.x-3.0-beta3) but have the same issue--will leave version of issue as-is since i'm late to the party.
I'm using postgresql. If I add search term filter, it adds a group-by clause and I get the error:
'Exception: SQLSTATE[42702]: Ambiguous column: 7 ERROR: column reference "nid" is ambiguous at character 1200' in views_plugin_query_default->execute()
Comment #32
BoobaaRerolled #9 to get rid of offsets; it's still against 6.x-2.12.
Comment #33
Kafu CreditAttribution: Kafu commentedPatch #32 works with 6.x-2.14
Comment #34
dawehnerI'm not really sure whether this is RTBC as it's a) not tested by really a lot of people and b) whether it should be added to 6.x-2.x that late.
Comment #35
Damien Tournoud CreditAttribution: Damien Tournoud commentedSame issue still applies to 7.x-3.x.
Comment #36
simonbshelley CreditAttribution: simonbshelley commentedHi. I would like to confirm that Patch #32 works for me too, also with version 6.x-2.16. Thank you very much for the fix!
I have to re-apply the patch everytime I update my version of Views 6.x-2.x. Any chance that it could still be added to 6.x-2.x in a future release?
Comment #37
seattlehimay CreditAttribution: seattlehimay commentedFor what it is worth, I manually made the changes in patch #32 to views 7.x-3.3 (changes went into file plugins/views_plugin_query_default.inc) , and it solved the problem for me. Hoping this gets added in.
...of course, I had to manually add new group_by clauses in order to make my query actually work, but that's another issue.
Comment #38
greenrover33 CreditAttribution: greenrover33 commentedI have had the same problem. Here a patch.
Comment #39
Liam MorlandComment #40
gmh04 CreditAttribution: gmh04 commented#38 works for me, thanks. could this please be put into future releases?
Comment #41
Island Usurper CreditAttribution: Island Usurper commentedHm, since this is a problem in 7.x-3.x, should we fix it in 8.x-3.x first? I don't know.
But I'm going to call back to merlinofchaos's original plan back in #11, where the base table's primary field needs a better alias. I believe that is all that is necessary to fix the original issue. However, merlin wanted to get rid of extraneous references to ->nid and ->cid, etc., as well. Does anyone know if that has been done by now?
Comment #42
Island Usurper CreditAttribution: Island Usurper commentedI tried changing the base field's alias, but it seems there is a lot of 3rd-party code that relies on it too much. I don't think anyone wants to mess with that in version 3.
So here's a different approach. The real problem is just the base field having an ambiguous alias in the GROUP BY clause. These other patches try to change how the alias is built, but I propose just changing the way the base field is added to the GROUP BY clause of the query itself.
Comment #43
jneubert CreditAttribution: jneubert commentedThe patch #42 worked fine for me.
The approach seems sensible, and the code is quite straightforward, so I move it to RTBC.
Comment #50
ricobanga CreditAttribution: ricobanga commented#42 worked for me too ...
Comment #53
Island Usurper CreditAttribution: Island Usurper commentedOh! Testbot is adding 'sites/default/modules/views/tests/templates/views-view--frontpage.tpl.php' to the end of the list of test files. I bet the double-dash there is messing up the argument parser. Is there an issue about this for the testbot? Why is it doing that?
Comment #54
Island Usurper CreditAttribution: Island Usurper commented"Scanning sites/default/files/checkout/sites/default/modules/views for /Tests/*.php files.", but it's not doing it in a case-sensitive manner. Guess I'll track down some Testbot admins.
Comment #55
Island Usurper CreditAttribution: Island Usurper commentedI found the issue queue instead. #2415991: Scan for D8 tests breaks Views tests in D7, or fix path case-sensitivity and argument parsing