[Rt-commit] rt branch, 4.2/cf-searching, created. rt-4.1.8-189-gcb1a739
Alex Vandiver
alexmv at bestpractical.com
Fri May 10 03:13:14 EDT 2013
The branch, 4.2/cf-searching has been created
at cb1a739cfb79b125a441a6dbe2a862049218b6d3 (commit)
- Log -----------------------------------------------------------------
commit 890d47ef1013760ae225ce8190e0528cc1543871
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:10:13 2013 -0500
Move custom field search code into RT::SearchBuilder
This merely moves two functions as-is, in preparation for refactoring
them to be useful on non-Ticket classes. As such, the code is placed in
_LimitCustomField, and the existing LimitCustomField implementation is
unchanged -- _LimitCustomField is still Ticket-specific.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index cd1c089..bc47576 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -202,6 +202,105 @@ sub _SingularClass {
return $class;
}
+=head2 _CustomFieldJoin
+
+Factor out the Join of custom fields so we can use it for sorting too
+
+=cut
+
+sub _CustomFieldJoin {
+ my ($self, $cfkey, $cfid, $field) = @_;
+ # Perform one Join per CustomField
+ if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
+ $self->{_sql_cf_alias}{$cfkey} )
+ {
+ return ( $self->{_sql_object_cfv_alias}{$cfkey},
+ $self->{_sql_cf_alias}{$cfkey} );
+ }
+
+ my ($TicketCFs, $CFs);
+ if ( $cfid ) {
+ $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'ObjectId',
+ );
+ $self->Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'CustomField',
+ VALUE => $cfid,
+ ENTRYAGGREGATOR => 'AND'
+ );
+ }
+ else {
+ my $ocfalias = $self->Join(
+ TYPE => 'LEFT',
+ FIELD1 => 'Queue',
+ TABLE2 => 'ObjectCustomFields',
+ FIELD2 => 'ObjectId',
+ );
+
+ $self->Limit(
+ LEFTJOIN => $ocfalias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => '0',
+ );
+
+ $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $ocfalias,
+ FIELD1 => 'CustomField',
+ TABLE2 => 'CustomFields',
+ FIELD2 => 'id',
+ );
+ $self->Limit(
+ LEFTJOIN => $CFs,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'LookupType',
+ VALUE => 'RT::Queue-RT::Ticket',
+ );
+ $self->Limit(
+ LEFTJOIN => $CFs,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'Name',
+ VALUE => $field,
+ );
+
+ $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $CFs,
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'CustomField',
+ );
+ $self->Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'ObjectId',
+ VALUE => 'main.id',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ }
+ $self->Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'ObjectType',
+ VALUE => 'RT::Ticket',
+ ENTRYAGGREGATOR => 'AND'
+ );
+ $self->Limit(
+ LEFTJOIN => $TicketCFs,
+ FIELD => 'Disabled',
+ OPERATOR => '=',
+ VALUE => '0',
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ return ($TicketCFs, $CFs);
+}
+
sub LimitCustomField {
my $self = shift;
my %args = ( VALUE => undef,
@@ -242,6 +341,380 @@ sub LimitCustomField {
);
}
+use Regexp::Common qw(RE_net_IPv4);
+use Regexp::Common::net::CIDR;
+
+sub _LimitCustomField {
+ my $self = shift;
+ my ( $field, $queue, $cfid, $cf, $column, $op, $value, %rest ) = @_;
+
+
+# If we're trying to find custom fields that don't match something, we
+# want tickets where the custom field has no value at all. Note that
+# we explicitly don't include the "IS NULL" case, since we would
+# otherwise end up with a redundant clause.
+
+ my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
+ my $null_op = ( 'is not' eq lc($op) || 'is' eq lc($op) );
+
+ my $fix_op = sub {
+ return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle';
+
+ my %args = @_;
+ return %args unless $args{'FIELD'} eq 'LargeContent';
+
+ my $op = $args{'OPERATOR'};
+ if ( $op eq '=' ) {
+ $args{'OPERATOR'} = 'MATCHES';
+ }
+ elsif ( $op eq '!=' ) {
+ $args{'OPERATOR'} = 'NOT MATCHES';
+ }
+ elsif ( $op =~ /^[<>]=?$/ ) {
+ $args{'FUNCTION'} = "TO_CHAR( $args{'ALIAS'}.LargeContent )";
+ }
+ return %args;
+ };
+
+ if ( $cf && $cf->Type eq 'IPAddress' ) {
+ my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
+ if ($parsed) {
+ $value = $parsed;
+ }
+ else {
+ $RT::Logger->warn("$value is not a valid IPAddress");
+ }
+ }
+
+ if ( $cf && $cf->Type eq 'IPAddressRange' ) {
+
+ if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
+
+ # convert incomplete 192.168/24 to 192.168.0.0/24 format
+ $value =
+ join( '.', map $_ || 0, ( split /\./, $1 )[ 0 .. 3 ] ) . "/$2"
+ || $value;
+ }
+
+ my ( $start_ip, $end_ip ) =
+ RT::ObjectCustomFieldValue->ParseIPRange($value);
+ if ( $start_ip && $end_ip ) {
+ if ( $op =~ /^([<>])=?$/ ) {
+ my $is_less = $1 eq '<' ? 1 : 0;
+ if ( $is_less ) {
+ $value = $start_ip;
+ }
+ else {
+ $value = $end_ip;
+ }
+ }
+ else {
+ $value = join '-', $start_ip, $end_ip;
+ }
+ }
+ else {
+ $RT::Logger->warn("$value is not a valid IPAddressRange");
+ }
+ }
+
+ if ( $cf && $cf->Type =~ /^Date(?:Time)?$/ ) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+ if ( $date->Unix ) {
+
+ if (
+ $cf->Type eq 'Date'
+ || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i
+ || ( $value !~ /midnight|\d+:\d+:\d+/i
+ && $date->Time( Timezone => 'user' ) eq '00:00:00' )
+ )
+ {
+ $value = $date->Date( Timezone => 'user' );
+ }
+ else {
+ $value = $date->DateTime;
+ }
+ }
+ else {
+ $RT::Logger->warn("$value is not a valid date string");
+ }
+ }
+
+ my $single_value = !$cf || !$cfid || $cf->SingleValue;
+
+ my $cfkey = $cfid ? $cfid : "$queue.$field";
+
+ if ( $null_op && !$column ) {
+ # IS[ NOT] NULL without column is the same as has[ no] any CF value,
+ # we can reuse our default joins for this operation
+ # with column specified we have different situation
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ $self->_OpenParen;
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'id',
+ OPERATOR => $op,
+ VALUE => $value,
+ %rest
+ );
+ $self->Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ ) if $CFs;
+ $self->_CloseParen;
+ }
+ elsif ( $op !~ /^[<>]=?$/ && ( $cf && $cf->Type eq 'IPAddressRange')) {
+
+ my ($start_ip, $end_ip) = split /-/, $value;
+
+ $self->_OpenParen;
+ if ( $op !~ /NOT|!=|<>/i ) { # positive equation
+ $self->_LimitCustomField(
+ $field, $queue, $cfid, $cf, 'Content', '<=', $end_ip, %rest,
+ );
+ $self->_LimitCustomField(
+ $field, $queue, $cfid, $cf, 'LargeContent', '>=', $start_ip, %rest,
+ ENTRYAGGREGATOR => 'AND',
+ );
+ # as well limit borders so DB optimizers can use better
+ # estimations and scan less rows
+# have to disable this tweak because of ipv6
+# $self->_CustomFieldLimit(
+# $field, '>=', '000.000.000.000', %rest,
+# SUBKEY => $rest{'SUBKEY'}. '.Content',
+# ENTRYAGGREGATOR => 'AND',
+# );
+# $self->_CustomFieldLimit(
+# $field, '<=', '255.255.255.255', %rest,
+# SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
+# ENTRYAGGREGATOR => 'AND',
+# );
+ }
+ else { # negative equation
+ $self->_LimitCustomField( $field, $queue, $cfid, $cf, 'Content', '>', $end_ip, %rest);
+ $self->_LimitCustomField(
+ $field, $queue, $cfid, $cf, 'LargeContent', '<', $start_ip, %rest,
+ ENTRYAGGREGATOR => 'OR',
+ );
+ # TODO: as well limit borders so DB optimizers can use better
+ # estimations and scan less rows, but it's harder to do
+ # as we have OR aggregator
+ }
+ $self->_CloseParen;
+ }
+ elsif ( !$negative_op || $single_value ) {
+ $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+
+ $self->_OpenParen;
+
+ $self->_OpenParen;
+
+ $self->_OpenParen;
+ # if column is defined then deal only with it
+ # otherwise search in Content and in LargeContent
+ if ( $column ) {
+ $self->Limit( $fix_op->(
+ ALIAS => $TicketCFs,
+ FIELD => $column,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ %rest
+ ) );
+ $self->_CloseParen;
+ $self->_CloseParen;
+ $self->_CloseParen;
+ }
+ else {
+ # need special treatment for Date
+ if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
+ # no time specified, that means we want everything on a
+ # particular day. in the database, we need to check for >
+ # and < the edges of that day.
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+ my $daystart = $date->ISO;
+ $date->AddDay;
+ my $dayend = $date->ISO;
+
+ $self->_OpenParen;
+
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => ">=",
+ VALUE => $daystart,
+ %rest,
+ );
+
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => "<",
+ VALUE => $dayend,
+ %rest,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ $self->_CloseParen;
+ }
+ elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
+ if ( length( Encode::encode_utf8($value) ) < 256 ) {
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ %rest
+ );
+ }
+ else {
+ $self->_OpenParen;
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => '=',
+ VALUE => '',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->_CloseParen;
+ $self->Limit( $fix_op->(
+ ALIAS => $TicketCFs,
+ FIELD => 'LargeContent',
+ OPERATOR => $op,
+ VALUE => $value,
+ ENTRYAGGREGATOR => 'AND',
+ CASESENSITIVE => 0,
+ ) );
+ }
+ }
+ else {
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ %rest
+ );
+
+ $self->_OpenParen;
+ $self->_OpenParen;
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => '=',
+ VALUE => '',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->_CloseParen;
+ $self->Limit( $fix_op->(
+ ALIAS => $TicketCFs,
+ FIELD => 'LargeContent',
+ OPERATOR => $op,
+ VALUE => $value,
+ ENTRYAGGREGATOR => 'AND',
+ CASESENSITIVE => 0,
+ ) );
+ $self->_CloseParen;
+ }
+ $self->_CloseParen;
+
+ # XXX: if we join via CustomFields table then
+ # because of order of left joins we get NULLs in
+ # CF table and then get nulls for those records
+ # in OCFVs table what result in wrong results
+ # as decifer method now tries to load a CF then
+ # we fall into this situation only when there
+ # are more than one CF with the name in the DB.
+ # the same thing applies to order by call.
+ # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
+ # we want treat IS NULL as (not applies or has
+ # no value)
+ $self->Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ ) if $CFs;
+ $self->_CloseParen;
+
+ if ($negative_op) {
+ $self->Limit(
+ ALIAS => $TicketCFs,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'OR',
+ );
+ }
+
+ $self->_CloseParen;
+ }
+ }
+ else {
+ $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+
+ # reverse operation
+ $op =~ s/!|NOT\s+//i;
+
+ # if column is defined then deal only with it
+ # otherwise search in Content and in LargeContent
+ if ( $column ) {
+ $self->Limit( $fix_op->(
+ LEFTJOIN => $TicketCFs,
+ ALIAS => $TicketCFs,
+ FIELD => $column,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ ) );
+ }
+ else {
+ $self->Limit(
+ LEFTJOIN => $TicketCFs,
+ ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+ }
+ $self->Limit(
+ %rest,
+ ALIAS => $TicketCFs,
+ FIELD => 'id',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ );
+ }
+}
+
=head2 Limit PARAMHASH
This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 6437d78..26b84bf 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1083,105 +1083,6 @@ sub _CustomFieldDecipher {
return ($queue, $field, $cf, $column);
}
-=head2 _CustomFieldJoin
-
-Factor out the Join of custom fields so we can use it for sorting too
-
-=cut
-
-sub _CustomFieldJoin {
- my ($self, $cfkey, $cfid, $field) = @_;
- # Perform one Join per CustomField
- if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
- $self->{_sql_cf_alias}{$cfkey} )
- {
- return ( $self->{_sql_object_cfv_alias}{$cfkey},
- $self->{_sql_cf_alias}{$cfkey} );
- }
-
- my ($TicketCFs, $CFs);
- if ( $cfid ) {
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'ObjectCustomFieldValues',
- FIELD2 => 'ObjectId',
- );
- $self->Limit(
- LEFTJOIN => $TicketCFs,
- FIELD => 'CustomField',
- VALUE => $cfid,
- ENTRYAGGREGATOR => 'AND'
- );
- }
- else {
- my $ocfalias = $self->Join(
- TYPE => 'LEFT',
- FIELD1 => 'Queue',
- TABLE2 => 'ObjectCustomFields',
- FIELD2 => 'ObjectId',
- );
-
- $self->Limit(
- LEFTJOIN => $ocfalias,
- ENTRYAGGREGATOR => 'OR',
- FIELD => 'ObjectId',
- VALUE => '0',
- );
-
- $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => $ocfalias,
- FIELD1 => 'CustomField',
- TABLE2 => 'CustomFields',
- FIELD2 => 'id',
- );
- $self->Limit(
- LEFTJOIN => $CFs,
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'LookupType',
- VALUE => 'RT::Queue-RT::Ticket',
- );
- $self->Limit(
- LEFTJOIN => $CFs,
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'Name',
- VALUE => $field,
- );
-
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => $CFs,
- FIELD1 => 'id',
- TABLE2 => 'ObjectCustomFieldValues',
- FIELD2 => 'CustomField',
- );
- $self->Limit(
- LEFTJOIN => $TicketCFs,
- FIELD => 'ObjectId',
- VALUE => 'main.id',
- QUOTEVALUE => 0,
- ENTRYAGGREGATOR => 'AND',
- );
- }
- $self->Limit(
- LEFTJOIN => $TicketCFs,
- FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
- ENTRYAGGREGATOR => 'AND'
- );
- $self->Limit(
- LEFTJOIN => $TicketCFs,
- FIELD => 'Disabled',
- OPERATOR => '=',
- VALUE => '0',
- ENTRYAGGREGATOR => 'AND'
- );
-
- return ($TicketCFs, $CFs);
-}
-
=head2 _CustomFieldLimit
Limit based on CustomFields
@@ -1191,10 +1092,6 @@ Meta Data:
=cut
-use Regexp::Common qw(RE_net_IPv4);
-use Regexp::Common::net::CIDR;
-
-
sub _CustomFieldLimit {
my ( $self, $_field, $op, $value, %rest ) = @_;
@@ -1206,373 +1103,7 @@ sub _CustomFieldLimit {
($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
$cfid = $cf ? $cf->id : 0 ;
-# If we're trying to find custom fields that don't match something, we
-# want tickets where the custom field has no value at all. Note that
-# we explicitly don't include the "IS NULL" case, since we would
-# otherwise end up with a redundant clause.
-
- my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
- my $null_op = ( 'is not' eq lc($op) || 'is' eq lc($op) );
-
- my $fix_op = sub {
- return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle';
-
- my %args = @_;
- return %args unless $args{'FIELD'} eq 'LargeContent';
-
- my $op = $args{'OPERATOR'};
- if ( $op eq '=' ) {
- $args{'OPERATOR'} = 'MATCHES';
- }
- elsif ( $op eq '!=' ) {
- $args{'OPERATOR'} = 'NOT MATCHES';
- }
- elsif ( $op =~ /^[<>]=?$/ ) {
- $args{'FUNCTION'} = "TO_CHAR( $args{'ALIAS'}.LargeContent )";
- }
- return %args;
- };
-
- if ( $cf && $cf->Type eq 'IPAddress' ) {
- my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
- if ($parsed) {
- $value = $parsed;
- }
- else {
- $RT::Logger->warn("$value is not a valid IPAddress");
- }
- }
-
- if ( $cf && $cf->Type eq 'IPAddressRange' ) {
-
- if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
-
- # convert incomplete 192.168/24 to 192.168.0.0/24 format
- $value =
- join( '.', map $_ || 0, ( split /\./, $1 )[ 0 .. 3 ] ) . "/$2"
- || $value;
- }
-
- my ( $start_ip, $end_ip ) =
- RT::ObjectCustomFieldValue->ParseIPRange($value);
- if ( $start_ip && $end_ip ) {
- if ( $op =~ /^([<>])=?$/ ) {
- my $is_less = $1 eq '<' ? 1 : 0;
- if ( $is_less ) {
- $value = $start_ip;
- }
- else {
- $value = $end_ip;
- }
- }
- else {
- $value = join '-', $start_ip, $end_ip;
- }
- }
- else {
- $RT::Logger->warn("$value is not a valid IPAddressRange");
- }
- }
-
- if ( $cf && $cf->Type =~ /^Date(?:Time)?$/ ) {
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
- if ( $date->Unix ) {
-
- if (
- $cf->Type eq 'Date'
- || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i
- || ( $value !~ /midnight|\d+:\d+:\d+/i
- && $date->Time( Timezone => 'user' ) eq '00:00:00' )
- )
- {
- $value = $date->Date( Timezone => 'user' );
- }
- else {
- $value = $date->DateTime;
- }
- }
- else {
- $RT::Logger->warn("$value is not a valid date string");
- }
- }
-
- my $single_value = !$cf || !$cfid || $cf->SingleValue;
-
- my $cfkey = $cfid ? $cfid : "$queue.$field";
-
- if ( $null_op && !$column ) {
- # IS[ NOT] NULL without column is the same as has[ no] any CF value,
- # we can reuse our default joins for this operation
- # with column specified we have different situation
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
- $self->_OpenParen;
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'id',
- OPERATOR => $op,
- VALUE => $value,
- %rest
- );
- $self->Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- OPERATOR => 'IS NOT',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- ENTRYAGGREGATOR => 'AND',
- ) if $CFs;
- $self->_CloseParen;
- }
- elsif ( $op !~ /^[<>]=?$/ && ( $cf && $cf->Type eq 'IPAddressRange')) {
-
- my ($start_ip, $end_ip) = split /-/, $value;
-
- $self->_OpenParen;
- if ( $op !~ /NOT|!=|<>/i ) { # positive equation
- $self->_CustomFieldLimit(
- 'CF', '<=', $end_ip, %rest,
- SUBKEY => $rest{'SUBKEY'}. '.Content',
- );
- $self->_CustomFieldLimit(
- 'CF', '>=', $start_ip, %rest,
- SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
- ENTRYAGGREGATOR => 'AND',
- );
- # as well limit borders so DB optimizers can use better
- # estimations and scan less rows
-# have to disable this tweak because of ipv6
-# $self->_CustomFieldLimit(
-# $field, '>=', '000.000.000.000', %rest,
-# SUBKEY => $rest{'SUBKEY'}. '.Content',
-# ENTRYAGGREGATOR => 'AND',
-# );
-# $self->_CustomFieldLimit(
-# $field, '<=', '255.255.255.255', %rest,
-# SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
-# ENTRYAGGREGATOR => 'AND',
-# );
- }
- else { # negative equation
- $self->_CustomFieldLimit($field, '>', $end_ip, %rest);
- $self->_CustomFieldLimit(
- $field, '<', $start_ip, %rest,
- SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
- ENTRYAGGREGATOR => 'OR',
- );
- # TODO: as well limit borders so DB optimizers can use better
- # estimations and scan less rows, but it's harder to do
- # as we have OR aggregator
- }
- $self->_CloseParen;
- }
- elsif ( !$negative_op || $single_value ) {
- $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
-
- $self->_OpenParen;
-
- $self->_OpenParen;
-
- $self->_OpenParen;
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
- if ( $column ) {
- $self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
- FIELD => $column,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- %rest
- ) );
- $self->_CloseParen;
- $self->_CloseParen;
- $self->_CloseParen;
- }
- else {
- # need special treatment for Date
- if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
- # no time specified, that means we want everything on a
- # particular day. in the database, we need to check for >
- # and < the edges of that day.
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
- my $daystart = $date->ISO;
- $date->AddDay;
- my $dayend = $date->ISO;
-
- $self->_OpenParen;
-
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => ">=",
- VALUE => $daystart,
- %rest,
- );
-
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => "<",
- VALUE => $dayend,
- %rest,
- ENTRYAGGREGATOR => 'AND',
- );
-
- $self->_CloseParen;
- }
- elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
- if ( length( Encode::encode_utf8($value) ) < 256 ) {
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- %rest
- );
- }
- else {
- $self->_OpenParen;
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->_CloseParen;
- $self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- CASESENSITIVE => 0,
- ) );
- }
- }
- else {
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- %rest
- );
-
- $self->_OpenParen;
- $self->_OpenParen;
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->_CloseParen;
- $self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- CASESENSITIVE => 0,
- ) );
- $self->_CloseParen;
- }
- $self->_CloseParen;
-
- # XXX: if we join via CustomFields table then
- # because of order of left joins we get NULLs in
- # CF table and then get nulls for those records
- # in OCFVs table what result in wrong results
- # as decifer method now tries to load a CF then
- # we fall into this situation only when there
- # are more than one CF with the name in the DB.
- # the same thing applies to order by call.
- # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
- # we want treat IS NULL as (not applies or has
- # no value)
- $self->Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- OPERATOR => 'IS NOT',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- ENTRYAGGREGATOR => 'AND',
- ) if $CFs;
- $self->_CloseParen;
-
- if ($negative_op) {
- $self->Limit(
- ALIAS => $TicketCFs,
- FIELD => $column || 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- ENTRYAGGREGATOR => 'OR',
- );
- }
-
- $self->_CloseParen;
- }
- }
- else {
- $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
-
- # reverse operation
- $op =~ s/!|NOT\s+//i;
-
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
- if ( $column ) {
- $self->Limit( $fix_op->(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
- FIELD => $column,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- ) );
- }
- else {
- $self->Limit(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
- $self->Limit(
- %rest,
- ALIAS => $TicketCFs,
- FIELD => 'id',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- );
- }
+ $self->_LimitCustomField( $field, $queue, $cfid, $cf, $column, $op, $value, %rest );
}
sub _HasAttributeLimit {
commit 12fc2f4ac64c26e3d77bbd071ace76def757239a
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:18:52 2013 -0500
Rename $TicketCFs variable to be more general
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index bc47576..8c11caf 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -218,9 +218,9 @@ sub _CustomFieldJoin {
$self->{_sql_cf_alias}{$cfkey} );
}
- my ($TicketCFs, $CFs);
+ my ($ocfvalias, $CFs);
if ( $cfid ) {
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ $ocfvalias = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
TYPE => 'LEFT',
ALIAS1 => 'main',
FIELD1 => 'id',
@@ -228,7 +228,7 @@ sub _CustomFieldJoin {
FIELD2 => 'ObjectId',
);
$self->Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
FIELD => 'CustomField',
VALUE => $cfid,
ENTRYAGGREGATOR => 'AND'
@@ -269,7 +269,7 @@ sub _CustomFieldJoin {
VALUE => $field,
);
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ $ocfvalias = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
TYPE => 'LEFT',
ALIAS1 => $CFs,
FIELD1 => 'id',
@@ -277,7 +277,7 @@ sub _CustomFieldJoin {
FIELD2 => 'CustomField',
);
$self->Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
FIELD => 'ObjectId',
VALUE => 'main.id',
QUOTEVALUE => 0,
@@ -285,20 +285,20 @@ sub _CustomFieldJoin {
);
}
$self->Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
FIELD => 'ObjectType',
VALUE => 'RT::Ticket',
ENTRYAGGREGATOR => 'AND'
);
$self->Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
FIELD => 'Disabled',
OPERATOR => '=',
VALUE => '0',
ENTRYAGGREGATOR => 'AND'
);
- return ($TicketCFs, $CFs);
+ return ($ocfvalias, $CFs);
}
sub LimitCustomField {
@@ -448,10 +448,10 @@ sub _LimitCustomField {
# IS[ NOT] NULL without column is the same as has[ no] any CF value,
# we can reuse our default joins for this operation
# with column specified we have different situation
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
$self->_OpenParen;
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'id',
OPERATOR => $op,
VALUE => $value,
@@ -508,7 +508,7 @@ sub _LimitCustomField {
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
$self->_OpenParen;
@@ -519,7 +519,7 @@ sub _LimitCustomField {
# otherwise search in Content and in LargeContent
if ( $column ) {
$self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
@@ -545,7 +545,7 @@ sub _LimitCustomField {
$self->_OpenParen;
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => ">=",
VALUE => $daystart,
@@ -553,7 +553,7 @@ sub _LimitCustomField {
);
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => "<",
VALUE => $dayend,
@@ -566,7 +566,7 @@ sub _LimitCustomField {
elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
@@ -577,14 +577,14 @@ sub _LimitCustomField {
else {
$self->_OpenParen;
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
@@ -592,7 +592,7 @@ sub _LimitCustomField {
);
$self->_CloseParen;
$self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
@@ -603,7 +603,7 @@ sub _LimitCustomField {
}
else {
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
@@ -614,14 +614,14 @@ sub _LimitCustomField {
$self->_OpenParen;
$self->_OpenParen;
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
@@ -629,7 +629,7 @@ sub _LimitCustomField {
);
$self->_CloseParen;
$self->Limit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
@@ -663,7 +663,7 @@ sub _LimitCustomField {
if ($negative_op) {
$self->Limit(
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => $column || 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
@@ -677,7 +677,7 @@ sub _LimitCustomField {
}
else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
# reverse operation
$op =~ s/!|NOT\s+//i;
@@ -686,8 +686,8 @@ sub _LimitCustomField {
# otherwise search in Content and in LargeContent
if ( $column ) {
$self->Limit( $fix_op->(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
+ ALIAS => $ocfvalias,
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
@@ -696,8 +696,8 @@ sub _LimitCustomField {
}
else {
$self->Limit(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
+ LEFTJOIN => $ocfvalias,
+ ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
@@ -706,7 +706,7 @@ sub _LimitCustomField {
}
$self->Limit(
%rest,
- ALIAS => $TicketCFs,
+ ALIAS => $ocfvalias,
FIELD => 'id',
OPERATOR => 'IS',
VALUE => 'NULL',
commit 84998f28400cd21fb7eaa07f17a7baa6b327a59b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:23:18 2013 -0500
Simplify _CustomFieldJoin to take a key and a CF||string
The logic surrounding the arguments for _CustomFieldJoin is more complex
than necessary; simplify it by expecting either a CF name of a CF object.
diff --git a/lib/RT/Report/Tickets.pm b/lib/RT/Report/Tickets.pm
index c39fa90..3d3336c 100644
--- a/lib/RT/Report/Tickets.pm
+++ b/lib/RT/Report/Tickets.pm
@@ -246,7 +246,7 @@ sub _FieldToFunction {
unless ( $cf->id ) {
$RT::Logger->error("Couldn't load CustomField #$cf_name");
} else {
- my ($ticket_cf_alias, $cf_alias) = $self->_CustomFieldJoin($cf->id, $cf->id, $cf_name);
+ my ($ticket_cf_alias, $cf_alias) = $self->_CustomFieldJoin($cf->id, $cf);
@args{qw(ALIAS FIELD)} = ($ticket_cf_alias, 'Content');
}
} elsif ( $field =~ /^(?:(Owner|Creator|LastUpdatedBy))(?:\.(.*))?$/ ) {
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 8c11caf..0e8dccd 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -72,6 +72,8 @@ use base qw(DBIx::SearchBuilder RT::Base);
use RT::Base;
use DBIx::SearchBuilder "1.40";
+use Scalar::Util qw/blessed/;
+
sub _Init {
my $self = shift;
@@ -209,7 +211,7 @@ Factor out the Join of custom fields so we can use it for sorting too
=cut
sub _CustomFieldJoin {
- my ($self, $cfkey, $cfid, $field) = @_;
+ my ($self, $cfkey, $cf) = @_;
# Perform one Join per CustomField
if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
$self->{_sql_cf_alias}{$cfkey} )
@@ -219,7 +221,7 @@ sub _CustomFieldJoin {
}
my ($ocfvalias, $CFs);
- if ( $cfid ) {
+ if ( blessed($cf) ) {
$ocfvalias = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
TYPE => 'LEFT',
ALIAS1 => 'main',
@@ -230,7 +232,7 @@ sub _CustomFieldJoin {
$self->Limit(
LEFTJOIN => $ocfvalias,
FIELD => 'CustomField',
- VALUE => $cfid,
+ VALUE => $cf->id,
ENTRYAGGREGATOR => 'AND'
);
}
@@ -266,7 +268,7 @@ sub _CustomFieldJoin {
LEFTJOIN => $CFs,
ENTRYAGGREGATOR => 'AND',
FIELD => 'Name',
- VALUE => $field,
+ VALUE => $cf,
);
$ocfvalias = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
@@ -448,7 +450,7 @@ sub _LimitCustomField {
# IS[ NOT] NULL without column is the same as has[ no] any CF value,
# we can reuse our default joins for this operation
# with column specified we have different situation
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
$self->_OpenParen;
$self->Limit(
ALIAS => $ocfvalias,
@@ -508,7 +510,7 @@ sub _LimitCustomField {
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
$self->_OpenParen;
@@ -677,7 +679,7 @@ sub _LimitCustomField {
}
else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
# reverse operation
$op =~ s/!|NOT\s+//i;
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 26b84bf..3a8e6e1 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1212,7 +1212,7 @@ sub OrderByCols {
my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field";
$cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
+ my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj || $field) );
# this is described in _CustomFieldLimit
$self->Limit(
ALIAS => $CFs,
commit d799fe63fd85ca4576e613c1ec8e4fc4d98e3fb7
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:29:18 2013 -0500
Switch to a more standard paramhash for arguments
This also collapses the $cfid, $cf, $queue, and $field arguments down
into the more understandable KEY and CUSTOMFIELD.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 0e8dccd..a750ccb 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -348,8 +348,22 @@ use Regexp::Common::net::CIDR;
sub _LimitCustomField {
my $self = shift;
- my ( $field, $queue, $cfid, $cf, $column, $op, $value, %rest ) = @_;
+ my %args = ( VALUE => undef,
+ CUSTOMFIELD => undef,
+ OPERATOR => '=',
+ KEY => undef,
+ @_ );
+ my $op = delete $args{OPERATOR};
+ my $value = delete $args{VALUE};
+ my $cf = delete $args{CUSTOMFIELD};
+ my $column = delete $args{COLUMN};
+ my $cfkey = delete $args{KEY};
+ if (blessed($cf) and $cf->id) {
+ $cfkey ||= $cf->id;
+ } else {
+ $cfkey ||= $cf;
+ }
# If we're trying to find custom fields that don't match something, we
# want tickets where the custom field has no value at all. Note that
@@ -378,7 +392,7 @@ sub _LimitCustomField {
return %args;
};
- if ( $cf && $cf->Type eq 'IPAddress' ) {
+ if ( blessed($cf) && $cf->Type eq 'IPAddress' ) {
my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
if ($parsed) {
$value = $parsed;
@@ -388,7 +402,7 @@ sub _LimitCustomField {
}
}
- if ( $cf && $cf->Type eq 'IPAddressRange' ) {
+ if ( blessed($cf) && $cf->Type eq 'IPAddressRange' ) {
if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
@@ -419,7 +433,7 @@ sub _LimitCustomField {
}
}
- if ( $cf && $cf->Type =~ /^Date(?:Time)?$/ ) {
+ if ( blessed($cf) && $cf->Type =~ /^Date(?:Time)?$/ ) {
my $date = RT::Date->new( $self->CurrentUser );
$date->Set( Format => 'unknown', Value => $value );
if ( $date->Unix ) {
@@ -442,22 +456,20 @@ sub _LimitCustomField {
}
}
- my $single_value = !$cf || !$cfid || $cf->SingleValue;
-
- my $cfkey = $cfid ? $cfid : "$queue.$field";
+ my $single_value = !blessed($cf) || $cf->SingleValue;
if ( $null_op && !$column ) {
# IS[ NOT] NULL without column is the same as has[ no] any CF value,
# we can reuse our default joins for this operation
# with column specified we have different situation
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen;
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'id',
OPERATOR => $op,
VALUE => $value,
- %rest
+ %args
);
$self->Limit(
ALIAS => $CFs,
@@ -469,19 +481,26 @@ sub _LimitCustomField {
) if $CFs;
$self->_CloseParen;
}
- elsif ( $op !~ /^[<>]=?$/ && ( $cf && $cf->Type eq 'IPAddressRange')) {
-
+ elsif ( $op !~ /^[<>]=?$/ && ( blessed($cf) && $cf->Type eq 'IPAddressRange')) {
my ($start_ip, $end_ip) = split /-/, $value;
$self->_OpenParen;
if ( $op !~ /NOT|!=|<>/i ) { # positive equation
$self->_LimitCustomField(
- $field, $queue, $cfid, $cf, 'Content', '<=', $end_ip, %rest,
+ OPERATOR => '<=',
+ VALUE => $end_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ %args,
);
$self->_LimitCustomField(
- $field, $queue, $cfid, $cf, 'LargeContent', '>=', $start_ip, %rest,
+ OPERATOR => '>=',
+ VALUE => $start_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'LargeContent',
+ %args,
ENTRYAGGREGATOR => 'AND',
- );
+ );
# as well limit borders so DB optimizers can use better
# estimations and scan less rows
# have to disable this tweak because of ipv6
@@ -497,11 +516,21 @@ sub _LimitCustomField {
# );
}
else { # negative equation
- $self->_LimitCustomField( $field, $queue, $cfid, $cf, 'Content', '>', $end_ip, %rest);
$self->_LimitCustomField(
- $field, $queue, $cfid, $cf, 'LargeContent', '<', $start_ip, %rest,
+ OPERATOR => '>',
+ VALUE => $end_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ %args,
+ );
+ $self->_LimitCustomField(
+ OPERATOR => '<',
+ VALUE => $start_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'LargeContent',
+ %args,
ENTRYAGGREGATOR => 'OR',
- );
+ );
# TODO: as well limit borders so DB optimizers can use better
# estimations and scan less rows, but it's harder to do
# as we have OR aggregator
@@ -510,7 +539,7 @@ sub _LimitCustomField {
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen;
@@ -526,7 +555,7 @@ sub _LimitCustomField {
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %rest
+ %args
) );
$self->_CloseParen;
$self->_CloseParen;
@@ -534,7 +563,7 @@ sub _LimitCustomField {
}
else {
# need special treatment for Date
- if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
+ if ( blessed($cf) and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
# no time specified, that means we want everything on a
# particular day. in the database, we need to check for >
# and < the edges of that day.
@@ -551,7 +580,7 @@ sub _LimitCustomField {
FIELD => 'Content',
OPERATOR => ">=",
VALUE => $daystart,
- %rest,
+ %args,
);
$self->Limit(
@@ -559,7 +588,7 @@ sub _LimitCustomField {
FIELD => 'Content',
OPERATOR => "<",
VALUE => $dayend,
- %rest,
+ %args,
ENTRYAGGREGATOR => 'AND',
);
@@ -573,7 +602,7 @@ sub _LimitCustomField {
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %rest
+ %args
);
}
else {
@@ -610,7 +639,7 @@ sub _LimitCustomField {
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %rest
+ %args
);
$self->_OpenParen;
@@ -679,7 +708,7 @@ sub _LimitCustomField {
}
else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf || $field) );
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
# reverse operation
$op =~ s/!|NOT\s+//i;
@@ -707,7 +736,7 @@ sub _LimitCustomField {
);
}
$self->Limit(
- %rest,
+ %args,
ALIAS => $ocfvalias,
FIELD => 'id',
OPERATOR => 'IS',
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 3a8e6e1..cb7f6da 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1101,9 +1101,17 @@ sub _CustomFieldLimit {
my ($queue, $cfid, $cf, $column);
($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
- $cfid = $cf ? $cf->id : 0 ;
- $self->_LimitCustomField( $field, $queue, $cfid, $cf, $column, $op, $value, %rest );
+
+ $self->_LimitCustomField(
+ %rest,
+ CUSTOMFIELD => $cf || $field,
+ KEY => $cf ? $cf->id : "$queue.$field",
+ OPERATOR => $op,
+ VALUE => $value,
+ COLUMN => $column,
+ SUBCLAUSE => "ticketsql",
+ );
}
sub _HasAttributeLimit {
commit 4dff5928064de2375399b0b37f1069ff7485d2a7
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 19:07:54 2013 -0400
Prevent confusion about what %args can override
As the basic arguments are delete()'d from %args, all the remains
therein are extra arguments, like ENTRYAGGREGATOR or SUBCLAUSE. Clarify
the precedence of the arguments by moving %args to the beginning of the
Limit() call; this changes no functionality.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index a750ccb..285a8ac 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -465,11 +465,11 @@ sub _LimitCustomField {
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen;
$self->Limit(
+ %args,
ALIAS => $ocfvalias,
FIELD => 'id',
OPERATOR => $op,
VALUE => $value,
- %args
);
$self->Limit(
ALIAS => $CFs,
@@ -487,18 +487,18 @@ sub _LimitCustomField {
$self->_OpenParen;
if ( $op !~ /NOT|!=|<>/i ) { # positive equation
$self->_LimitCustomField(
+ %args,
OPERATOR => '<=',
VALUE => $end_ip,
CUSTOMFIELD => $cf,
COLUMN => 'Content',
- %args,
);
$self->_LimitCustomField(
+ %args,
OPERATOR => '>=',
VALUE => $start_ip,
CUSTOMFIELD => $cf,
COLUMN => 'LargeContent',
- %args,
ENTRYAGGREGATOR => 'AND',
);
# as well limit borders so DB optimizers can use better
@@ -517,18 +517,18 @@ sub _LimitCustomField {
}
else { # negative equation
$self->_LimitCustomField(
+ %args,
OPERATOR => '>',
VALUE => $end_ip,
CUSTOMFIELD => $cf,
COLUMN => 'Content',
- %args,
);
$self->_LimitCustomField(
+ %args,
OPERATOR => '<',
VALUE => $start_ip,
CUSTOMFIELD => $cf,
COLUMN => 'LargeContent',
- %args,
ENTRYAGGREGATOR => 'OR',
);
# TODO: as well limit borders so DB optimizers can use better
@@ -550,12 +550,12 @@ sub _LimitCustomField {
# otherwise search in Content and in LargeContent
if ( $column ) {
$self->Limit( $fix_op->(
+ %args,
ALIAS => $ocfvalias,
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %args
) );
$self->_CloseParen;
$self->_CloseParen;
@@ -576,19 +576,19 @@ sub _LimitCustomField {
$self->_OpenParen;
$self->Limit(
+ %args,
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => ">=",
VALUE => $daystart,
- %args,
);
$self->Limit(
+ %args,
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => "<",
VALUE => $dayend,
- %args,
ENTRYAGGREGATOR => 'AND',
);
@@ -597,12 +597,12 @@ sub _LimitCustomField {
elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
+ %args,
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %args
);
}
else {
@@ -634,12 +634,12 @@ sub _LimitCustomField {
}
else {
$self->Limit(
+ %args,
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- %args
);
$self->_OpenParen;
commit 8ee65ec20ab1b85b3895d84069f5b3671e4e7711
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:30:20 2013 -0500
Generalize _CustomFieldJoin somewhat
This replaces explicit RT::Ticket ObjectTypes and LookupTypes with their
more general forms. It also factors out the concept of listing all
applicable CFs that match a given name -- which may be additionally
limited by queues, in the case of tickets.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 285a8ac..f77e01c 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -237,59 +237,14 @@ sub _CustomFieldJoin {
);
}
else {
- my $ocfalias = $self->Join(
- TYPE => 'LEFT',
- FIELD1 => 'Queue',
- TABLE2 => 'ObjectCustomFields',
- FIELD2 => 'ObjectId',
- );
-
- $self->Limit(
- LEFTJOIN => $ocfalias,
- ENTRYAGGREGATOR => 'OR',
- FIELD => 'ObjectId',
- VALUE => '0',
- );
-
- $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => $ocfalias,
- FIELD1 => 'CustomField',
- TABLE2 => 'CustomFields',
- FIELD2 => 'id',
- );
- $self->Limit(
- LEFTJOIN => $CFs,
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'LookupType',
- VALUE => 'RT::Queue-RT::Ticket',
- );
- $self->Limit(
- LEFTJOIN => $CFs,
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'Name',
- VALUE => $cf,
- );
-
- $ocfvalias = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => $CFs,
- FIELD1 => 'id',
- TABLE2 => 'ObjectCustomFieldValues',
- FIELD2 => 'CustomField',
- );
- $self->Limit(
- LEFTJOIN => $ocfvalias,
- FIELD => 'ObjectId',
- VALUE => 'main.id',
- QUOTEVALUE => 0,
- ENTRYAGGREGATOR => 'AND',
- );
+ ($ocfvalias, $CFs) = $self->_CustomFieldJoinByName( $cf );
+ $self->{_sql_cf_alias}{$cfkey} = $CFs;
+ $self->{_sql_object_cfv_alias}{$cfkey} = $ocfvalias;
}
$self->Limit(
LEFTJOIN => $ocfvalias,
FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
+ VALUE => ref($self->NewItem),
ENTRYAGGREGATOR => 'AND'
);
$self->Limit(
@@ -303,6 +258,54 @@ sub _CustomFieldJoin {
return ($ocfvalias, $CFs);
}
+sub _CustomFieldJoinByName {
+ my $self = shift;
+ my ($cf) = @_;
+ my $ocfalias = $self->Join(
+ TYPE => 'LEFT',
+ EXPRESSION => q|'0'|,
+ TABLE2 => 'ObjectCustomFields',
+ FIELD2 => 'ObjectId',
+ );
+
+ my $CFs = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $ocfalias,
+ FIELD1 => 'CustomField',
+ TABLE2 => 'CustomFields',
+ FIELD2 => 'id',
+ );
+ $self->Limit(
+ LEFTJOIN => $CFs,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'LookupType',
+ VALUE => $self->NewItem->CustomFieldLookupType,
+ );
+ $self->Limit(
+ LEFTJOIN => $CFs,
+ ENTRYAGGREGATOR => 'AND',
+ FIELD => 'Name',
+ VALUE => $cf,
+ );
+
+ my $ocfvalias = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $CFs,
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'CustomField',
+ );
+ $self->Limit(
+ LEFTJOIN => $ocfvalias,
+ FIELD => 'ObjectId',
+ VALUE => 'main.id',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ return ($ocfvalias, $CFs, $ocfalias);
+}
+
sub LimitCustomField {
my $self = shift;
my %args = ( VALUE => undef,
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index cb7f6da..ed00341 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -86,6 +86,8 @@ use base 'RT::SearchBuilder';
use Role::Basic 'with';
with 'RT::SearchBuilder::Role::Roles';
+use Scalar::Util qw/blessed/;
+
use RT::Ticket;
use RT::SQL;
@@ -1114,6 +1116,21 @@ sub _CustomFieldLimit {
);
}
+sub _CustomFieldJoinByName {
+ my $self = shift;
+ my ($cf) = @_;
+
+ my ($ocfvalias, $CFs, $ocfalias) = $self->SUPER::_CustomFieldJoinByName($cf);
+ $self->Limit(
+ LEFTJOIN => $ocfalias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => 'main.Queue',
+ QUOTEVALUE => 0,
+ );
+ return ($ocfvalias, $CFs, $ocfalias);
+}
+
sub _HasAttributeLimit {
my ( $self, $field, $op, $value, %rest ) = @_;
commit c558d15e413c6fc0588dd474e974bcfebd3dc81f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:31:32 2013 -0500
Add explicit subclauses to all limits
RT::Tickets assumes a subclause of "ticketsql" for all Limit and
_OpenParen / _CloseParen calls. Allow calling on non-Tickets
collections by providing a default SUBCLAUSE name, and performing all
Limit, _OpenParen, and _CloseParen adjustments therein.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index f77e01c..abaea52 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -368,6 +368,8 @@ sub _LimitCustomField {
$cfkey ||= $cf;
}
+ $args{SUBCLAUSE} ||= "cf-$cfkey";
+
# If we're trying to find custom fields that don't match something, we
# want tickets where the custom field has no value at all. Note that
# we explicitly don't include the "IS NULL" case, since we would
@@ -466,7 +468,7 @@ sub _LimitCustomField {
# we can reuse our default joins for this operation
# with column specified we have different situation
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
%args,
ALIAS => $ocfvalias,
@@ -481,13 +483,13 @@ sub _LimitCustomField {
VALUE => 'NULL',
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
elsif ( $op !~ /^[<>]=?$/ && ( blessed($cf) && $cf->Type eq 'IPAddressRange')) {
my ($start_ip, $end_ip) = split /-/, $value;
-
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
if ( $op !~ /NOT|!=|<>/i ) { # positive equation
$self->_LimitCustomField(
%args,
@@ -538,17 +540,15 @@ sub _LimitCustomField {
# estimations and scan less rows, but it's harder to do
# as we have OR aggregator
}
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
- $self->_OpenParen;
-
- $self->_OpenParen;
-
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
# if column is defined then deal only with it
# otherwise search in Content and in LargeContent
if ( $column ) {
@@ -560,9 +560,9 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
) );
- $self->_CloseParen;
- $self->_CloseParen;
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
else {
# need special treatment for Date
@@ -576,7 +576,7 @@ sub _LimitCustomField {
$date->AddDay;
my $dayend = $date->ISO;
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
%args,
@@ -595,7 +595,7 @@ sub _LimitCustomField {
ENTRYAGGREGATOR => 'AND',
);
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
@@ -609,28 +609,31 @@ sub _LimitCustomField {
);
}
else {
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
- ENTRYAGGREGATOR => 'OR'
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
);
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR'
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
);
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
$self->Limit( $fix_op->(
ALIAS => $ocfvalias,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
CASESENSITIVE => 0,
) );
}
@@ -645,13 +648,14 @@ sub _LimitCustomField {
CASESENSITIVE => 0,
);
- $self->_OpenParen;
- $self->_OpenParen;
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
+ SUBCLAUSE => $args{SUBCLAUSE},
ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
@@ -659,20 +663,22 @@ sub _LimitCustomField {
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
+ SUBCLAUSE => $args{SUBCLAUSE},
ENTRYAGGREGATOR => 'OR'
);
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
$self->Limit( $fix_op->(
ALIAS => $ocfvalias,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
CASESENSITIVE => 0,
) );
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
# XXX: if we join via CustomFields table then
# because of order of left joins we get NULLs in
@@ -692,8 +698,9 @@ sub _LimitCustomField {
VALUE => 'NULL',
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
if ($negative_op) {
$self->Limit(
@@ -703,10 +710,11 @@ sub _LimitCustomField {
VALUE => 'NULL',
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
);
}
- $self->_CloseParen;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
}
else {
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index ed00341..694cdae 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1328,10 +1328,10 @@ sub _SQLJoin {
}
sub _OpenParen {
- $_[0]->SUPER::_OpenParen( 'ticketsql' );
+ $_[0]->SUPER::_OpenParen( $_[1] || 'ticketsql' );
}
sub _CloseParen {
- $_[0]->SUPER::_CloseParen( 'ticketsql' );
+ $_[0]->SUPER::_CloseParen( $_[1] || 'ticketsql' );
}
sub Limit {
commit 53b2a9a8d6974f21fff475f1d1c5f4df2ca96bb3
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Sat Apr 27 00:40:55 2013 -0400
Treat CF ids as specific identifiers like objects, not as names
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index abaea52..0cedc0f 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -364,6 +364,15 @@ sub _LimitCustomField {
my $cfkey = delete $args{KEY};
if (blessed($cf) and $cf->id) {
$cfkey ||= $cf->id;
+ } elsif ($cf =~ /^\d+$/) {
+ my $obj = RT::CustomField->new( $self->CurrentUser );
+ $obj->Load($cf);
+ if ($obj->id) {
+ $cf = $obj;
+ $cfkey ||= $cf->id;
+ } else {
+ $cfkey ||= $cf;
+ }
} else {
$cfkey ||= $cf;
}
commit 3a70b3c8127220ab3a557340e94d322e1a7b74fc
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 01:32:04 2013 -0500
Switch the general LimitCustomField to the new implementation
Use the new _LimitCustomField implementation for RT::SearchBuilder's
LimitCustomField. The former is not simply renamed to the latter
because RT::Tickets contains a LimitCustomField which acts on
"restrictions", and must continue to do so. As such, _LimitCustomField
(and its recursive calls) must continue to occupy some other name.
This removes the (dubious and unused) ability to call LimitCustomField
with no CUSTOMFIELD provided, which searches within all custom fields.
It adds all of the complex date and IP parsing capabilities that Tickets
have enjoyed.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 0cedc0f..9340637 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -308,42 +308,7 @@ sub _CustomFieldJoinByName {
sub LimitCustomField {
my $self = shift;
- my %args = ( VALUE => undef,
- CUSTOMFIELD => undef,
- OPERATOR => '=',
- @_ );
-
- my $alias = $self->Join(
- TYPE => 'left',
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'ObjectCustomFieldValues',
- FIELD2 => 'ObjectId'
- );
- $self->Limit(
- ALIAS => $alias,
- FIELD => 'CustomField',
- OPERATOR => '=',
- VALUE => $args{'CUSTOMFIELD'},
- ) if ($args{'CUSTOMFIELD'});
- $self->Limit(
- ALIAS => $alias,
- FIELD => 'ObjectType',
- OPERATOR => '=',
- VALUE => $self->_SingularClass,
- );
- $self->Limit(
- ALIAS => $alias,
- FIELD => 'Content',
- OPERATOR => $args{'OPERATOR'},
- VALUE => $args{'VALUE'},
- );
- $self->Limit(
- ALIAS => $alias,
- FIELD => 'Disabled',
- OPERATOR => '=',
- VALUE => 0,
- );
+ return $self->_LimitCustomField( @_ );
}
use Regexp::Common qw(RE_net_IPv4);
commit 0f6ee87be9a7c399d18e8c05f9ad90888b9ff6d2
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Jan 29 15:08:45 2013 -0500
Refactor CF ordering into a method on SearchBuilder
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 9340637..3b2d637 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -121,6 +121,51 @@ sub JoinTransactions {
return $alias;
}
+sub _OrderByCF {
+ my $self = shift;
+ my ($row, $cf) = @_;
+
+ my $cfkey = blessed($cf) ? $cf->id : $cf;
+ $cfkey .= ".ordering" if !blessed($cf) || ($cf->MaxValues||0) != 1;
+ my ($ocfvs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
+ # this is described in _LimitCustomField
+ $self->Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ QUOTEVALUE => 1,
+ ENTRYAGGREGATOR => 'AND',
+ ) if $CFs;
+ unless (blessed($cf)) {
+ # For those cases where we are doing a join against the
+ # CF name, and don't have a CFid, use Unique to make sure
+ # we don't show duplicate tickets. NOTE: I'm pretty sure
+ # this will stay mixed in for the life of the
+ # class/package, and not just for the life of the object.
+ # Potential performance issue.
+ require DBIx::SearchBuilder::Unique;
+ DBIx::SearchBuilder::Unique->import;
+ }
+ my $CFvs = $self->Join(
+ TYPE => 'LEFT',
+ ALIAS1 => $ocfvs,
+ FIELD1 => 'CustomField',
+ TABLE2 => 'CustomFieldValues',
+ FIELD2 => 'CustomField',
+ );
+ $self->Limit(
+ LEFTJOIN => $CFvs,
+ FIELD => 'Name',
+ QUOTEVALUE => 0,
+ VALUE => "$ocfvs.Content",
+ ENTRYAGGREGATOR => 'AND'
+ );
+
+ return { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' },
+ { %$row, ALIAS => $ocfvs, FIELD => 'Content' };
+}
+
sub OrderByCols {
my $self = shift;
my @sort;
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 694cdae..9d3abf1 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1234,46 +1234,8 @@ sub OrderByCols {
}
push @res, { %$row, ALIAS => $users, FIELD => $subkey };
} elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) {
- my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
- my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field";
- $cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj || $field) );
- # this is described in _CustomFieldLimit
- $self->Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- OPERATOR => 'IS NOT',
- VALUE => 'NULL',
- QUOTEVALUE => 1,
- ENTRYAGGREGATOR => 'AND',
- ) if $CFs;
- unless ($cf_obj) {
- # For those cases where we are doing a join against the
- # CF name, and don't have a CFid, use Unique to make sure
- # we don't show duplicate tickets. NOTE: I'm pretty sure
- # this will stay mixed in for the life of the
- # class/package, and not just for the life of the object.
- # Potential performance issue.
- require DBIx::SearchBuilder::Unique;
- DBIx::SearchBuilder::Unique->import;
- }
- my $CFvs = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => $TicketCFs,
- FIELD1 => 'CustomField',
- TABLE2 => 'CustomFieldValues',
- FIELD2 => 'CustomField',
- );
- $self->Limit(
- LEFTJOIN => $CFvs,
- FIELD => 'Name',
- QUOTEVALUE => 0,
- VALUE => $TicketCFs . ".Content",
- ENTRYAGGREGATOR => 'AND'
- );
-
- push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' };
- push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' };
+ my ($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $subkey );
+ push @res, $self->_OrderByCF( $row, ($cf || "$queue.$field") );
} elsif ( $field eq "Custom" && $subkey eq "Ownership") {
# PAW logic is "reversed"
my $order = "ASC";
commit 604a8ec5141fd88348b63ae4dc52afdc8bdfcfa3
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 18:34:14 2013 -0400
SearchBuilder forces QUOTEVALUE => 0 for IS and IS NOT limits
Remove the unnecessary (and in the first hunk, misleading) QUOTEVALUEs.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 3b2d637..38fdbb4 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -134,7 +134,6 @@ sub _OrderByCF {
FIELD => 'Name',
OPERATOR => 'IS NOT',
VALUE => 'NULL',
- QUOTEVALUE => 1,
ENTRYAGGREGATOR => 'AND',
) if $CFs;
unless (blessed($cf)) {
@@ -500,7 +499,6 @@ sub _LimitCustomField {
FIELD => 'Name',
OPERATOR => 'IS NOT',
VALUE => 'NULL',
- QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'AND',
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
@@ -715,7 +713,6 @@ sub _LimitCustomField {
FIELD => 'Name',
OPERATOR => 'IS NOT',
VALUE => 'NULL',
- QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'AND',
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
@@ -727,7 +724,6 @@ sub _LimitCustomField {
FIELD => $column || 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
- QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
);
@@ -771,7 +767,6 @@ sub _LimitCustomField {
FIELD => 'id',
OPERATOR => 'IS',
VALUE => 'NULL',
- QUOTEVALUE => 0,
);
}
}
commit bcd79f3afc85d8976a65b94c5de9c158b6d1962f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 19:06:28 2013 -0400
Remove uses of ::Unique; our SELECT DISTINCT is sufficient
Rather than import a mixin (twice, in Tickets' case (?!)) which
unique-ifies the results, rely on our SELECT DISCINCT machinery. The
original code was added when DistinctQuery was unimplemented for some
database handles.
diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index 48e571d..6834751 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -70,8 +70,6 @@ use warnings;
use base 'RT::SearchBuilder';
-use DBIx::SearchBuilder::Unique;
-
use RT::CustomField;
sub Table { 'CustomFields'}
diff --git a/lib/RT/Report/Tickets.pm b/lib/RT/Report/Tickets.pm
index 3d3336c..afd79e2 100644
--- a/lib/RT/Report/Tickets.pm
+++ b/lib/RT/Report/Tickets.pm
@@ -275,15 +275,6 @@ sub _FieldToFunction {
return %args;
}
-
-# Override the AddRecord from DBI::SearchBuilder::Unique. id isn't id here
-# wedon't want to disambiguate all the items with a count of 1.
-sub AddRecord {
- my $self = shift;
- my $record = shift;
- push @{$self->{'items'}}, $record;
-}
-
1;
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 38fdbb4..1f6e01b 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -136,16 +136,6 @@ sub _OrderByCF {
VALUE => 'NULL',
ENTRYAGGREGATOR => 'AND',
) if $CFs;
- unless (blessed($cf)) {
- # For those cases where we are doing a join against the
- # CF name, and don't have a CFid, use Unique to make sure
- # we don't show duplicate tickets. NOTE: I'm pretty sure
- # this will stay mixed in for the life of the
- # class/package, and not just for the life of the object.
- # Potential performance issue.
- require DBIx::SearchBuilder::Unique;
- DBIx::SearchBuilder::Unique->import;
- }
my $CFvs = $self->Join(
TYPE => 'LEFT',
ALIAS1 => $ocfvs,
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 9d3abf1..1dcccfb 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -94,7 +94,6 @@ use RT::SQL;
sub Table { 'Tickets'}
use RT::CustomFields;
-use DBIx::SearchBuilder::Unique;
# Configuration Tables:
commit 46f2e4c77aeb25aea99c249b1f7465fb033c72d9
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 16:07:40 2013 -0400
Ensure that ENTRYAGGRATOR does not mistakenly OR the ordering limit with some other limit
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 1f6e01b..13afc88 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -135,6 +135,7 @@ sub _OrderByCF {
OPERATOR => 'IS NOT',
VALUE => 'NULL',
ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => ".ordering",
) if $CFs;
my $CFvs = $self->Join(
TYPE => 'LEFT',
commit cfbb86257843a8844f7d318a5ec72966f9e42cdf
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 22:44:00 2013 -0400
Update style of test file, and ensure that tests run in prefictable order
diff --git a/t/ticket/search_by_cf_freeform_multiple.t b/t/ticket/search_by_cf_freeform_multiple.t
index 1b4f092..61ec426 100644
--- a/t/ticket/search_by_cf_freeform_multiple.t
+++ b/t/ticket/search_by_cf_freeform_multiple.t
@@ -2,8 +2,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 118;
-use RT::Ticket;
+use RT::Test nodata => 1, tests => undef;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $q && $q->id, 'loaded or created queue';
@@ -22,42 +21,19 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
$cf_id = $cf->id;
}
-my ($total, @data, @tickets, %test) = (0, ());
-
-sub run_tests {
- my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
- foreach my $key ( sort keys %test ) {
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL( "( $query_prefix ) AND ( $key )" );
-
- my $error = 0;
-
- my $count = 0;
- $count++ foreach grep $_, values %{ $test{$key} };
- is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1;
-
- my $good_tickets = ($tix->Count == $count);
- while ( my $ticket = $tix->Next ) {
- next if $test{$key}->{ $ticket->Subject };
- diag $ticket->Subject ." ticket has been found when it's not expected";
- $good_tickets = 0;
- }
- ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1;
-
- diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error;
- }
-}
+subtest "Creating tickets" => sub {
+ RT::Test->create_tickets( { Queue => $q->id },
+ { Subject => '-' },
+ { Subject => 'x', "CustomField-$cf_id" => 'x', },
+ { Subject => 'y', "CustomField-$cf_id" => 'y', },
+ { Subject => 'z', "CustomField-$cf_id" => 'z', },
+ { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], },
+ { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], },
+ { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], },
+ );
+};
- at data = (
- { Subject => '-' },
- { Subject => 'x', "CustomField-$cf_id" => 'x', },
- { Subject => 'y', "CustomField-$cf_id" => 'y', },
- { Subject => 'z', "CustomField-$cf_id" => 'z', },
- { Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], },
- { Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], },
- { Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], },
-);
-%test = (
+my @tests = (
"CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
"'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
"'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
@@ -113,13 +89,36 @@ sub run_tests {
"'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
"'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
);
- at tickets = RT::Test->create_tickets( { Queue => $q->id }, @data);
-$total = scalar @tickets;
+run_tests(@tests);
-{
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL("Queue = '$queue'");
- is($tix->Count, $total, "found $total tickets");
+
+sub run_tests {
+ my @tests = @_;
+ while (@tests) {
+ my $query = shift @tests;
+ my %results = %{ shift @tests };
+ subtest $query => sub {
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL( "$query" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %results;
+ is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $results{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good" ) or $error = 1;
+
+ diag "Wrong SQL: ". $tix->BuildSelectQuery if $error;
+ };
+ }
}
-run_tests();
+
+done_testing;
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index f8462a9..733df15 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -2,8 +2,7 @@
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 106;
-use RT::Ticket;
+use RT::Test nodata => 1, tests => undef;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $q && $q->id, 'loaded or created queue';
@@ -22,39 +21,19 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
$cf_id = $cf->id;
}
-my ($total, @data, @tickets, %test) = (0, ());
+my $other_q = RT::Test->load_or_create_queue( Name => 'Other' );
+ok $other_q && $other_q->id, 'loaded or created queue';
-sub run_tests {
- my $query_prefix = join ' OR ', map 'id = '. $_->id, @tickets;
- foreach my $key ( sort keys %test ) {
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL( "( $query_prefix ) AND ( $key )" );
-
- my $error = 0;
-
- my $count = 0;
- $count++ foreach grep $_, values %{ $test{$key} };
- is($tix->Count, $count, "found correct number of ticket(s) by '$key'") or $error = 1;
-
- my $good_tickets = ($tix->Count == $count);
- while ( my $ticket = $tix->Next ) {
- next if $test{$key}->{ $ticket->Subject };
- diag $ticket->Subject ." ticket has been found when it's not expected";
- $good_tickets = 0;
- }
- ok( $good_tickets, "all tickets are good with '$key'" ) or $error = 1;
-
- diag "Wrong SQL query for '$key':". $tix->BuildSelectQuery if $error;
- }
-}
+subtest "Creating tickets" => sub {
+ RT::Test->create_tickets( { Queue => $q->id },
+ { Subject => '-' },
+ { Subject => 'x', "CustomField-$cf_id" => 'x', },
+ { Subject => 'y', "CustomField-$cf_id" => 'y', },
+ { Subject => 'z', "CustomField-$cf_id" => 'z', },
+ );
+};
- at data = (
- { Subject => '-' },
- { Subject => 'x', "CustomField-$cf_id" => 'x', },
- { Subject => 'y', "CustomField-$cf_id" => 'y', },
- { Subject => 'z', "CustomField-$cf_id" => 'z', },
-);
-%test = (
+my @tests = (
"CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
"'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
"'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
@@ -109,16 +88,37 @@ sub run_tests {
"'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
"'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
"'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
-
);
- at tickets = RT::Test->create_tickets( { Queue => $q->id }, @data);
-$total = scalar @tickets;
-{
- my $tix = RT::Tickets->new(RT->SystemUser);
- $tix->FromSQL("Queue = '$queue'");
- is($tix->Count, $total, "found $total tickets");
+run_tests(@tests);
+
+
+sub run_tests {
+ my @tests = @_;
+ while (@tests) {
+ my $query = shift @tests;
+ my %results = %{ shift @tests };
+ subtest $query => sub {
+ my $tix = RT::Tickets->new(RT->SystemUser);
+ $tix->FromSQL( "$query" );
+
+ my $error = 0;
+
+ my $count = 0;
+ $count++ foreach grep $_, values %results;
+ is($tix->Count, $count, "found correct number of ticket(s)") or $error = 1;
+
+ my $good_tickets = ($tix->Count == $count);
+ while ( my $ticket = $tix->Next ) {
+ next if $results{ $ticket->Subject };
+ diag $ticket->Subject ." ticket has been found when it's not expected";
+ $good_tickets = 0;
+ }
+ ok( $good_tickets, "all tickets are good" ) or $error = 1;
+
+ diag "Wrong SQL: ". $tix->BuildSelectQuery if $error;
+ };
+ }
}
-run_tests();
- at tickets = ();
+done_testing;
commit 266a8343511848dc464ba13dda8350f3fcd8df53
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 22:52:34 2013 -0400
Fix != conditions with an explicit column provided
Previously, the check for "or allow a row which has no value" failed to
apply if an explicit column was provided. Refactor the parenthization
slightly, and allow the possibility of no-value even in the case of
searching through particular columns.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 13afc88..40db514 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -555,8 +555,6 @@ sub _LimitCustomField {
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
# if column is defined then deal only with it
# otherwise search in Content and in LargeContent
if ( $column ) {
@@ -568,11 +566,10 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
) );
- $self->_CloseParen( $args{SUBCLAUSE} );
- $self->_CloseParen( $args{SUBCLAUSE} );
- $self->_CloseParen( $args{SUBCLAUSE} );
}
else {
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
# need special treatment for Date
if ( blessed($cf) and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
# no time specified, that means we want everything on a
@@ -708,20 +705,20 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
$self->_CloseParen( $args{SUBCLAUSE} );
+ }
- if ($negative_op) {
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- }
-
- $self->_CloseParen( $args{SUBCLAUSE} );
+ if ($negative_op and not $null_op) {
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ );
}
+ $self->_CloseParen( $args{SUBCLAUSE} );
+
}
else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index 733df15..3681c39 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -45,14 +45,22 @@ my @tests = (
"'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
"CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
"'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
"'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
"'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
"CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
"'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
"'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
"'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
"CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
"'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
commit bbb10d6bf864f7544b970d646066663417363642
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 22:56:16 2013 -0400
Tests for tickets in alternate queues, without the CF applied
A queue without the CF applied should act identically to a ticket with
no value for that CF, in all cases.
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index 3681c39..c465563 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -27,6 +27,7 @@ ok $other_q && $other_q->id, 'loaded or created queue';
subtest "Creating tickets" => sub {
RT::Test->create_tickets( { Queue => $q->id },
{ Subject => '-' },
+ { Subject => "other", Queue => $other_q->id },
{ Subject => 'x', "CustomField-$cf_id" => 'x', },
{ Subject => 'y', "CustomField-$cf_id" => 'y', },
{ Subject => 'z', "CustomField-$cf_id" => 'z', },
@@ -34,68 +35,68 @@ subtest "Creating tickets" => sub {
};
my @tests = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1 },
+ "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+
+ "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
+ "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
+ "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
+ "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
);
run_tests(@tests);
commit a031107c79d0814889ce56600a73da4571224022
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri Apr 26 22:58:20 2013 -0400
Condense IS NULL and IS NOT NULL logic
There is no hint as to why with a column specified, null tests should
act differently than without -- which the recently added tests bear out.
Simplify the logic by removing the $column check.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 40db514..6e46fcb 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -472,16 +472,16 @@ sub _LimitCustomField {
my $single_value = !blessed($cf) || $cf->SingleValue;
- if ( $null_op && !$column ) {
- # IS[ NOT] NULL without column is the same as has[ no] any CF value,
- # we can reuse our default joins for this operation
- # with column specified we have different situation
+ if ( $null_op ) {
+ # IS NULL is the same as lacks a CF value, and IS NOT NULL means
+ # has any value. We can reuse our default joins for this
+ # operation.
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
%args,
ALIAS => $ocfvalias,
- FIELD => 'id',
+ FIELD => ($column || 'id'),
OPERATOR => $op,
VALUE => $value,
);
@@ -707,7 +707,7 @@ sub _LimitCustomField {
$self->_CloseParen( $args{SUBCLAUSE} );
}
- if ($negative_op and not $null_op) {
+ if ($negative_op) {
$self->Limit(
ALIAS => $ocfvalias,
FIELD => $column || 'Content',
diff --git a/t/ticket/search_by_cf_freeform_multiple.t b/t/ticket/search_by_cf_freeform_multiple.t
index 61ec426..4772beb 100644
--- a/t/ticket/search_by_cf_freeform_multiple.t
+++ b/t/ticket/search_by_cf_freeform_multiple.t
@@ -34,23 +34,39 @@ subtest "Creating tickets" => sub {
};
my @tests = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
"CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
"'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
"'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
"'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
"CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
"'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
"'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
"'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index c465563..74fd057 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -35,33 +35,49 @@ subtest "Creating tickets" => sub {
};
my @tests = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_id}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
+ "'CF.$queue.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_id}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
+ "'CF.$queue.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
"CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
"'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
commit 4f04243ba702fb0e347eb6152574db669aea6bc8
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:17:22 2013 -0400
Factor out blessed($cf) check
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 6e46fcb..bc08581 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -406,68 +406,61 @@ sub _LimitCustomField {
return %args;
};
- if ( blessed($cf) && $cf->Type eq 'IPAddress' ) {
- my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
- if ($parsed) {
- $value = $parsed;
- }
- else {
- $RT::Logger->warn("$value is not a valid IPAddress");
+ ########## Content pre-parsing if we know things about the CF
+ if ( blessed($cf) ) {
+ if ( $cf->Type eq 'IPAddress' ) {
+ my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
+ if ($parsed) {
+ $value = $parsed;
+ } else {
+ $RT::Logger->warn("$value is not a valid IPAddress");
+ }
}
- }
-
- if ( blessed($cf) && $cf->Type eq 'IPAddressRange' ) {
- if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
-
- # convert incomplete 192.168/24 to 192.168.0.0/24 format
- $value =
- join( '.', map $_ || 0, ( split /\./, $1 )[ 0 .. 3 ] ) . "/$2"
- || $value;
- }
+ if ( $cf->Type eq 'IPAddressRange' ) {
+ if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
+ # convert incomplete 192.168/24 to 192.168.0.0/24 format
+ $value =
+ join( '.', map $_ || 0, ( split /\./, $1 )[ 0 .. 3 ] ) . "/$2"
+ || $value;
+ }
- my ( $start_ip, $end_ip ) =
- RT::ObjectCustomFieldValue->ParseIPRange($value);
- if ( $start_ip && $end_ip ) {
- if ( $op =~ /^([<>])=?$/ ) {
- my $is_less = $1 eq '<' ? 1 : 0;
- if ( $is_less ) {
+ my ( $start_ip, $end_ip ) =
+ RT::ObjectCustomFieldValue->ParseIPRange($value);
+ if ( $start_ip && $end_ip ) {
+ if ( $op =~ /^<=?$/ ) {
$value = $start_ip;
- }
- else {
+ } elsif ($op =~ /^>=?$/ ) {
$value = $end_ip;
+ } else {
+ $value = join '-', $start_ip, $end_ip;
}
- }
- else {
- $value = join '-', $start_ip, $end_ip;
+ } else {
+ $RT::Logger->warn("$value is not a valid IPAddressRange");
}
}
- else {
- $RT::Logger->warn("$value is not a valid IPAddressRange");
- }
- }
- if ( blessed($cf) && $cf->Type =~ /^Date(?:Time)?$/ ) {
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
- if ( $date->Unix ) {
-
- if (
- $cf->Type eq 'Date'
- || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i
- || ( $value !~ /midnight|\d+:\d+:\d+/i
- && $date->Time( Timezone => 'user' ) eq '00:00:00' )
- )
- {
- $value = $date->Date( Timezone => 'user' );
- }
- else {
- $value = $date->DateTime;
+ if ( $cf->Type =~ /^Date(?:Time)?$/ ) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+ if ( $date->Unix ) {
+ if (
+ $cf->Type eq 'Date'
+ # Heuristics to determine if a date, and not
+ # a datetime, was entered:
+ || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i
+ || ( $value !~ /midnight|\d+:\d+:\d+/i
+ && $date->Time( Timezone => 'user' ) eq '00:00:00' )
+ )
+ {
+ $value = $date->Date( Timezone => 'user' );
+ } else {
+ $value = $date->DateTime;
+ }
+ } else {
+ $RT::Logger->warn("$value is not a valid date string");
}
}
- else {
- $RT::Logger->warn("$value is not a valid date string");
- }
}
my $single_value = !blessed($cf) || $cf->SingleValue;
commit d77d9074c45fd4597c3caffb105f5ffebd6451b8
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:20:37 2013 -0400
Move IPAddressRange recursive call earlier
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index bc08581..ac13c7c 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -438,6 +438,50 @@ sub _LimitCustomField {
} else {
$RT::Logger->warn("$value is not a valid IPAddressRange");
}
+
+ # Recurse if they want a range comparison
+ if ( $op !~ /^[<>]=?$/ ) {
+ my ($start_ip, $end_ip) = split /-/, $value;
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ # Ideally we would limit >= 000.000.000.000 and <=
+ # 255.255.255.255 so DB optimizers could use better
+ # estimations and scan less rows, but this breaks with IPv6.
+ if ( $op !~ /NOT|!=|<>/i ) { # positive equation
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => '<=',
+ VALUE => $end_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ );
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => '>=',
+ VALUE => $start_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'LargeContent',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ } else { # negative equation
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => '>',
+ VALUE => $end_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ );
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => '<',
+ VALUE => $start_ip,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'LargeContent',
+ ENTRYAGGREGATOR => 'OR',
+ );
+ }
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ return;
+ }
}
if ( $cf->Type =~ /^Date(?:Time)?$/ ) {
@@ -488,61 +532,6 @@ sub _LimitCustomField {
) if $CFs;
$self->_CloseParen( $args{SUBCLAUSE} );
}
- elsif ( $op !~ /^[<>]=?$/ && ( blessed($cf) && $cf->Type eq 'IPAddressRange')) {
- my ($start_ip, $end_ip) = split /-/, $value;
- $self->_OpenParen( $args{SUBCLAUSE} );
- if ( $op !~ /NOT|!=|<>/i ) { # positive equation
- $self->_LimitCustomField(
- %args,
- OPERATOR => '<=',
- VALUE => $end_ip,
- CUSTOMFIELD => $cf,
- COLUMN => 'Content',
- );
- $self->_LimitCustomField(
- %args,
- OPERATOR => '>=',
- VALUE => $start_ip,
- CUSTOMFIELD => $cf,
- COLUMN => 'LargeContent',
- ENTRYAGGREGATOR => 'AND',
- );
- # as well limit borders so DB optimizers can use better
- # estimations and scan less rows
-# have to disable this tweak because of ipv6
-# $self->_CustomFieldLimit(
-# $field, '>=', '000.000.000.000', %rest,
-# SUBKEY => $rest{'SUBKEY'}. '.Content',
-# ENTRYAGGREGATOR => 'AND',
-# );
-# $self->_CustomFieldLimit(
-# $field, '<=', '255.255.255.255', %rest,
-# SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
-# ENTRYAGGREGATOR => 'AND',
-# );
- }
- else { # negative equation
- $self->_LimitCustomField(
- %args,
- OPERATOR => '>',
- VALUE => $end_ip,
- CUSTOMFIELD => $cf,
- COLUMN => 'Content',
- );
- $self->_LimitCustomField(
- %args,
- OPERATOR => '<',
- VALUE => $start_ip,
- CUSTOMFIELD => $cf,
- COLUMN => 'LargeContent',
- ENTRYAGGREGATOR => 'OR',
- );
- # TODO: as well limit borders so DB optimizers can use better
- # estimations and scan less rows, but it's harder to do
- # as we have OR aggregator
- }
- $self->_CloseParen( $args{SUBCLAUSE} );
- }
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
commit db0d1208adb8a4c779c8306f81cac603e779671d
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:23:32 2013 -0400
Move Date range check for DateTimes into a recursive call, earlier
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index ac13c7c..6127d41 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -504,6 +504,36 @@ sub _LimitCustomField {
} else {
$RT::Logger->warn("$value is not a valid date string");
}
+
+ # Recurse if day equality is being checked on a datetime
+ if ( $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'unknown', Value => $value );
+ my $daystart = $date->ISO;
+ $date->AddDay;
+ my $dayend = $date->ISO;
+
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => ">=",
+ VALUE => $daystart,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ $self->_LimitCustomField(
+ %args,
+ OPERATOR => "<",
+ VALUE => $dayend,
+ CUSTOMFIELD => $cf,
+ COLUMN => 'Content',
+ ENTRYAGGREGATOR => 'AND',
+ );
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ return;
+ }
}
}
@@ -552,39 +582,7 @@ sub _LimitCustomField {
else {
$self->_OpenParen( $args{SUBCLAUSE} );
$self->_OpenParen( $args{SUBCLAUSE} );
- # need special treatment for Date
- if ( blessed($cf) and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) {
- # no time specified, that means we want everything on a
- # particular day. in the database, we need to check for >
- # and < the edges of that day.
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
- my $daystart = $date->ISO;
- $date->AddDay;
- my $dayend = $date->ISO;
-
- $self->_OpenParen( $args{SUBCLAUSE} );
-
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => ">=",
- VALUE => $daystart,
- );
-
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => "<",
- VALUE => $dayend,
- ENTRYAGGREGATOR => 'AND',
- );
-
- $self->_CloseParen( $args{SUBCLAUSE} );
- }
- elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
+ if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
%args,
commit 6b1e38d92be093acb36b91a931fcc33a44d8e34b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:24:35 2013 -0400
Remove a mis-placed comment
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 6127d41..d73186d 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -379,11 +379,6 @@ sub _LimitCustomField {
$args{SUBCLAUSE} ||= "cf-$cfkey";
-# If we're trying to find custom fields that don't match something, we
-# want tickets where the custom field has no value at all. Note that
-# we explicitly don't include the "IS NULL" case, since we would
-# otherwise end up with a redundant clause.
-
my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
my $null_op = ( 'is not' eq lc($op) || 'is' eq lc($op) );
@@ -392,7 +387,7 @@ sub _LimitCustomField {
my %args = @_;
return %args unless $args{'FIELD'} eq 'LargeContent';
-
+
my $op = $args{'OPERATOR'};
if ( $op eq '=' ) {
$args{'OPERATOR'} = 'MATCHES';
commit d807a56d84dde2c3f6930e3a65d0c9be077d186a
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:31:26 2013 -0400
Short-circuit the NULL tests
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index d73186d..8d61f27 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -380,7 +380,6 @@ sub _LimitCustomField {
$args{SUBCLAUSE} ||= "cf-$cfkey";
my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
- my $null_op = ( 'is not' eq lc($op) || 'is' eq lc($op) );
my $fix_op = sub {
return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle';
@@ -534,10 +533,9 @@ sub _LimitCustomField {
my $single_value = !blessed($cf) || $cf->SingleValue;
- if ( $null_op ) {
- # IS NULL is the same as lacks a CF value, and IS NOT NULL means
- # has any value. We can reuse our default joins for this
- # operation.
+ ########## Limits
+ # IS NULL and IS NOT NULL checks
+ if ( $op =~ /^IS( NOT)?$/i ) {
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
@@ -547,6 +545,7 @@ sub _LimitCustomField {
OPERATOR => $op,
VALUE => $value,
);
+ # See below for an explanation of this limit
$self->Limit(
ALIAS => $CFs,
FIELD => 'Name',
@@ -556,8 +555,9 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
$self->_CloseParen( $args{SUBCLAUSE} );
+ return;
}
- elsif ( !$negative_op || $single_value ) {
+ if ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
commit 4ddd202b48ec75ec6f79b4f89d1301eef80d5750
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:34:06 2013 -0400
Collect helper variables closer to where they are used
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 8d61f27..ec36604 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -379,7 +379,6 @@ sub _LimitCustomField {
$args{SUBCLAUSE} ||= "cf-$cfkey";
- my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
my $fix_op = sub {
return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle';
@@ -531,8 +530,6 @@ sub _LimitCustomField {
}
}
- my $single_value = !blessed($cf) || $cf->SingleValue;
-
########## Limits
# IS NULL and IS NOT NULL checks
if ( $op =~ /^IS( NOT)?$/i ) {
@@ -557,6 +554,10 @@ sub _LimitCustomField {
$self->_CloseParen( $args{SUBCLAUSE} );
return;
}
+
+ my $single_value = !blessed($cf) || $cf->SingleValue;
+ my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
+
if ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
commit f160a44c0f0b16178529dc612d471b665c6eafcb
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:00:11 2013 -0400
Apply Demorgan's to make the less common case clearer
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index ec36604..8e6adc5 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -558,7 +558,45 @@ sub _LimitCustomField {
my $single_value = !blessed($cf) || $cf->SingleValue;
my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
- if ( !$negative_op || $single_value ) {
+ # A negative limit on a multi-value CF means _none_ of the values
+ # are the given value
+ if ( $negative_op and not $single_value ) {
+ $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
+
+ # Reverse the limit we apply to the join, and check IS NULL
+ $op =~ s/!|NOT\s+//i;
+
+ # if column is defined then deal only with it
+ # otherwise search in Content and in LargeContent
+ if ( $column ) {
+ $self->Limit( $fix_op->(
+ LEFTJOIN => $ocfvalias,
+ ALIAS => $ocfvalias,
+ FIELD => $column,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ ) );
+ }
+ else {
+ $self->Limit(
+ LEFTJOIN => $ocfvalias,
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+ }
+ $self->Limit(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => 'id',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ );
+ } else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
@@ -696,43 +734,6 @@ sub _LimitCustomField {
$self->_CloseParen( $args{SUBCLAUSE} );
}
- else {
- $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
-
- # reverse operation
- $op =~ s/!|NOT\s+//i;
-
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
- if ( $column ) {
- $self->Limit( $fix_op->(
- LEFTJOIN => $ocfvalias,
- ALIAS => $ocfvalias,
- FIELD => $column,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- ) );
- }
- else {
- $self->Limit(
- LEFTJOIN => $ocfvalias,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'id',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- );
- }
}
=head2 Limit PARAMHASH
commit f1da2423d4f8b43812733fd697701b730c70411a
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:02:07 2013 -0400
Fold two parallel statements into one, removing a misleading and incorrect comment
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 8e6adc5..0b8d85a 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -567,28 +567,14 @@ sub _LimitCustomField {
# Reverse the limit we apply to the join, and check IS NULL
$op =~ s/!|NOT\s+//i;
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
- if ( $column ) {
- $self->Limit( $fix_op->(
- LEFTJOIN => $ocfvalias,
- ALIAS => $ocfvalias,
- FIELD => $column,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- ) );
- }
- else {
- $self->Limit(
- LEFTJOIN => $ocfvalias,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
+ $self->Limit( $fix_op->(
+ LEFTJOIN => $ocfvalias,
+ ALIAS => $ocfvalias,
+ FIELD => ($column || 'Content'),
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ ) );
$self->Limit(
%args,
ALIAS => $ocfvalias,
commit 2e8894ebf4e0c5da7de0e3caa4a1150968a145c1
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 14:36:52 2013 -0400
Factor out the common join to the OCFV table
The two calls to _CustomFieldJoin can be moved out and into one call, as
long as care is taken to preserve the logic as to when new indexes are
necessary. Specifically, new indexes are never necessary with
single-value CFs, and are used if the operator is a negative operator OR
is not /^[<>]=?$/ -- that is, if it is not a relative comparitor.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 0b8d85a..b78551f 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -558,12 +558,13 @@ sub _LimitCustomField {
my $single_value = !blessed($cf) || $cf->SingleValue;
my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
+ $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++
+ if not $single_value and $op =~ /^(!?=|(NOT )?LIKE)$/i;
+ my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
+
# A negative limit on a multi-value CF means _none_ of the values
# are the given value
if ( $negative_op and not $single_value ) {
- $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
-
# Reverse the limit we apply to the join, and check IS NULL
$op =~ s/!|NOT\s+//i;
@@ -583,8 +584,6 @@ sub _LimitCustomField {
VALUE => 'NULL',
);
} else {
- $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if not $single_value and not $op =~ /^[<>]=?$/;
- my ($ocfvalias, $CFs) = $self->_CustomFieldJoin( $cfkey, $cf );
$self->_OpenParen( $args{SUBCLAUSE} );
# if column is defined then deal only with it
commit 4b85902e330ea22604b29f8fb51ebab3c95c5f08
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:12:41 2013 -0400
Short-circuit from negative queries on multiple-value CFs
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index b78551f..7f3de96 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -583,66 +583,27 @@ sub _LimitCustomField {
OPERATOR => 'IS',
VALUE => 'NULL',
);
- } else {
+ return;
+ }
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ # if column is defined then deal only with it
+ # otherwise search in Content and in LargeContent
+ if ( $column ) {
+ $self->Limit( $fix_op->(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => $column,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ ) );
+ }
+ else {
$self->_OpenParen( $args{SUBCLAUSE} );
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
- if ( $column ) {
- $self->Limit( $fix_op->(
- %args,
- ALIAS => $ocfvalias,
- FIELD => $column,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- ) );
- }
- else {
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
- if ( length( Encode::encode_utf8($value) ) < 256 ) {
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
- else {
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->_CloseParen( $args{SUBCLAUSE} );
- $self->Limit( $fix_op->(
- ALIAS => $ocfvalias,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- SUBCLAUSE => $args{SUBCLAUSE},
- CASESENSITIVE => 0,
- ) );
- }
- }
- else {
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
+ if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
%args,
ALIAS => $ocfvalias,
@@ -651,24 +612,24 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
);
-
- $self->_OpenParen( $args{SUBCLAUSE} );
+ }
+ else {
$self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
$self->_CloseParen( $args{SUBCLAUSE} );
$self->Limit( $fix_op->(
@@ -680,45 +641,83 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
CASESENSITIVE => 0,
) );
- $self->_CloseParen( $args{SUBCLAUSE} );
}
- $self->_CloseParen( $args{SUBCLAUSE} );
+ }
+ else {
+ $self->Limit(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
- # XXX: if we join via CustomFields table then
- # because of order of left joins we get NULLs in
- # CF table and then get nulls for those records
- # in OCFVs table what result in wrong results
- # as decifer method now tries to load a CF then
- # we fall into this situation only when there
- # are more than one CF with the name in the DB.
- # the same thing applies to order by call.
- # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
- # we want treat IS NULL as (not applies or has
- # no value)
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- OPERATOR => 'IS NOT',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'AND',
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => '=',
+ VALUE => '',
SUBCLAUSE => $args{SUBCLAUSE},
- ) if $CFs;
- $self->_CloseParen( $args{SUBCLAUSE} );
- }
-
- if ($negative_op) {
+ ENTRYAGGREGATOR => 'OR'
+ );
$self->Limit(
ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
+ FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
+ ENTRYAGGREGATOR => 'OR'
);
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->Limit( $fix_op->(
+ ALIAS => $ocfvalias,
+ FIELD => 'LargeContent',
+ OPERATOR => $op,
+ VALUE => $value,
+ ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ CASESENSITIVE => 0,
+ ) );
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
$self->_CloseParen( $args{SUBCLAUSE} );
+ # XXX: if we join via CustomFields table then
+ # because of order of left joins we get NULLs in
+ # CF table and then get nulls for those records
+ # in OCFVs table what result in wrong results
+ # as decifer method now tries to load a CF then
+ # we fall into this situation only when there
+ # are more than one CF with the name in the DB.
+ # the same thing applies to order by call.
+ # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
+ # we want treat IS NULL as (not applies or has
+ # no value)
+ $self->Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ ) if $CFs;
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ }
+
+ if ($negative_op) {
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ );
}
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
=head2 Limit PARAMHASH
commit 21a3af995fa2d4d5d3b04c94a4f36b26a07d6d5a
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:19:08 2013 -0400
Push negative-op into we-have-a-column if/else to increase reading locality
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 7f3de96..9d020d7 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -586,10 +586,9 @@ sub _LimitCustomField {
return;
}
- $self->_OpenParen( $args{SUBCLAUSE} );
- # if column is defined then deal only with it
- # otherwise search in Content and in LargeContent
+ # If column is defined, then we just search it that, with no magic
if ( $column ) {
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit( $fix_op->(
%args,
ALIAS => $ocfvalias,
@@ -598,10 +597,20 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
) );
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column,
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ ) if $negative_op;
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
else {
$self->_OpenParen( $args{SUBCLAUSE} );
$self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
@@ -705,19 +714,19 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
$self->_CloseParen( $args{SUBCLAUSE} );
+ if ($negative_op) {
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ );
+ }
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
- if ($negative_op) {
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- }
- $self->_CloseParen( $args{SUBCLAUSE} );
}
=head2 Limit PARAMHASH
commit bee02d61647112a8707b3497c9b797b5057598a8
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:20:48 2013 -0400
Short-circuit from column-specific limit
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 9d020d7..f582870 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -606,53 +606,13 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
) if $negative_op;
$self->_CloseParen( $args{SUBCLAUSE} );
+ return;
}
- else {
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
- if ( length( Encode::encode_utf8($value) ) < 256 ) {
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
- else {
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->_CloseParen( $args{SUBCLAUSE} );
- $self->Limit( $fix_op->(
- ALIAS => $ocfvalias,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- SUBCLAUSE => $args{SUBCLAUSE},
- CASESENSITIVE => 0,
- ) );
- }
- }
- else {
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
+ if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->Limit(
%args,
ALIAS => $ocfvalias,
@@ -661,24 +621,24 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
);
-
- $self->_OpenParen( $args{SUBCLAUSE} );
+ }
+ else {
$self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
$self->_CloseParen( $args{SUBCLAUSE} );
$self->Limit( $fix_op->(
@@ -690,43 +650,81 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
CASESENSITIVE => 0,
) );
- $self->_CloseParen( $args{SUBCLAUSE} );
}
- $self->_CloseParen( $args{SUBCLAUSE} );
+ }
+ else {
+ $self->Limit(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
- # XXX: if we join via CustomFields table then
- # because of order of left joins we get NULLs in
- # CF table and then get nulls for those records
- # in OCFVs table what result in wrong results
- # as decifer method now tries to load a CF then
- # we fall into this situation only when there
- # are more than one CF with the name in the DB.
- # the same thing applies to order by call.
- # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
- # we want treat IS NULL as (not applies or has
- # no value)
+ $self->_OpenParen( $args{SUBCLAUSE} );
+ $self->_OpenParen( $args{SUBCLAUSE} );
$self->Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- OPERATOR => 'IS NOT',
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => '=',
+ VALUE => '',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ ENTRYAGGREGATOR => 'OR'
+ );
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
VALUE => 'NULL',
- ENTRYAGGREGATOR => 'AND',
SUBCLAUSE => $args{SUBCLAUSE},
- ) if $CFs;
+ ENTRYAGGREGATOR => 'OR'
+ );
$self->_CloseParen( $args{SUBCLAUSE} );
- if ($negative_op) {
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- }
+ $self->Limit( $fix_op->(
+ ALIAS => $ocfvalias,
+ FIELD => 'LargeContent',
+ OPERATOR => $op,
+ VALUE => $value,
+ ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ CASESENSITIVE => 0,
+ ) );
$self->_CloseParen( $args{SUBCLAUSE} );
}
-
+ $self->_CloseParen( $args{SUBCLAUSE} );
+
+ # XXX: if we join via CustomFields table then
+ # because of order of left joins we get NULLs in
+ # CF table and then get nulls for those records
+ # in OCFVs table what result in wrong results
+ # as decifer method now tries to load a CF then
+ # we fall into this situation only when there
+ # are more than one CF with the name in the DB.
+ # the same thing applies to order by call.
+ # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if
+ # we want treat IS NULL as (not applies or has
+ # no value)
+ $self->Limit(
+ ALIAS => $CFs,
+ FIELD => 'Name',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'AND',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ ) if $CFs;
+ $self->_CloseParen( $args{SUBCLAUSE} );
+ if ($negative_op) {
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ );
+ }
+ $self->_CloseParen( $args{SUBCLAUSE} );
}
=head2 Limit PARAMHASH
commit 0011c4ec6066bf2fc1709cb17d1c43211b8db924
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue Apr 30 15:29:09 2013 -0400
Merge two parallel content search branches
The only occasion upon which Content should not be searched is if the
search is an equality search and the value we are matching is certainly
larger than would fit into Content. Similarly, skip looking at
LargeContent iff the value is short and it is an equality.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index f582870..c00b037 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -608,51 +608,13 @@ sub _LimitCustomField {
$self->_CloseParen( $args{SUBCLAUSE} );
return;
}
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
- if ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
- if ( length( Encode::encode_utf8($value) ) < 256 ) {
- $self->Limit(
- %args,
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- }
- else {
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- $self->_CloseParen( $args{SUBCLAUSE} );
- $self->Limit( $fix_op->(
- ALIAS => $ocfvalias,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- SUBCLAUSE => $args{SUBCLAUSE},
- CASESENSITIVE => 0,
- ) );
- }
- }
- else {
+
+ $self->_OpenParen( $args{SUBCLAUSE} ); # For negative_op "OR it is null" clause
+ $self->_OpenParen( $args{SUBCLAUSE} ); # NAME IS NOT NULL clause
+
+ my $value_is_long = (length( Encode::encode_utf8($value)) > 255) ? 1 : 0;
+ $self->_OpenParen( $args{SUBCLAUSE} ); # Check Content / LargeContent
+ unless ($value_is_long and $op =~ /^(=|!=|<>)$/) {
$self->Limit(
%args,
ALIAS => $ocfvalias,
@@ -661,26 +623,27 @@ sub _LimitCustomField {
VALUE => $value,
CASESENSITIVE => 0,
);
-
- $self->_OpenParen( $args{SUBCLAUSE} );
- $self->_OpenParen( $args{SUBCLAUSE} );
+ }
+ unless (!$value_is_long and $op =~ /^(=|!=|<>)$/) {
+ $self->_OpenParen( $args{SUBCLAUSE} ); # LargeContent check
+ $self->_OpenParen( $args{SUBCLAUSE} ); # Content is null?
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
$self->Limit(
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
SUBCLAUSE => $args{SUBCLAUSE},
- ENTRYAGGREGATOR => 'OR'
);
- $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->_CloseParen( $args{SUBCLAUSE} ); # Content is null?
$self->Limit( $fix_op->(
ALIAS => $ocfvalias,
FIELD => 'LargeContent',
@@ -690,9 +653,10 @@ sub _LimitCustomField {
SUBCLAUSE => $args{SUBCLAUSE},
CASESENSITIVE => 0,
) );
- $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->_CloseParen( $args{SUBCLAUSE} ); # LargeContent check
}
- $self->_CloseParen( $args{SUBCLAUSE} );
+
+ $self->_CloseParen( $args{SUBCLAUSE} ); # Check Content/LargeContent
# XXX: if we join via CustomFields table then
# because of order of left joins we get NULLs in
@@ -713,18 +677,19 @@ sub _LimitCustomField {
ENTRYAGGREGATOR => 'AND',
SUBCLAUSE => $args{SUBCLAUSE},
) if $CFs;
- $self->_CloseParen( $args{SUBCLAUSE} );
- if ($negative_op) {
- $self->Limit(
- ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR',
- SUBCLAUSE => $args{SUBCLAUSE},
- );
- }
- $self->_CloseParen( $args{SUBCLAUSE} );
+ $self->_CloseParen( $args{SUBCLAUSE} ); # Name IS NOT NULL clause
+
+ # If we were looking for != or NOT LIKE, we need to include the
+ # possibility that the row had no value.
+ $self->Limit(
+ ALIAS => $ocfvalias,
+ FIELD => $column || 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $args{SUBCLAUSE},
+ ) if $negative_op;
+ $self->_CloseParen( $args{SUBCLAUSE} ); # negative_op clause
}
=head2 Limit PARAMHASH
commit b1d6b927858961dad4bd8d2703c7fc8dd80bcbf4
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Wed May 1 22:59:59 2013 -0400
Add and test LongContent values; some tests fail
diff --git a/t/ticket/search_by_cf_freeform_multiple.t b/t/ticket/search_by_cf_freeform_multiple.t
index 4772beb..a4a1a40 100644
--- a/t/ticket/search_by_cf_freeform_multiple.t
+++ b/t/ticket/search_by_cf_freeform_multiple.t
@@ -21,6 +21,7 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
$cf_id = $cf->id;
}
+my $ylong = "y" x 300;
subtest "Creating tickets" => sub {
RT::Test->create_tickets( { Queue => $q->id },
{ Subject => '-' },
@@ -30,80 +31,88 @@ subtest "Creating tickets" => sub {
{ Subject => 'xy', "CustomField-$cf_id" => [ 'x', 'y' ], },
{ Subject => 'xz', "CustomField-$cf_id" => [ 'x', 'z' ], },
{ Subject => 'yz', "CustomField-$cf_id" => [ 'y', 'z' ], },
+ { Subject => 'x_ylong', "CustomField-$cf_id" => [ 'x', $ylong ], },
+ { Subject => 'ylong', "CustomField-$cf_id" => $ylong, },
);
};
my @tests = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "CF.{$cf_id}.Content IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0 },
-
- "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1 },
+ "CF.{$cf_id} IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id} = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id} != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 1, y => 1, z => 0, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 1, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, x => 0, y => 0, z => 1, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
);
run_tests(@tests);
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index 74fd057..a5a5a94 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -24,6 +24,7 @@ my ($cf_name, $cf_id, $cf) = ("Test", 0, undef);
my $other_q = RT::Test->load_or_create_queue( Name => 'Other' );
ok $other_q && $other_q->id, 'loaded or created queue';
+my $ylong = 'y' x 300;
subtest "Creating tickets" => sub {
RT::Test->create_tickets( { Queue => $q->id },
{ Subject => '-' },
@@ -31,88 +32,95 @@ subtest "Creating tickets" => sub {
{ Subject => 'x', "CustomField-$cf_id" => 'x', },
{ Subject => 'y', "CustomField-$cf_id" => 'y', },
{ Subject => 'z', "CustomField-$cf_id" => 'z', },
+ { Subject => 'ylong', "CustomField-$cf_id" => $ylong, },
);
};
my @tests = (
- "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "CF.{$cf_id}.Content IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
- "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
- "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
- "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0 },
-
- "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
- "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1 },
+ "CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.Content IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.LargeContent IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' IS NULL" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.Content IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.LargeContent IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' IS NOT NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.Content = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id} = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.Content != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id} != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 1, y => 1, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' = 'y'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} != 'x' AND CF.{$cf_id} != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.{$cf_name}' != 'x' AND 'CF.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' != 'x' AND 'CF.$queue.{$cf_id}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' != 'x' AND 'CF.$queue.{$cf_name}' != 'y'" => { '-' => 1, other => 1, x => 0, y => 0, z => 1, ylong => 1 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NULL" => { '-' => 1, other => 1, x => 1, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' AND CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.{$cf_name}' = 'x' AND 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_id}' = 'x' AND 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+ "'CF.$queue.{$cf_name}' = 'x' AND 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
+
+ "CF.{$cf_id} = 'x' OR CF.{$cf_id} IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.{$cf_name}' = 'x' OR 'CF.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_id}' = 'x' OR 'CF.$queue.{$cf_id}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
+ "'CF.$queue.{$cf_name}' = 'x' OR 'CF.$queue.{$cf_name}' IS NOT NULL" => { '-' => 0, other => 0, x => 1, y => 1, z => 1, ylong => 1 },
);
run_tests(@tests);
commit ac13502595d2009888abc577f3ccbfce157a61b9
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Thu May 2 00:00:19 2013 -0400
When a value overflows into LargeContent, ensure that Content is null
The ->AddCustomFieldValue codepath attempts to ensure that empty values
for Content or LargeContent are never passed down the stack for
insertion. However, this is foiled if a >255-character value is passed
through for Value, is only caught at the ObjectCustomFieldValue->Create
level -- which sets Content to the empty string when upgrading the
Content to LargeContent, rather than undef.
Ensure that NULLs are properly stored in the Content column when
LargeContent is used, by setting Content to undef in such cases. There
may also exist historical OCFVs which contain '' instead of NULL; update
them accordingly.
diff --git a/etc/upgrade/4.1.10/schema.Oracle b/etc/upgrade/4.1.10/schema.Oracle
new file mode 100644
index 0000000..93f036f
--- /dev/null
+++ b/etc/upgrade/4.1.10/schema.Oracle
@@ -0,0 +1 @@
+-- No update is necessary, given that '' == NULL on Oracle
diff --git a/etc/upgrade/4.1.10/schema.Pg b/etc/upgrade/4.1.10/schema.Pg
new file mode 100644
index 0000000..af862b6
--- /dev/null
+++ b/etc/upgrade/4.1.10/schema.Pg
@@ -0,0 +1 @@
+UPDATE ObjectCustomFieldValues SET Content = NULL WHERE LargeContent IS NOT NULL AND Content = '';
diff --git a/etc/upgrade/4.1.10/schema.mysql b/etc/upgrade/4.1.10/schema.mysql
new file mode 100644
index 0000000..af862b6
--- /dev/null
+++ b/etc/upgrade/4.1.10/schema.mysql
@@ -0,0 +1 @@
+UPDATE ObjectCustomFieldValues SET Content = NULL WHERE LargeContent IS NOT NULL AND Content = '';
diff --git a/lib/RT/ObjectCustomFieldValue.pm b/lib/RT/ObjectCustomFieldValue.pm
index bb360e3..1e8de8b 100644
--- a/lib/RT/ObjectCustomFieldValue.pm
+++ b/lib/RT/ObjectCustomFieldValue.pm
@@ -119,7 +119,7 @@ sub Create {
}
else {
$args{'LargeContent'} = $args{'Content'};
- $args{'Content'} = '';
+ $args{'Content'} = undef;
$args{'ContentType'} ||= 'text/plain';
}
}
commit eca2558b86a20ca1b09e3218a6d097719c3bed87
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri May 10 02:05:36 2013 -0400
Improve != searching in combination with LargeContent
When attempting to perform a != on a single-value CF, with a value
longer than 255 characters, the search is limited by:
((Content = '' OR Content IS NULL) AND LargeContent != '...')
The limitation «Content != '...' OR » was intentionally omitted, based
on the proposition that a value longer than 255 characters cannot relate
to the Content field. However, _any_ value in the Content field
necessarily implies a custom field value which does not match the long
string (because it is too short), meaning that the ticket should be
contained in the resultset.
Conflicts:
lib/RT/SearchBuilder.pm
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index c00b037..974ec12 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -614,7 +614,20 @@ sub _LimitCustomField {
my $value_is_long = (length( Encode::encode_utf8($value)) > 255) ? 1 : 0;
$self->_OpenParen( $args{SUBCLAUSE} ); # Check Content / LargeContent
- unless ($value_is_long and $op =~ /^(=|!=|<>)$/) {
+ if ($value_is_long and $op eq "=") {
+ # Doesn't matter what Content contains, as it cannot match the
+ # too-long value; we just look in LargeContent, below.
+ } elsif ($value_is_long and $op =~ /^(!=|<>)$/) {
+ # If Content is non-null, that's a valid way to _not_ contain the too-long value.
+ $self->Limit(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => 'Content',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+ } else {
+ # Otherwise, go looking at the Content
$self->Limit(
%args,
ALIAS => $ocfvalias,
@@ -624,7 +637,22 @@ sub _LimitCustomField {
CASESENSITIVE => 0,
);
}
- unless (!$value_is_long and $op =~ /^(=|!=|<>)$/) {
+
+ if (!$value_is_long and $op eq "=") {
+ # Doesn't matter what LargeContent contains, as it cannot match
+ # the short value.
+ } elsif (!$value_is_long and $op =~ /^(!=|<>)$/) {
+ # If LargeContent is non-null, that's a valid way to _not_
+ # contain the too-short value.
+ $self->Limit(
+ %args,
+ ALIAS => $ocfvalias,
+ FIELD => 'LargeContent',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ ENTRYAGGREGATOR => 'OR',
+ );
+ } else {
$self->_OpenParen( $args{SUBCLAUSE} ); # LargeContent check
$self->_OpenParen( $args{SUBCLAUSE} ); # Content is null?
$self->Limit(
commit 8ee882067482416cc63a5ac9131a3de757a96563
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Thu May 2 01:40:02 2013 -0400
Improve != and NOT LIKE searching in combination with LargeContent
When attempting to perform a NOT LIKE (or != with a long value) on a
single-value CF, the search is limited by:
Content NOT LIKE '%value%' OR
((Content = '' OR Content IS NULL) AND LargeContent NOT LIKE '%value%')
However, it must also be open to the possibility that the ticket did not
contain a value for that CF, which is a valid method of having
not-that-value. It thus also added the possibility for lack-of-value by
further limiting by:
OR Content IS NULL
Unfortunately, as Content is nearly definitionally NULL for rows with
LargeContent, this results in a false positives where LargeContent
matches '%value%' and thus the ticket should not match.
The limitation of "or the ticket could fail to have a value for the CF"
is better stated by checking that OCFV.id IS NULL, rather than
OCFV.Content IS NULL; switch to this to avoid the false positive. Note
that $column cannot be defined at this point, as it has previously been
checked for truth, and return'd already in such cases, so the removing
the reference to $column is intentional.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 974ec12..2f93e91 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -711,7 +711,7 @@ sub _LimitCustomField {
# possibility that the row had no value.
$self->Limit(
ALIAS => $ocfvalias,
- FIELD => $column || 'Content',
+ FIELD => 'id',
OPERATOR => 'IS',
VALUE => 'NULL',
ENTRYAGGREGATOR => 'OR',
diff --git a/t/ticket/search_by_cf_freeform_single.t b/t/ticket/search_by_cf_freeform_single.t
index a5a5a94..0463412 100644
--- a/t/ticket/search_by_cf_freeform_single.t
+++ b/t/ticket/search_by_cf_freeform_single.t
@@ -61,6 +61,9 @@ my @tests = (
"CF.{$cf_id} = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
"CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
"CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
+ "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 1 },
"'CF.{$cf_name}' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
"'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, other => 0, x => 1, y => 0, z => 0, ylong => 0 },
"'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, other => 0, x => 0, y => 0, z => 0, ylong => 0 },
@@ -77,6 +80,9 @@ my @tests = (
"CF.{$cf_id} != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
"CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
"CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
+ "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 0 },
"'CF.{$cf_name}' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
"'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, other => 1, x => 0, y => 1, z => 1, ylong => 1 },
"'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, other => 1, x => 1, y => 1, z => 1, ylong => 1 },
commit 26bbd612ef3e5250418041db04d59af1c5223ead
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Thu May 2 04:14:01 2013 -0400
Improve !=, and note the limitations of NOT LIKE, with multi-value LargeContent
When attempting to perform a != or NOT LIKE search on a multi-value CF,
with a value longer than 255 characters, the search limits the LEFT JOIN
based on the opposite condition. However, it assumed the Content column
unless explicitly instructed; this means that, when checking against a
long string, it would return false positives.
For the case of !=, we can determine which of Content or LargeContent to
examine ahead of time. Unfortunately, for NOT LIKE, we would be forced
to use a more complex limit to search both:
Content LIKE '%value%' OR
((Content = '' OR Content IS NULL) AND LargeContent LIKE '%value%')
...and such a limit cannot be added to a LEFTJOIN clause using the
current DBIx::SearchBuilder.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 2f93e91..294836e 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -557,6 +557,7 @@ sub _LimitCustomField {
my $single_value = !blessed($cf) || $cf->SingleValue;
my $negative_op = ($op eq '!=' || $op =~ /\bNOT\b/i);
+ my $value_is_long = (length( Encode::encode_utf8($value)) > 255) ? 1 : 0;
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++
if not $single_value and $op =~ /^(!?=|(NOT )?LIKE)$/i;
@@ -568,10 +569,17 @@ sub _LimitCustomField {
# Reverse the limit we apply to the join, and check IS NULL
$op =~ s/!|NOT\s+//i;
+ # Ideally we would check both Content and LargeContent here, as
+ # the positive searches do below -- however, we cannot place
+ # complex limits inside LEFTJOINs due to searchbuilder
+ # limitations. Guessing which to check based on the value's
+ # string length is sufficient for !=, but sadly insufficient for
+ # NOT LIKE checks, giving false positives.
+ $column ||= $value_is_long ? 'LargeContent' : 'Content';
$self->Limit( $fix_op->(
LEFTJOIN => $ocfvalias,
ALIAS => $ocfvalias,
- FIELD => ($column || 'Content'),
+ FIELD => $column,
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
@@ -612,7 +620,6 @@ sub _LimitCustomField {
$self->_OpenParen( $args{SUBCLAUSE} ); # For negative_op "OR it is null" clause
$self->_OpenParen( $args{SUBCLAUSE} ); # NAME IS NOT NULL clause
- my $value_is_long = (length( Encode::encode_utf8($value)) > 255) ? 1 : 0;
$self->_OpenParen( $args{SUBCLAUSE} ); # Check Content / LargeContent
if ($value_is_long and $op eq "=") {
# Doesn't matter what Content contains, as it cannot match the
diff --git a/t/ticket/search_by_cf_freeform_multiple.t b/t/ticket/search_by_cf_freeform_multiple.t
index a4a1a40..1324abd 100644
--- a/t/ticket/search_by_cf_freeform_multiple.t
+++ b/t/ticket/search_by_cf_freeform_multiple.t
@@ -61,6 +61,9 @@ my @tests = (
"CF.{$cf_id} = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
"CF.{$cf_id}.Content = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
"CF.{$cf_id}.LargeContent = '$ylong'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id} LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.Content LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.LargeContent LIKE 'yyy'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 1, ylong => 1 },
"'CF.{$cf_name}' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
"'CF.{$cf_name}.Content' = 'x'" => { '-' => 0, x => 1, y => 0, z => 0, xy => 1, xz => 1, yz => 0, x_ylong => 1, ylong => 0 },
"'CF.{$cf_name}.LargeContent' = 'x'" => { '-' => 0, x => 0, y => 0, z => 0, xy => 0, xz => 0, yz => 0, x_ylong => 0, ylong => 0 },
@@ -73,6 +76,9 @@ my @tests = (
"CF.{$cf_id} != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
"CF.{$cf_id}.Content != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
"CF.{$cf_id}.LargeContent != '$ylong'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+"TODO: CF.{$cf_id} NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
+ "CF.{$cf_id}.Content NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
+ "CF.{$cf_id}.LargeContent NOT LIKE 'yyy'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 0, ylong => 0 },
"'CF.{$cf_name}' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
"'CF.{$cf_name}.Content' != 'x'" => { '-' => 1, x => 0, y => 1, z => 1, xy => 0, xz => 0, yz => 1, x_ylong => 0, ylong => 1 },
"'CF.{$cf_name}.LargeContent' != 'x'" => { '-' => 1, x => 1, y => 1, z => 1, xy => 1, xz => 1, yz => 1, x_ylong => 1, ylong => 1 },
@@ -122,6 +128,7 @@ sub run_tests {
while (@tests) {
my $query = shift @tests;
my %results = %{ shift @tests };
+ local $TODO = "Not implemented correctly" if $query =~ s/^TODO:\s*//;
subtest $query => sub {
my $tix = RT::Tickets->new(RT->SystemUser);
$tix->FromSQL( "$query" );
commit fe2640c7b5e72e98be515b7af9a16e4782022574
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri May 10 00:37:13 2013 -0400
Save possibly multiple calls to $cf->Type
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 294836e..89ad069 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -401,16 +401,15 @@ sub _LimitCustomField {
########## Content pre-parsing if we know things about the CF
if ( blessed($cf) ) {
- if ( $cf->Type eq 'IPAddress' ) {
+ my $type = $cf->Type;
+ if ( $type eq 'IPAddress' ) {
my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
if ($parsed) {
$value = $parsed;
} else {
$RT::Logger->warn("$value is not a valid IPAddress");
}
- }
-
- if ( $cf->Type eq 'IPAddressRange' ) {
+ } elsif ( $type eq 'IPAddressRange' ) {
if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
# convert incomplete 192.168/24 to 192.168.0.0/24 format
$value =
@@ -475,9 +474,7 @@ sub _LimitCustomField {
$self->_CloseParen( $args{SUBCLAUSE} );
return;
}
- }
-
- if ( $cf->Type =~ /^Date(?:Time)?$/ ) {
+ } elsif ( $type =~ /^Date(?:Time)?$/ ) {
my $date = RT::Date->new( $self->CurrentUser );
$date->Set( Format => 'unknown', Value => $value );
if ( $date->Unix ) {
commit cb1a739cfb79b125a441a6dbe2a862049218b6d3
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Fri May 10 01:11:18 2013 -0400
Ensure that values are not pre-parsed more than once
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 89ad069..0ef3d5d 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -355,6 +355,7 @@ sub _LimitCustomField {
CUSTOMFIELD => undef,
OPERATOR => '=',
KEY => undef,
+ PREPARSE => 1,
@_ );
my $op = delete $args{OPERATOR};
@@ -400,7 +401,7 @@ sub _LimitCustomField {
};
########## Content pre-parsing if we know things about the CF
- if ( blessed($cf) ) {
+ if ( blessed($cf) and delete $args{PREPARSE} ) {
my $type = $cf->Type;
if ( $type eq 'IPAddress' ) {
my $parsed = RT::ObjectCustomFieldValue->ParseIP($value);
@@ -445,6 +446,7 @@ sub _LimitCustomField {
VALUE => $end_ip,
CUSTOMFIELD => $cf,
COLUMN => 'Content',
+ PREPARSE => 0,
);
$self->_LimitCustomField(
%args,
@@ -453,6 +455,7 @@ sub _LimitCustomField {
CUSTOMFIELD => $cf,
COLUMN => 'LargeContent',
ENTRYAGGREGATOR => 'AND',
+ PREPARSE => 0,
);
} else { # negative equation
$self->_LimitCustomField(
@@ -461,6 +464,7 @@ sub _LimitCustomField {
VALUE => $end_ip,
CUSTOMFIELD => $cf,
COLUMN => 'Content',
+ PREPARSE => 0,
);
$self->_LimitCustomField(
%args,
@@ -469,6 +473,7 @@ sub _LimitCustomField {
CUSTOMFIELD => $cf,
COLUMN => 'LargeContent',
ENTRYAGGREGATOR => 'OR',
+ PREPARSE => 0,
);
}
$self->_CloseParen( $args{SUBCLAUSE} );
@@ -511,6 +516,7 @@ sub _LimitCustomField {
CUSTOMFIELD => $cf,
COLUMN => 'Content',
ENTRYAGGREGATOR => 'AND',
+ PREPARSE => 0,
);
$self->_LimitCustomField(
@@ -520,6 +526,7 @@ sub _LimitCustomField {
CUSTOMFIELD => $cf,
COLUMN => 'Content',
ENTRYAGGREGATOR => 'AND',
+ PREPARSE => 0,
);
$self->_CloseParen( $args{SUBCLAUSE} );
return;
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list