[Postgres] GROUP BY uses field names instead of aliases

Liam McDermott - July 1, 2009 - 03:11
Project:Views
Version:6.x-2.x-dev
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:needs review
Issue tags:PostgreSQL Code Sprint
Description

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:

  1. Ensure you Drupal/Views are setup to use a Postgres database ;)
  2. import the following View:
    <?php
    $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' => '',
    ));
    ?>
  3. Save and go back to the Views list, select 'Edit' on 'postgres_test_02'
  4. Press 'Preview' on the edit page
  5. Enter some text into the search box, 'test' will do
  6. Press 'Apply'
  7. 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, [...]

#1

Shiny - July 16, 2009 - 02:20

tagging for next Postgres Code Spring

#2

phayes - August 18, 2009 - 02:02

Subscribing

#3

phayes - September 4, 2009 - 22:49

Hi 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

#4

Shiny - September 5, 2009 - 07:19

@phayes the New Zealand based team will likely have another mini-code sprint a couple weeks after Drupalcon.

#5

ositoblanco - November 10, 2009 - 11:18
Version:6.x-2.6» 6.x-2.x-dev

I can confirm that this error also exists for 2.7 ... i assume its also in the current 2.x-dev version.

#6

ositoblanco - November 10, 2009 - 14:41
Status:active» needs review

With the help of Damz (via irc) and fuerst we where able to fix the problem. Patch attached!

AttachmentSize
views-506818-6.patch 815 bytes

#7

merlinofchaos - November 10, 2009 - 19:02

#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.

#8

ositoblanco - November 11, 2009 - 09:34

I 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

#9

ositoblanco - November 11, 2009 - 10:04

DamZ said " the first hunk is fine" and he suggested to check $field['table'] like:

<?php
$non_aggregates
[] = (!empty($field['table']) ? $field['table'] . '.' : '') . $field['field'];
?>

Patch changed.

AttachmentSize
views-506818-9.patch 848 bytes
 
 

Drupal is a registered trademark of Dries Buytaert.