[Rt-commit] r6999 - rt/branches/3.6-RELEASE/lib/RT

ruz at bestpractical.com ruz at bestpractical.com
Tue Feb 13 14:37:02 EST 2007


Author: ruz
Date: Tue Feb 13 14:37:01 2007
New Revision: 6999

Modified:
   rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm

Log:
* fix all failing tests for searches by watchers

Modified: rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm	(original)
+++ rt/branches/3.6-RELEASE/lib/RT/Tickets_Overlay.pm	Tue Feb 13 14:37:01 2007
@@ -85,11 +85,8 @@
 package RT::Tickets;
 
 use strict;
-
-package RT::Tickets;
-
 no warnings qw(redefine);
-use vars qw(@SORTFIELDS);
+
 use RT::CustomFields;
 use DBIx::SearchBuilder::Unique;
 
@@ -162,7 +159,7 @@
     LINKFIELD       => \&_LinkFieldLimit,
     CUSTOMFIELD     => \&_CustomFieldLimit,
 );
-my %can_bundle = ( WATCHERFIELD => "yes", ); # XXX: in 3.4 this part is commented out
+my %can_bundle = (); # WATCHERFIELD => "yes", );
 
 # Default EntryAggregator per type
 # if you specify OP, you must specify all valid OPs
@@ -212,7 +209,7 @@
 
 # {{{ sub SortFields
 
