[Rt-commit] rt branch 5.0/optimize-from-sql-searches created. rt-5.0.3-125-g4b6fdee3e8

BPS Git Server git at git.bestpractical.com
Tue Sep 27 21:14:30 UTC 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 5.0/optimize-from-sql-searches has been created
        at  4b6fdee3e8b77e2ef87db56f04377d21ae129522 (commit)

- Log -----------------------------------------------------------------
commit 4b6fdee3e8b77e2ef87db56f04377d21ae129522
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Sep 27 19:08:29 2022 +0800

    Test more about optimized ticket/transactions/asset searches
    
    This is to make sure the following optimizations work:
    
        * OR => IN for transaction/asset searches
        * Active/Inactive status searches with queue/catalog/lifecycle limited

diff --git a/t/api/sql.t b/t/api/sql.t
index 7a39cd65f9..262a6c2d8b 100644
--- a/t/api/sql.t
+++ b/t/api/sql.t
@@ -17,21 +17,89 @@ my $alice_id = RT::Test->load_or_create_user( Name => 'alice' )->id;
 my $general_id = RT::Test->load_or_create_queue( Name => 'General' )->id;
 my $support_id = RT::Test->load_or_create_queue( Name => 'Support' )->id;
 
-my %ticketsql = (
-    q{Status = 'new' OR Status = 'open'}                => qr{Status IN \('new', 'open'\)},
-    q{Status = '__Active__'}                            => qr{Status IN \('new', 'open', 'stalled'\)},
-    q{id = 2 OR id = 3}                                 => qr{id IN \('2', '3'\)},
-    q{Creator = 'root' OR Creator = 'alice'}            => qr{Creator IN \('$alice_id', '$root_id'\)},
-    q{Queue = 'General' OR Queue = 'Support'}           => qr{Queue IN \('$general_id', '$support_id'\)},
-    q{Lifecycle = 'default' or Lifecycle = 'approvals'} => qr{Lifecycle IN \('approvals', 'default'\)},
-    q{(Queue = 'General' OR Queue = 'Support') AND (Status = 'new' OR Status = 'open')} =>
-        qr{Queue IN \('$general_id', '$support_id'\).+Status IN \('new', 'open'\)},
+my $lifecycles = RT->Config->Get('Lifecycles');
+RT->Config->Set(
+    Lifecycles => %{$lifecycles},
+    hardware   => {
+        type     => 'asset',
+        initial  => ['new'],
+        active   => ['tracked'],
+        inactive => ['retired'],
+        defaults => { on_create => 'new', },
+    },
 );
 
-my $tickets = RT::Tickets->new( RT->SystemUser );
-for my $query ( sort keys %ticketsql ) {
-    $tickets->FromSQL($query);
-    like( $tickets->BuildSelectQuery(PreferBind => 0), $ticketsql{$query}, qq{TicketSQL "$query" uses IN} );
+RT::Lifecycle->FillCache();
+
+require RT::Test::Assets;
+my $general_catalog_id = RT::Test::Assets->load_or_create_catalog( Name => 'General assets' )->Id;
+my $hardware_catalog_id = RT::Test::Assets->load_or_create_catalog( Name => 'Hardware', Lifecycle => 'hardware' )->Id;
+
+my %sql = (
+    'RT::Tickets' => {
+        like => {
+            q{Status = 'new' OR Status = 'open'}                => qr{Status IN \('new', 'open'\)},
+            q{Status = '__Active__'}                            => qr{Status IN \('new', 'open', 'stalled'\)},
+            q{id = 2 OR id = 3}                                 => qr{id IN \('2', '3'\)},
+            q{Creator = 'root' OR Creator = 'alice'}            => qr{Creator IN \('$alice_id', '$root_id'\)},
+            q{Queue = 'General' OR Queue = 'Support'}           => qr{Queue IN \('$general_id', '$support_id'\)},
+            q{Lifecycle = 'default' or Lifecycle = 'approvals'} => qr{Lifecycle IN \('approvals', 'default'\)},
+            q{(Queue = 'General' OR Queue = 'Support') AND (Status = 'new' OR Status = 'open')} =>
+                qr{Queue IN \('$general_id', '$support_id'\).+Status IN \('new', 'open'\)},
+        },
+        unlike => {
+            q{Status = '__Active__' and Queue = 'General'}       => qr{approvals},
+            q{Status = '__Inactive__' and Lifecycle = 'default'} => qr{approvals},
+        },
+    },
+    'RT::Transactions' => {
+        like => {
+            q{TicketStatus = 'new' OR TicketStatus = 'open'}      => qr{Status IN \('new', 'open'\)},
+            q{TicketStatus = '__Active__'}                        => qr{Status IN \('new', 'open', 'stalled'\)},
+            q{id = 2 OR id = 3}                                   => qr{id IN \('2', '3'\)},
+            q{Creator = 'root' OR Creator = 'alice'}              => qr{Creator IN \('$alice_id', '$root_id'\)},
+            q{TicketCreator = 'root' OR TicketCreator = 'alice'}  => qr{Creator IN \('$alice_id', '$root_id'\)},
+            q{TicketLastUpdatedBy = 'root' OR TicketLastUpdatedBy = 'alice'}  => qr{LastUpdatedBy IN \('$alice_id', '$root_id'\)},
+            q{TicketQueue = 'General' OR TicketQueue = 'Support'} => qr{Queue IN \('$general_id', '$support_id'\)},
+            q{TicketQueueLifecycle = 'default' or TicketQueueLifecycle = 'approvals'} =>
+                qr{Lifecycle IN \('approvals', 'default'\)},
+            q{(TicketQueue = 'General' OR TicketQueue = 'Support') AND (TicketStatus = 'new' OR TicketStatus = 'open')}
+                => qr{Queue IN \('$general_id', '$support_id'\).+Status IN \('new', 'open'\)},
+        },
+        unlike => {
+            q{TicketStatus = '__Active__' and TicketQueue = 'General'} => qr{approvals},
+        },
+    },
+    'RT::Assets' => {
+        like => {
+            q{Status = 'new' OR Status = 'allocated'}             => qr{Status IN \('allocated', 'new'\)},
+            q{Status = '__Active__'}                              => qr{Status IN \('allocated', 'in-use', 'new'\)},
+            q{id = 2 OR id = 3}                                   => qr{id IN \('2', '3'\)},
+            q{Catalog = 'General assets' OR Catalog = 'Hardware'} =>
+                qr{Catalog IN \('$general_catalog_id', '$hardware_catalog_id'\)},
+            q{(Catalog = 'General assets' OR Catalog = 'Hardware') AND (Status = 'allocated' OR Status = 'new')} =>
+                qr{Catalog IN \('$general_catalog_id', '$hardware_catalog_id'\).+Status IN \('allocated', 'new'\)},
+        },
+        unlike => {
+            q{Status = '__Active__' and Catalog = 'General assets'} => qr{hardware},
+        },
+    },
+);
+
+for my $type ( sort keys %sql ) {
+    my $collection = $type->new( RT->SystemUser );
+    for my $op ( sort keys %{ $sql{$type} } ) {
+        for my $query ( sort keys %{ $sql{$type}{$op} } ) {
+            $collection->FromSQL($query);
+            no strict 'refs';
+            $op->(
+                $collection->BuildSelectQuery( PreferBind => 0 ),
+                $sql{$type}{$op}{$query},
+                qq{SQL "$query" is simplified}
+            );
+        }
+    }
+
 }
 
 done_testing;

commit afa2e9598c7a0fda5f76e97532bf53b22176ad86
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Sep 27 16:30:26 2022 +0800

    Convert "OR" clauses in transactions/assets searches to "IN" for perforamnce
    
    This is generally to mirror the behavior in ticket searches. See also
    2745b9ff05

diff --git a/lib/RT/Assets.pm b/lib/RT/Assets.pm
index 262f566f7b..72a3a35e96 100644
--- a/lib/RT/Assets.pm
+++ b/lib/RT/Assets.pm
@@ -865,16 +865,30 @@ sub _EnumLimit {
     # SQL::Statement changes != to <>.  (Can we remove this now?)
     $op = "!=" if $op eq "<>";
 
-    die "Invalid Operation: $op for $field"
-        unless $op eq "="
-        or $op     eq "!=";
+    die "Invalid Operation: $op for $field" unless $op =~ /^(?:=|!=|IN|NOT IN)$/i;
 
     my $meta = $FIELD_METADATA{$field};
     if ( defined $meta->[1] && defined $value && $value !~ /^\d+$/ ) {
         my $class = "RT::" . $meta->[1];
-        my $o     = $class->new( $sb->CurrentUser );
-        $o->Load($value);
-        $value = $o->Id || 0;
+        if ( ref $value eq 'ARRAY' ) {
+            my @values;
+            for my $i (@$value) {
+                if ( $i !~ /^\d+$/ ) {
+                    my $o = $class->new( $sb->CurrentUser );
+                    $o->Load($i);
+                    push @values, $o->Id || 0;
+                }
+                else {
+                    push @values, $i;
+                }
+            }
+            $value = \@values;
+        }
+        else {
+            my $o = $class->new( $sb->CurrentUser );
+            $o->Load($value);
+            $value = $o->Id || 0;
+        }
     }
     $sb->Limit(
         FIELD    => $field,
@@ -1205,7 +1219,12 @@ sub _StringLimit {
     }
 
     if ($field eq "Status") {
-        $value = lc $value;
+        if ( ref $value eq 'ARRAY' ) {
+            $value = [ map lc, @$value ];
+        }
+        else {
+            $value = lc $value;
+        }
     }
 
     $sb->Limit(
@@ -1617,6 +1636,8 @@ sub _parser {
         }
     );
 
+    RT::SQL::_Optimize($tree);
+
     my $ea = '';
     $tree->traverse(
         sub {
diff --git a/lib/RT/SQL.pm b/lib/RT/SQL.pm
index 2d1f047cde..e5dfd6a85c 100644
--- a/lib/RT/SQL.pm
+++ b/lib/RT/SQL.pm
@@ -239,6 +239,85 @@ sub _BitmaskToString {
     return join ' or ', @res;
 }
 
+sub _Optimize {
+    my $tree = shift;
+
+    # Convert simple OR'd clauses to IN for better performance, e.g.
+    #     (Status = 'new' OR Status = 'open' OR Status = 'stalled')
+    # to
+    #     Status IN ('new', 'open', 'stalled')
+
+    $tree->traverse(
+        sub {
+            my $node   = shift;
+            my $parent = $node->getParent;
+            return if $parent eq 'root';    # Skip root's root
+
+            # For simple searches like "Status = 'new' OR Status = 'open'",
+            # the OR node is also the root node, go up one level.
+            $node = $parent if $node->isLeaf && $parent->isRoot;
+
+            return if $node->isLeaf;
+
+            if ( ( $node->getNodeValue // '' ) =~ /^or$/i && $node->getChildCount > 1 ) {
+                my @children = $node->getAllChildren;
+                my %info;
+                for my $child (@children) {
+
+                    # Only handle innermost ORs
+                    return unless $child->isLeaf;
+                    my $entry = $child->getNodeValue;
+                    return unless $entry->{Op} =~ /^!?=$/;
+
+                    # Handle String/Int/Id/Enum/Queue/Lifecycle only for
+                    # now. Others have more complicated logic inside, which
+                    # can't be easily converted.
+
+                    # TICKETQUEUEFIELD only supports Lifecycle right now, which is fine
+                    return
+                        unless ( $entry->{Meta}[0] // '' )
+                        =~ /^(?:STRING|INT|ID|ENUM|QUEUE|LIFECYCLE|TICKETFIELD|TICKETQUEUEFIELD)$/;
+
+                    # TICKETFIELD contains more than what we want, need to filter a bit more deeply.
+                    if ( $entry->{Meta}[0] eq 'TICKETFIELD' ) {
+                        my ($field) = $entry->{Key} =~ /Ticket(\w+)/;
+                        require RT::Tickets;
+                        return
+                            unless $RT::Tickets::FIELD_METADATA{$field}[0]
+                            =~ /^(?:STRING|INT|ID|ENUM|QUEUE|LIFECYCLE)$/;
+                    }
+
+                    for my $field (qw/Key SubKey Op Value/) {
+                        $info{$field}{ $entry->{$field} // '' } ||= 1;
+
+                        if ( $field eq 'Value' ) {
+
+                            # In case it's meta value like __Bookmarked__
+                            return if $entry->{Meta}[0] eq 'ID' && $entry->{$field} !~ /^\d+$/;
+                        }
+                        elsif ( keys %{ $info{$field} } > 1 ) {
+                            return;    # Skip if Key/SubKey/Op are different
+                        }
+                    }
+                }
+
+                my $first_child = shift @children;
+                my $entry       = $first_child->getNodeValue;
+                $entry->{Op} = $info{Op}{'='} ? 'IN' : 'NOT IN';
+                $entry->{Value} = [ sort keys %{ $info{Value} } ];
+                if ( $node->isRoot ) {
+                    $parent->removeChild($_) for @children;
+                }
+                else {
+                    $parent->removeChild($node);
+                    $parent->addChild($first_child);
+                }
+            }
+        }
+    );
+    return $tree;
+}
+
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index e355c4dd10..1d98b97f50 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -3340,67 +3340,7 @@ sub _parser {
         }
     );
 
-    # Convert simple OR'd clauses to IN for better performance, e.g.
-    #     (Status = 'new' OR Status = 'open' OR Status = 'stalled')
-    # to
-    #     Status IN ('new', 'open', 'stalled')
-
-    $tree->traverse(
-        sub {
-            my $node   = shift;
-            my $parent = $node->getParent;
-            return if $parent eq 'root';    # Skip root's root
-
-            # For simple searches like "Status = 'new' OR Status = 'open'",
-            # the OR node is also the root node, go up one level.
-            $node = $parent if $node->isLeaf && $parent->isRoot;
-
-            return if $node->isLeaf;
-
-            if ( ( $node->getNodeValue // '' ) =~ /^or$/i && $node->getChildCount > 1 ) {
-                my @children = $node->getAllChildren;
-                my %info;
-                for my $child (@children) {
-
-                    # Only handle innermost ORs
-                    return unless $child->isLeaf;
-                    my $entry = $child->getNodeValue;
-                    return unless $entry->{Op} =~ /^!?=$/;
-
-                    # Handle String/Int/Id/Enum/Queue/Lifecycle only for
-                    # now. Others have more complicated logic inside, which
-                    # can't be easily converted.
-
-                    return unless ( $entry->{Meta}[0] // '' ) =~ /^(?:STRING|INT|ID|ENUM|QUEUE|LIFECYCLE)$/;
-
-                    for my $field (qw/Key SubKey Op Value/) {
-                        $info{$field}{ $entry->{$field} // '' } ||= 1;
-
-                        if ( $field eq 'Value' ) {
-
-                            # In case it's meta value like __Bookmarked__
-                            return if $entry->{Meta}[0] eq 'ID' && $entry->{$field} !~ /^\d+$/;
-                        }
-                        elsif ( keys %{ $info{$field} } > 1 ) {
-                            return;    # Skip if Key/SubKey/Op are different
-                        }
-                    }
-                }
-
-                my $first_child = shift @children;
-                my $entry       = $first_child->getNodeValue;
-                $entry->{Op} = $info{Op}{'='} ? 'IN' : 'NOT IN';
-                $entry->{Value} = [ sort keys %{ $info{Value} } ];
-                if ( $node->isRoot ) {
-                    $parent->removeChild($_) for @children;
-                }
-                else {
-                    $parent->removeChild($node);
-                    $parent->addChild($first_child);
-                }
-            }
-        }
-    );
+    RT::SQL::_Optimize($tree);
 
     my $ea = '';
     $tree->traverse(
diff --git a/lib/RT/Transactions.pm b/lib/RT/Transactions.pm
index b465f3d8ff..54dc0f9e6d 100644
--- a/lib/RT/Transactions.pm
+++ b/lib/RT/Transactions.pm
@@ -284,16 +284,30 @@ sub _EnumLimit {
     # SQL::Statement changes != to <>.  (Can we remove this now?)
     $op = "!=" if $op eq "<>";
 
-    die "Invalid Operation: $op for $field"
-        unless $op eq "="
-        or $op     eq "!=";
+    die "Invalid Operation: $op for $field" unless $op =~ /^(?:=|!=|IN|NOT IN)$/i;
 
     my $meta = $FIELD_METADATA{$field};
     if ( defined $meta->[1] && defined $value && $value !~ /^\d+$/ ) {
         my $class = "RT::" . $meta->[1];
-        my $o     = $class->new( $sb->CurrentUser );
-        $o->Load($value);
-        $value = $o->Id || 0;
+        if ( ref $value eq 'ARRAY' ) {
+            my @values;
+            for my $i (@$value) {
+                if ( $i !~ /^\d+$/ ) {
+                    my $o = $class->new( $sb->CurrentUser );
+                    $o->Load($i);
+                    push @values, $o->Id || 0;
+                }
+                else {
+                    push @values, $i;
+                }
+            }
+            $value = \@values;
+        }
+        else {
+            my $o = $class->new( $sb->CurrentUser );
+            $o->Load($value);
+            $value = $o->Id || 0;
+        }
     }
     $sb->Limit(
         FIELD    => $field,
@@ -807,15 +821,37 @@ sub _TicketLimit {
     $field =~ s!^Ticket!!;
 
     if ( $field eq 'Queue' && $value =~ /\D/ ) {
-        my $queue = RT::Queue->new($self->CurrentUser);
-        $queue->Load($value);
-        $value = $queue->id if $queue->id;
+        if ( ref $value eq 'ARRAY' ) {
+            my @values;
+            for my $v ( @$value ) {
+                my $o = RT::Queue->new( $self->CurrentUser );
+                $o->Load($v);
+                push @values, $o->Id || 0;
+            }
+            $value = \@values;
+        }
+        else {
+            my $queue = RT::Queue->new($self->CurrentUser);
+            $queue->Load($value);
+            $value = $queue->id if $queue->id;
+        }
     }
 
-    if ( $field =~ /^(?:Owner|Creator)$/ && $value =~ /\D/ ) {
-        my $user = RT::User->new( $self->CurrentUser );
-        $user->Load($value);
-        $value = $user->id if $user->id;
+    if ( $field =~ /^(?:Owner|Creator|LastUpdatedBy)$/ && $value =~ /\D/ ) {
+        if ( ref $value eq 'ARRAY' ) {
+            my @values;
+            for my $v ( @$value ) {
+                my $o = RT::User->new( $self->CurrentUser );
+                $o->Load($v);
+                push @values, $o->Id || 0;
+            }
+            $value = \@values;
+        }
+        else {
+            my $user = RT::User->new( $self->CurrentUser );
+            $user->Load($value);
+            $value = $user->id if $user->id;
+        }
     }
 
     $self->Limit(
@@ -1038,6 +1074,8 @@ sub _parser {
         }
     );
 
+    RT::SQL::_Optimize($tree);
+
     my $ea = '';
     $tree->traverse(
         sub {
@@ -1073,6 +1111,7 @@ sub _parser {
             return $self->_CloseParen unless $node->isLeaf;
         }
     );
+
 }
 
 sub FromSQL {

commit b9a7691fdb6fb048ae69e9874d6d3e5d9eb38c82
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Sep 24 11:09:06 2022 +0800

    Include referenced queues/catalogs only for active/inactive status searches
    
    Previously we included active/inactive statuses in all ticket/asset
    lifecycles, which isn't quite necessary if queue or lifecycle is
    limited. This commit filters statuses to only include ones referenced.
    
    E.g.
    
        Queue = 'General' AND Status = '__Active__'
    
    will be translated to something like
    
        Queue = '1' AND Status IN ('new', 'open', 'stalled')
    
    instead of
    
        Queue = '1' AND
        (   ( Queues.Lifecycle = 'default' AND Status IN ('new', 'open', 'stalled') )
         OR ( Queues.Lifecycle = 'approvals' AND Status IN ('new', 'open', 'stalled')
        )

diff --git a/lib/RT/Assets.pm b/lib/RT/Assets.pm
index 046a834e9e..262f566f7b 100644
--- a/lib/RT/Assets.pm
+++ b/lib/RT/Assets.pm
@@ -1497,6 +1497,9 @@ sub _parser {
         return $text;
     };
 
+    my $catalogs = $tree->GetReferencedCatalogs( CurrentUser => $self->CurrentUser );
+    my %referenced_lifecycle = map { $_->{Lifecycle} => 1 } values %$catalogs;
+
     my ( $active_status_node, $inactive_status_node );
 
     $tree->traverse(
@@ -1516,6 +1519,7 @@ sub _parser {
                       map { $_ => $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } }
                       grep { @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } || [] } }
                       grep { $_ ne '__maps__' && $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'asset' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 
@@ -1559,6 +1563,7 @@ sub _parser {
                           || @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ active }  || [] }
                       }
                       grep { $_ ne '__maps__' && $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'asset' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 
diff --git a/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/lib/RT/Interface/Web/QueryBuilder/Tree.pm
index e566d7f472..2c8d662b17 100644
--- a/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+++ b/lib/RT/Interface/Web/QueryBuilder/Tree.pm
@@ -116,14 +116,20 @@ sub GetReferencedQueues {
             my $clause = $node->getNodeValue();
             if ( $clause->{Key} =~ /^(?:Ticket)?Queue$/ ) {
                 if ( $clause->{Op} eq '=' ) {
-                    $queues->{ $clause->{Value} } ||= 1;
+                    my $q = RT::Queue->new( $args{CurrentUser} || $HTML::Mason::Commands::session{CurrentUser} );
+                    $q->Load( $clause->{Value} );
+                    if ( $q->id ) {
+                        # Skip ACL check
+                        $queues->{ $q->id } ||= { map { $_ => $q->__Value($_) } qw/Name Lifecycle/ };
+                    }
                 }
                 elsif ( $clause->{Op} =~ /^LIKE$/i ) {
                     my $qs = RT::Queues->new( $args{CurrentUser} || $HTML::Mason::Commands::session{CurrentUser} );
                     $qs->Limit( FIELD => 'Name', VALUE => $clause->{Value}, OPERATOR => 'LIKE', CASESENSITIVE => 0 );
                     while ( my $q = $qs->Next ) {
                         next unless $q->id;
-                        $queues->{ $q->id } ||= 1;
+                        # Skip ACL check
+                        $queues->{ $q->id } ||= { map { $_ => $q->__Value($_) } qw/Name Lifecycle/ };
                     }
                 }
             }
@@ -133,7 +139,8 @@ sub GetReferencedQueues {
                     $qs->Limit( FIELD => 'Lifecycle', VALUE => $clause->{Value} );
                     while ( my $q = $qs->Next ) {
                         next unless $q->id;
-                        $queues->{ $q->id } ||= 1;
+                        # Skip ACL check
+                        $queues->{ $q->id } ||= { map { $_ => $q->__Value($_) } qw/Name Lifecycle/ };
                     }
                 }
             }
@@ -153,6 +160,10 @@ will appear as a key whose value is 1.
 
 sub GetReferencedCatalogs {
     my $self = shift;
+    my %args = (
+        CurrentUser => '',
+        @_,
+    );
 
     my $catalogs = {};
 
@@ -167,7 +178,12 @@ sub GetReferencedCatalogs {
             return unless $clause->{ Key } eq 'Catalog';
             return unless $clause->{ Op } eq '=';
 
-            $catalogs->{ $clause->{ Value } } = 1;
+            my $catalog = RT::Catalog->new( $args{CurrentUser} || $HTML::Mason::Commands::session{CurrentUser} );
+            $catalog->Load( $clause->{Value} );
+            if ( $catalog->Id ) {
+                # Skip ACL check
+                $catalogs->{ $catalog->Id } ||= { map { $_ => $catalog->__Value($_) } qw/Name Lifecycle/ };
+            }
         }
     );
 
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 1278f3b000..e355c4dd10 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -3183,18 +3183,14 @@ sub _parser {
     );
     die join "; ", map { ref $_ eq 'ARRAY' ? $_->[ 0 ] : $_ } @results if @results;
 
+    my $queues = $tree->GetReferencedQueues( CurrentUser => $self->CurrentUser );
+    my %referenced_lifecycle = map { $_->{Lifecycle} => 1 } values %$queues;
+
     if ( RT->Config->Get('EnablePriorityAsString') ) {
-        my $queues = $tree->GetReferencedQueues( CurrentUser => $self->CurrentUser );
         my %config = RT->Config->Get('PriorityAsString');
         my @names;
         if (%$queues) {
-            for my $id ( keys %$queues ) {
-                my $queue = RT::Queue->new( $self->CurrentUser );
-                $queue->Load($id);
-                if ( $queue->Id ) {
-                    push @names, $queue->__Value('Name');    # Skip ACL check
-                }
-            }
+            @names = map { $_->{Name} } values %$queues;
         }
         else {
             @names = keys %config;
@@ -3248,6 +3244,7 @@ sub _parser {
                       map { $_ => $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } }
                       grep { @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } || [] } }
                       grep { $_ ne '__maps__' && $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'ticket' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 
@@ -3290,6 +3287,7 @@ sub _parser {
                           || @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ active }  || [] }
                       }
                       grep { $_ ne '__maps__' && $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'ticket' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 
diff --git a/lib/RT/Transactions.pm b/lib/RT/Transactions.pm
index 6ee5697bb4..b465f3d8ff 100644
--- a/lib/RT/Transactions.pm
+++ b/lib/RT/Transactions.pm
@@ -896,18 +896,14 @@ sub _parser {
     );
     die join "; ", map { ref $_ eq 'ARRAY' ? $_->[ 0 ] : $_ } @results if @results;
 
+    my $queues = $tree->GetReferencedQueues( CurrentUser => $self->CurrentUser );
+    my %referenced_lifecycle = map { $_->{Lifecycle} => 1 } values %$queues;
+
     if ( RT->Config->Get('EnablePriorityAsString') ) {
-        my $queues = $tree->GetReferencedQueues( CurrentUser => $self->CurrentUser );
         my %config = RT->Config->Get('PriorityAsString');
         my @names;
         if (%$queues) {
-            for my $id ( keys %$queues ) {
-                my $queue = RT::Queue->new( $self->CurrentUser );
-                $queue->Load($id);
-                if ( $queue->Id ) {
-                    push @names, $queue->__Value('Name');    # Skip ACL check
-                }
-            }
+            @names = map { $_->{Name} } values %$queues;
         }
         else {
             @names = keys %config;
@@ -965,6 +961,7 @@ sub _parser {
                       map { $_ => $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } }
                       grep { @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ inactive } || [] } }
                       grep { $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'ticket' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 
@@ -1008,6 +1005,7 @@ sub _parser {
                           || @{ $RT::Lifecycle::LIFECYCLES{ $_ }{ active }  || [] }
                       }
                       grep { $RT::Lifecycle::LIFECYCLES_CACHE{ $_ }{ type } eq 'ticket' }
+                      grep { %referenced_lifecycle ? $referenced_lifecycle{$_} : 1 }
                       keys %RT::Lifecycle::LIFECYCLES;
                     return unless %lifecycle;
 

-----------------------------------------------------------------------


hooks/post-receive
-- 
rt


More information about the rt-commit mailing list