[Rt-commit] r7004 - in rt/branches/3.7-EXPERIMENTAL-RTIR-2.2: lib/RT
ruz at bestpractical.com
ruz at bestpractical.com
Wed Feb 14 11:09:46 EST 2007
Author: ruz
Date: Wed Feb 14 11:09:46 2007
New Revision: 7004
Modified:
rt/branches/3.7-EXPERIMENTAL-RTIR-2.2/ (props changed)
rt/branches/3.7-EXPERIMENTAL-RTIR-2.2/lib/RT/Tickets_Overlay.pm
Log:
r4560 at cubic-pc (orig r6999): ruz | 2007-02-13 22:37:01 +0300
* fix all failing tests for searches by watchers
Modified: rt/branches/3.7-EXPERIMENTAL-RTIR-2.2/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-RTIR-2.2/lib/RT/Tickets_Overlay.pm (original)
+++ rt/branches/3.7-EXPERIMENTAL-RTIR-2.2/lib/RT/Tickets_Overlay.pm Wed Feb 14 11:09:46 2007
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2006 Best Practical Solutions, LLC
# <jesse at bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -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;
@@ -215,7 +212,7 @@
# {{{ sub SortFields
- at SORTFIELDS = qw(id Status
+our @SORTFIELDS = qw(id Status
Queue Subject
Owner Created Due Starts Started
Told
@@ -236,6 +233,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
@@ -852,136 +864,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->{'_sql_watcher_join_users_alias'}{ $type || 'any' } ) {
- return $self->{'_sql_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
@@ -990,20 +1084,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->{'_sql_watcher_join_users_alias'}{ $type || 'any' } = $users;
+ return ($groups, $group_members, $users);
}
=head2 _WatcherMembershipLimit
@@ -1360,10 +1454,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