- at SORTFIELDS = qw(id Status
+our @SORTFIELDS = qw(id Status
     Queue Subject
     Owner Created Due Starts Started
     Told
@@ -233,6 +230,21 @@
 
 # BEGIN SQL STUFF *********************************
 
+
+sub CleanSlate {
+    my $self = shift;
+    $self->SUPER::CleanSlate( @_ );
+    delete $self->{$_} foreach qw(
+        _sql_cf_alias
+        _sql_group_members_aliases
+        _sql_object_cfv_alias
+        _sql_role_group_aliases
+        _sql_transalias
+        _sql_trattachalias
+        _sql_u_watchers_alias_for_sort
+    );
+}
+
 =head1 Limit Helper Routines
 
 These routines are the targets of a dispatch table depending on the
@@ -820,136 +832,218 @@
     my $value = shift;
     my %rest  = (@_);
 
-    # Find out what sort of watcher we're looking for
-    my $fieldname;
-    if ( ref $field ) {
-        $fieldname = $field->[0]->[0];
-    }
-    else {
-        $fieldname = $field;
-        $field = [ [ $field, $op, $value, %rest ] ];    # gross hack
-    }
-    my $meta = $FIELD_METADATA{$fieldname};
-    my $type = ( defined $meta->[1] ? $meta->[1] : undef );
+    my $meta = $FIELD_METADATA{ $field };
+    my $type = $meta->[1] || '';
 
     # Owner was ENUM field, so "Owner = 'xxx'" allowed user to
     # search by id and Name at the same time, this is workaround
     # to preserve backward compatibility
-    if ( $fieldname eq 'Owner' ) {
-        my $flag = 0;
-        for my $chunk ( splice @$field ) {
-            my ( $f, $op, $value, %rest ) = @$chunk;
-            if ( !$rest{SUBKEY} && $op =~ /^!?=$/ ) {
-                $self->_OpenParen unless $flag++;
-                my $o = RT::User->new( $self->CurrentUser );
-                $o->Load($value);
-                $value = $o->Id;
-                $self->_SQLLimit(
-                    FIELD    => 'Owner',
-                    OPERATOR => $op,
-                    VALUE    => $value,
-                    %rest,
-                );
-            }
-            else {
-                push @$field, $chunk;
-            }
-        }
-        $self->_CloseParen if $flag;
-        return unless @$field;
+    if ( $field eq 'Owner' && !$rest{SUBKEY} && $op =~ /^!?=$/ ) {
+        my $o = RT::User->new( $self->CurrentUser );
+        $o->Load( $value );
+        $self->_SQLLimit(
+            FIELD    => 'Owner',
+            OPERATOR => $op,
+            VALUE    => $o->Id,
+            %rest,
+        );
+        return;
     }
+    $rest{SUBKEY} ||= 'EmailAddress';
 
-    my $users = $self->_WatcherJoin($type);
+    my $groups = $self->_RoleGroupsJoin( Type => $type );
 
-    # If we're looking for multiple watchers of a given type,
-    # TicketSQL will be handing it to us as an array of clauses in
-    # $field
     $self->_OpenParen;
-    for my $chunk (@$field) {
-        ( $field, $op, $value, %rest ) = @$chunk;
-        $rest{SUBKEY} ||= 'EmailAddress';
-
-        my $re_negative_op = qr[!=|NOT LIKE];
-        $self->_OpenParen if $op =~ /$re_negative_op/;
-
+    if ( $op =~ /^IS(?: NOT)?$/ ) {
+        my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+        $self->SUPER::Limit(
+            LEFTJOIN   => $group_members,
+            FIELD      => 'GroupId',
+            OPERATOR   => '!=',
+            VALUE      => "$group_members.MemberId",
+            QUOTEVALUE => 0,
+        );
         $self->_SQLLimit(
-            ALIAS         => $users,
-            FIELD         => $rest{SUBKEY},
+            ALIAS         => $group_members,
+            FIELD         => 'GroupId',
+            OPERATOR      => $op,
             VALUE         => $value,
+            %rest,
+        );
+    }
+    elsif ( $op =~ /^!=$|^NOT\s+/i ) {
+        # reverse op
+        $op =~ s/!|NOT\s+//i;
+
+        # XXX: we have no way to build correct "Watcher.X != 'Y'" when condition
+        # "X = 'Y'" matches more then one user so we try to fetch two records and
+        # do the right thing when there is only one exist and semi-working solution
+        # otherwise.
+        my $users_obj = RT::Users->new( $self->CurrentUser );
+        $users_obj->Limit(
+            FIELD         => $rest{SUBKEY},
             OPERATOR      => $op,
-            CASESENSITIVE => 0,
-            %rest
+            VALUE         => $value,
         );
-
-        if ( $op =~ /$re_negative_op/ ) {
+        $users_obj->OrderBy;
+        $users_obj->RowsPerPage(2);
+        my @users = @{ $users_obj->ItemsArrayRef };
+
+        my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+        if ( @users <= 1 ) {
+            my $uid = 0;
+            $uid = $users[0]->id if @users;
+            $self->SUPER::Limit(
+                LEFTJOIN      => $group_members,
+                ALIAS         => $group_members,
+                FIELD         => 'MemberId',
+                VALUE         => $uid,
+            );
             $self->_SQLLimit(
-                ALIAS           => $users,
-                FIELD           => $rest{SUBKEY},
+                %rest,
+                ALIAS           => $group_members,
+                FIELD           => 'id',
                 OPERATOR        => 'IS',
                 VALUE           => 'NULL',
-                ENTRYAGGREGATOR => 'OR',
             );
-            $self->_CloseParen;
+        } else {
+            $self->SUPER::Limit(
+                LEFTJOIN   => $group_members,
+                FIELD      => 'GroupId',
+                OPERATOR   => '!=',
+                VALUE      => "$group_members.MemberId",
+                QUOTEVALUE => 0,
+            );
+            my $users = $self->Join(
+                TYPE            => 'LEFT',
+                ALIAS1          => $group_members,
+                FIELD1          => 'MemberId',
+                TABLE2          => 'Users',
+                FIELD2          => 'id',
+            );
+            $self->SUPER::Limit(
+                LEFTJOIN      => $users,
+                ALIAS         => $users,
+                FIELD         => $rest{SUBKEY},
+                OPERATOR      => $op,
+                VALUE         => $value,
+                CASESENSITIVE => 0,
+            );
+            $self->_SQLLimit(
+                %rest,
+                ALIAS         => $users,
+                FIELD         => 'id',
+                OPERATOR      => 'IS',
+                VALUE         => 'NULL',
+            );
         }
+    } else {
+        my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+        my $users = $self->NewAlias('Users');
+        $self->SUPER::Limit(
+            LEFTJOIN      => $group_members,
+            ALIAS         => $group_members,
+            FIELD         => 'MemberId',
+            VALUE         => "$users.id",
+            QUOTEVALUE    => 0,
+        );
+        $self->_SQLLimit(
+            ALIAS         => $users,
+            FIELD         => $rest{SUBKEY},
+            VALUE         => $value,
+            OPERATOR      => $op,
+            CASESENSITIVE => 0,
+            %rest,
+        );
+        $self->_SQLLimit(
+            ENTRYAGGREGATOR => 'AND',
+            ALIAS           => $group_members,
+            FIELD           => 'id',
+            OPERATOR        => 'IS NOT',
+            VALUE           => 'NULL',
+        );
     }
     $self->_CloseParen;
 }
 
-=head2 _WatcherJoin
-
-Helper function which provides joins to a watchers table both for limits
-and for ordering.
-
-=cut
-
-sub _WatcherJoin {
+sub _RoleGroupsJoin {
     my $self = shift;
-    my $type = shift;
+    my %args = (New => 0, Type => '', @_);
+    return $self->{'_sql_role_group_aliases'}{ $args{'Type'} }
+        if $self->{'_sql_role_group_aliases'}{ $args{'Type'} } && !$args{'New'};
+
+    # XXX: if we change this from Join to NewAlias+Limit
+    # then Pg will complain because SB build wrong query.
+    # Query looks like "FROM (Tickets LEFT JOIN CGM ON(Groups.id = CGM.GroupId)), Groups"
+    # Pg doesn't like that fact that it doesn't know about Groups table yet when
+    # join CGM table into Tickets. Problem is in Join method which doesn't use
+    # ALIAS1 argument when build braces.
+    # XXX: this should be fixed in DBIx::SB-1.46
 
-    # we cache joins chain per watcher type
-    # if we limit by requestor then we shouldn't join requestors again
-    # for sort or limit on other requestors
-    if ( $self->{'_watcher_join_users_alias'}{ $type || 'any' } ) {
-        return $self->{'_watcher_join_users_alias'}{ $type || 'any' };
-    }
-
-# we always have watcher groups for ticket
-# this join should be NORMAL
-# XXX: if we change this from Join to NewAlias+Limit
-# then Pg will complain because SB build wrong query.
-# Query looks like "FROM (Tickets LEFT JOIN CGM ON(Groups.id = CGM.GroupId)), Groups"
-# Pg doesn't like that fact that it doesn't know about Groups table yet when
-# join CGM table into Tickets. Problem is in Join method which doesn't use
-# ALIAS1 argument when build braces.
+    # we always have watcher groups for ticket, so we use INNER join
     my $groups = $self->Join(
         ALIAS1          => 'main',
         FIELD1          => 'id',
         TABLE2          => 'Groups',
         FIELD2          => 'Instance',
-        ENTRYAGGREGATOR => 'AND'
+        ENTRYAGGREGATOR => 'AND',
     );
     $self->SUPER::Limit(
+        LEFTJOIN        => $groups,
         ALIAS           => $groups,
         FIELD           => 'Domain',
         VALUE           => 'RT::Ticket-Role',
-        ENTRYAGGREGATOR => 'AND'
     );
     $self->SUPER::Limit(
+        LEFTJOIN        => $groups,
         ALIAS           => $groups,
         FIELD           => 'Type',
-        VALUE           => $type,
-        ENTRYAGGREGATOR => 'AND'
-        )
-        if ($type);
+        VALUE           => $args{'Type'},
+    ) if $args{'Type'};
 
-    my $groupmembers = $self->Join(
-        TYPE   => 'LEFT',
-        ALIAS1 => $groups,
-        FIELD1 => 'id',
-        TABLE2 => 'CachedGroupMembers',
-        FIELD2 => 'GroupId'
+    $self->{'_sql_role_group_aliases'}{ $args{'Type'} } = $groups
+        unless $args{'New'};
+
+    return $groups;
+}
+
+sub _GroupMembersJoin {
+    my $self = shift;
+    my %args = (New => 1, GroupsAlias => undef, @_);
+
+    return $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} }
+        if $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} }
+            && !$args{'New'};
+
+    my $alias = $self->Join(
+        TYPE            => 'LEFT',
+        ALIAS1          => $args{'GroupsAlias'},
+        FIELD1          => 'id',
+        TABLE2          => 'CachedGroupMembers',
+        FIELD2          => 'GroupId',
+        ENTRYAGGREGATOR => 'AND',
     );
 
+    $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } = $alias
+        unless $args{'New'};
+
+    return $alias;
+}
+
+=head2 _WatcherJoin
+
+Helper function which provides joins to a watchers table both for limits
+and for ordering.
+
+=cut
+
+sub _WatcherJoin {
+    my $self = shift;
+    my $type = shift || '';
+
+
+    my $groups = $self->_RoleGroupsJoin( Type => $type );
+    my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
     # XXX: work around, we must hide groups that
     # are members of the role group we search in,
     # otherwise them result in wrong NULLs in Users
@@ -958,20 +1052,20 @@
     # ticket roles, so we just hide entries in CGM table
     # with MemberId == GroupId from results
     $self->SUPER::Limit(
-        LEFTJOIN   => $groupmembers,
+        LEFTJOIN   => $group_members,
         FIELD      => 'GroupId',
         OPERATOR   => '!=',
-        VALUE      => "$groupmembers.MemberId",
+        VALUE      => "$group_members.MemberId",
         QUOTEVALUE => 0,
     );
     my $users = $self->Join(
-        TYPE   => 'LEFT',
-        ALIAS1 => $groupmembers,
-        FIELD1 => 'MemberId',
-        TABLE2 => 'Users',
-        FIELD2 => 'id'
+        TYPE            => 'LEFT',
+        ALIAS1          => $group_members,
+        FIELD1          => 'MemberId',
+        TABLE2          => 'Users',
+        FIELD2          => 'id',
     );
-    return $self->{'_watcher_join_users_alias'}{ $type || 'any' } = $users;
+    return ($groups, $group_members, $users);
 }
 
 =head2 _WatcherMembershipLimit
@@ -1403,10 +1497,13 @@
         my ( $field, $subkey ) = split /\./, $row->{FIELD}, 2;
         my $meta = $self->FIELDS->{$field};
         if ( $meta->[0] eq 'WATCHERFIELD' ) {
-            my $users = $self->_WatcherJoin( $meta->[1], "order" . $order++ );
+            # cache alias as we want to use one alias per watcher type for sorting
+            my $users = $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] };
+            unless ( $users ) {
+                $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] }
+                    = $users = ( $self->_WatcherJoin( $meta->[1] ) )[2];
+            }
             push @res, { %$row, ALIAS => $users, FIELD => $subkey };
-        
-
        } elsif ( $meta->[0] eq 'CUSTOMFIELD' ) {
            my ($queue, $field, $cfid ) = $self->_CustomFieldDecipher( $subkey );
            my $cfkey = $cfid ? $cfid : "$queue.$field";


More information about the Rt-commit mailing list