- $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, $null_op, $inv_op, $range_op)
- = $self->ClassifySQLOperation( $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' ) {
- 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 : "$type-$object.$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 ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
- $self->_OpenParen;
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'id',
- OPERATOR => $op,
- VALUE => $value,
- %rest
- );
- $self->_SQLLimit(
- 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(
- $_field, '<=', $end_ip, %rest,
- SUBKEY => $rest{'SUBKEY'}. '.Content',
- );
- $self->_CustomFieldLimit(
- $_field, '>=', $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 !$single_value && !$range_op;
- my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
-
- $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->_SQLLimit( $fix_op->(
- ALIAS => $ObjectCFs,
- 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->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => ">=",
- VALUE => $daystart,
- %rest,
- );
-
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => "<",
- VALUE => $dayend,
- %rest,
- ENTRYAGGREGATOR => 'AND',
- );
-
- $self->_CloseParen;
- }
- elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
- if ( length( Encode::encode( "UTF-8", $value) ) < 256 ) {
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- %rest
- );
- }
- else {
- $self->_OpenParen;
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => '=',
- VALUE => '',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- ENTRYAGGREGATOR => 'OR'
- );
- $self->_CloseParen;
- $self->_SQLLimit( $fix_op->(
- ALIAS => $ObjectCFs,
- FIELD => 'LargeContent',
- OPERATOR => $op,
- VALUE => $value,
- ENTRYAGGREGATOR => 'AND',
- CASESENSITIVE => 0,
- ) );
- }
- }
- else {
- $self->_SQLLimit(
- ALIAS => $ObjectCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- %rest
- );