]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 601848: Fix percentage_complete searches for all operators on both MySQL
authorMax Kanat-Alexander <mkanat@bugzilla.org>
Tue, 5 Oct 2010 05:55:23 +0000 (22:55 -0700)
committerMax Kanat-Alexander <mkanat@bugzilla.org>
Tue, 5 Oct 2010 05:55:23 +0000 (22:55 -0700)
and PostgreSQL
r=mkanat, a=mkanat (module owner)

Bugzilla/Bug.pm
Bugzilla/Search.pm
xt/lib/Bugzilla/Test/Search/Constants.pm
xt/lib/Bugzilla/Test/Search/FieldTest.pm

index fccf94a02ca9d68f2e8b5a0a7fedad85668fec57..327e55ccc479d2382f60e8ca2faab408cac474cc 100644 (file)
@@ -3327,7 +3327,10 @@ sub percentage_complete {
     my $actual    = $self->actual_time;
     my $total = $remaining + $actual;
     return undef if $total == 0;
-    return 100 * ($actual / $total);
+    # Search.pm truncates this value to an integer, so we want to as well,
+    # since this is mostly used in a test where its value needs to be
+    # identical to what the database will return.
+    return int(100 * ($actual / $total));
 }
 
 sub product {
index d887677b0551e83013e3bc2faba87167b00d1d9f..da6f57bf3bf00b1bfc3971297966a60f7d6db445 100644 (file)
@@ -406,6 +406,11 @@ use constant COLUMN_DEPENDS => {
 # DB::Schema to figure out what needs to be joined, but for some
 # fields it needs a little help.
 use constant COLUMN_JOINS => {
+    actual_time => {
+        table => '(SELECT bug_id, SUM(work_time) AS total'
+                 . ' FROM longdescs GROUP BY bug_id)',
+        join  => 'INNER',
+    },
     assigned_to => {
         from  => 'assigned_to',
         to    => 'userid',
@@ -441,10 +446,6 @@ use constant COLUMN_JOINS => {
         to    => 'id',
         join  => 'INNER',
     },
-    actual_time => {
-        table  => 'longdescs',
-        join   => 'INNER',
-    },
     'flagtypes.name' => {
         as    => 'map_flags',
         table => 'flags',
@@ -504,18 +505,20 @@ sub COLUMNS {
 
     # Next we define columns that have special SQL instead of just something
     # like "bugs.bug_id".
-    my $actual_time = '(SUM(map_actual_time.work_time)'
-        . ' * COUNT(DISTINCT map_actual_time.bug_when)/COUNT(bugs.bug_id))';
+    my $total_time = "(map_actual_time.total + bugs.remaining_time)";
     my %special_sql = (
         deadline    => $dbh->sql_date_format('bugs.deadline', '%Y-%m-%d'),
-        actual_time => $actual_time,
+        actual_time => 'map_actual_time.total',
 
+        # "FLOOR" is in there to turn this into an integer, making searches
+        # totally predictable. Otherwise you get floating-point numbers that
+        # are rather hard to search reliably if you're asking for exact
+        # numbers.
         percentage_complete =>
-            "(CASE WHEN $actual_time + bugs.remaining_time = 0.0"
-              . " THEN 0.0"
-              . " ELSE 100"
-                   . " * ($actual_time / ($actual_time + bugs.remaining_time))"
-              . " END)",
+            "(CASE WHEN $total_time = 0"
+               . " THEN 0"
+               . " ELSE FLOOR(100 * (map_actual_time.total / $total_time))"
+                . " END)",
 
         'flagtypes.name' => $dbh->sql_group_concat('DISTINCT ' 
             . $dbh->sql_string_concat('map_flagtypes.name', 'map_flags.status')),
@@ -614,7 +617,6 @@ sub REPORT_COLUMNS {
 # is here because it *always* goes into the GROUP BY as the first item,
 # so it should be skipped when determining extra GROUP BY columns.
 use constant GROUP_BY_SKIP => EMPTY_COLUMN, qw(
-    actual_time
     bug_id
     flagtypes.name
     keywords
@@ -2240,30 +2242,12 @@ sub _work_time {
 
 sub _percentage_complete {
     my ($self, $args) = @_;
-    my ($chart_id, $joins, $operator, $having, $fields) =
-        @$args{qw(chart_id joins operator having fields)};
-
-    my $table = "longdescs_$chart_id";
-
-    # We can't just use "percentage_complete" as the field, because
-    # (a) PostgreSQL doesn't accept it in the HAVING clause
-    # and (b) it wouldn't work in multiple chart rows, because it uses
-    # a fixed name for the table, "ldtime".
-    my $expression = COLUMNS->{percentage_complete}->{name};
-    $expression =~ s/\bldtime\b/$table/g;
-    $args->{full_field} = "($expression)";
-    push(@$joins, { table => 'longdescs', as => $table });
-
-    # We need remaining_time in _select_columns, otherwise we can't use
-    # it in the expression for creating percentage_complete.
-    $self->_add_extra_column('remaining_time');
+    
+    $args->{full_field} = COLUMNS->{percentage_complete}->{name};
 
-    $self->_do_operator_function($args);
-    push(@$having, $args->{term});
-   
-    # We put something into $args->{term} so that do_search_function
-    # stops processing.
-    $args->{term} = '';
+    # We need actual_time in _select_columns, otherwise we can't use
+    # it in the expression for searching percentage_complete.
+    $self->_add_extra_column('actual_time');
 }
 
 sub _bug_group_nonchanged {
index aeb392c700aacdc7f2d8817190901143e9ce073f..c0dfe30c8a7747fad7fc7b7efcc6eb771d496ff3 100644 (file)
@@ -140,7 +140,6 @@ use constant SKIP_FIELDS => qw(
 # right in OR tests, and it's too much work to document the exact tests
 # that they cause to fail.
 use constant OR_SKIP => qw(
-    percentage_complete
     flagtypes.name
 );
 
@@ -167,7 +166,7 @@ use constant FIELD_SUBSTR_SIZE => {
     deadline => -5,
     creation_ts => -8,
     delta_ts => -8,
-    percentage_complete => 7,
+    percentage_complete => 1,
     work_time => 3,
     remaining_time => 3,
     target_milestone => 15,
@@ -361,19 +360,8 @@ use constant KNOWN_BROKEN => {
         work_time => { contains => [2,3,4] },
     },
 
-    greaterthan => { GREATERTHAN_BROKEN },
-
-    # percentage_complete is broken -- it won't match equal values.
-    greaterthaneq => {
-        GREATERTHAN_BROKEN,
-        percentage_complete => { contains => [2] },
-    },
-
-    # percentage_complete doesn't do a numeric comparison, so
-    # it doesn't find decimal values.
-    anyexact => {
-        percentage_complete => { contains => [2] },
-    },
+    greaterthan   => { GREATERTHAN_BROKEN },
+    greaterthaneq => { GREATERTHAN_BROKEN },
 
     'allwordssubstr-<1>' => { ALLWORDS_BROKEN },
     # flagtypes.name does not work here, probably because they all try to
@@ -399,7 +387,6 @@ use constant KNOWN_BROKEN => {
         work_time => { contains => [1] },
     },
     'anywords-<1> <2>' => {
-        percentage_complete => { contains => [2] },
         work_time => { contains => [1,2] },
     },
 
@@ -481,16 +468,6 @@ use constant PG_BROKEN => {
         notregexp => { contains => [5] },
         nowords   => { contains => [5] },
     },
-    percentage_complete => {
-        'allwordssubstr-<1>' => { contains => [3] },
-        anywordssubstr  => { contains => [2,3] },
-        casesubstring   => { contains => [3] },
-        'notregexp-<1>' => { contains => [3] },
-        notsubstring    => { contains => [3] },
-        nowordssubstr   => { contains => [3] },
-        'regexp-<1>'    => { contains => [3] },
-        substring       => { contains => [3] },
-    },
 };
 
 ###################
@@ -512,7 +489,6 @@ use constant COMMON_BROKEN_NOT => (
     "flagtypes.name"          => { contains => [5] },
     "keywords"                => { contains => [5] },
     "longdescs.isprivate"     => { contains => [1] },
-    "percentage_complete"     => { contains => [1 .. 5] },
     "see_also"                => { contains => [5] },
     FIELD_TYPE_BUG_ID,           { contains => [5] },
     FIELD_TYPE_DATETIME,         { contains => [5] },
@@ -527,7 +503,7 @@ use constant CHANGED_BROKEN_NOT => (
     "classification"        => { contains => [1] },
     "commenter"             => { contains => [1] },
     "delta_ts"              => { contains => [1] },
-    "percentage_complete"   => { contains => [1] },
+    percentage_complete     => { contains => [1] },
     "requestees.login_name" => { contains => [1] },
     "setters.login_name"    => { contains => [1] },
     "work_time"             => { contains => [1] },    
@@ -549,7 +525,6 @@ use constant NEGATIVE_BROKEN_NOT => (
     "bug_group"           => { contains => [5] },
     "dependson"           => { contains => [2, 4, 5] },
     "flagtypes.name"      => { contains => [1 .. 5] },
-    "percentage_complete" => { contains => [1 .. 5] },    
 );
 
 # These are field/operator combinations that are broken when run under NOT().
@@ -603,7 +578,6 @@ use constant BROKEN_NOT => {
     },
     'anyexact-<1>, <2>' => {
         bug_group => { contains => [1] },
-        percentage_complete => { contains => [1,3,4,5] },
         keywords => { contains => [1,5] },
         see_also => { },
         FIELD_TYPE_MULTI_SELECT, { },
@@ -616,7 +590,6 @@ use constant BROKEN_NOT => {
     },
     'anywords-<1> <2>' => {
         'attach_data.thedata' => { contains => [5] },
-        "percentage_complete" => { contains => [1,3,4,5] },
         work_time => { contains => [1,2] },
     },
     anywordssubstr => {
@@ -624,7 +597,6 @@ use constant BROKEN_NOT => {
         "work_time" => { contains => [1, 2] },
     },
     'anywordssubstr-<1> <2>' => {
-        percentage_complete => { contains => [1,2,3,4,5] },
         FIELD_TYPE_MULTI_SELECT, { contains => [5] },
     },
     casesubstring => {
@@ -645,8 +617,8 @@ use constant BROKEN_NOT => {
         "attach_data.thedata"   => { contains => [2, 3, 4] },
         "classification"        => { contains => [2, 3, 4] },
         "commenter"             => { contains => [2, 3, 4] },
+        percentage_complete     => { contains => [2, 3, 4] },
         "delta_ts"              => { contains => [2, 3, 4] },
-        "percentage_complete"   => { contains => [2, 3, 4] },
         "requestees.login_name" => { contains => [2, 3, 4] },
         "setters.login_name"    => { contains => [2, 3, 4] },
     },
@@ -693,7 +665,6 @@ use constant BROKEN_NOT => {
         cc               => { contains => [1] },
         "flagtypes.name" => { contains => [2, 5] },
         "work_time"      => { contains => [2, 3, 4] },
-        percentage_complete => { contains => [1,3,4,5] },,
         FIELD_TYPE_MULTI_SELECT, { contains => [5] },
     },
     lessthan => {
@@ -717,14 +688,14 @@ use constant BROKEN_NOT => {
     notsubstring   => { NEGATIVE_BROKEN_NOT },
     nowords        => {
         NEGATIVE_BROKEN_NOT,
-        "work_time"           => { contains => [2, 3, 4] },
+        "work_time" => { contains => [2, 3, 4] },
         "flagtypes.name" => { },
     },
     nowordssubstr  => {
         NEGATIVE_BROKEN_NOT,
         "attach_data.thedata" => { },
         "flagtypes.name" => { },
-        "work_time"           => { contains => [2, 3, 4] },
+        "work_time" => { contains => [2, 3, 4] },
     },
     regexp         => {
         COMMON_BROKEN_NOT,
@@ -774,7 +745,7 @@ use constant REGEX_OVERRIDE => {
     remaining_time => { value => '^9.0' },
     work_time      => { value => '^1.0' },
     longdesc       => { value => '^1-' },
-    percentage_complete => { value => '^10.0' },
+    percentage_complete => { value => '^10' },
     FIELD_TYPE_BUG_ID, { value => '^<1>$' },
     FIELD_TYPE_DATETIME, { value => '^2037-03-01' }
 };
@@ -915,22 +886,46 @@ use constant TESTS => {
         { contains => [2,3,4,5], value => '<1>' },
     ],
     substring => [
-        { contains => [1], value => '<1>' },
+        { contains => [1], value => '<1>',
+          override => {
+              percentage_complete => { contains => [1,2,3] },
+          }
+        },
     ],
     casesubstring => [
-        { contains => [1], value => '<1>' },
+        { contains => [1], value => '<1>',
+          override => {
+              percentage_complete => { contains => [1,2,3] },
+          }
+        },
         { contains => [], value => '<1>', transform => sub { lc($_[0]) },
-          extra_name => 'lc', if_equal => { contains => [1] } },
+          extra_name => 'lc', if_equal => { contains => [1] },
+          override => {
+              percentage_complete => { contains => [1,2,3] },
+          }
+        },
     ],
     notsubstring => [
-        { contains => [2,3,4,5], value => '<1>' },
+        { contains => [2,3,4,5], value => '<1>',
+          override => {
+              percentage_complete => { contains => [4,5] },
+          },
+        }
     ],
     regexp => [
-        { contains => [1], value => '<1>', escape => 1 },
+        { contains => [1], value => '<1>', escape => 1,
+          override => {
+              percentage_complete => { value => '^10' },
+          }
+        },
         { contains => [1], value => '^1-', override => REGEX_OVERRIDE },
     ],
     notregexp => [
-        { contains => [2,3,4,5], value => '<1>', escape => 1 },
+        { contains => [2,3,4,5], value => '<1>', escape => 1,
+          override => {
+              percentage_complete => { value => '^10' },
+          }
+        },
         { contains => [2,3,4,5], value => '^1-', override => REGEX_OVERRIDE },
     ],
     lessthan => [
@@ -1031,14 +1026,26 @@ use constant TESTS => {
     ],
     anywordssubstr => [
         { contains => [1,2], value => '<1> <2>', 
-          override => { ANY_OVERRIDE } },
+          override => {
+              ANY_OVERRIDE,
+              percentage_complete => { contains => [1,2,3] },
+          }
+        },
     ],
     allwordssubstr => [
         { contains => [1], value => '<1>',
-          override => { MULTI_BOOLEAN_OVERRIDE } },
+          override => {
+              MULTI_BOOLEAN_OVERRIDE,
+              # We search just the number "1" for percentage_complete,
+              # which matches a lot of bugs.
+              percentage_complete => { contains => [1,2,3] },
+          },
+        },
         { contains => [], value => '<1>,<2>',
           override => {
               dependson => { value => '<1-id> <3-id>', contains => [] },
+              # bug 3 has the value "21" here, so matches "2,1"
+              percentage_complete => { value => '<2>,<3>', contains => [3] }
           }
         },
     ],
@@ -1048,6 +1055,7 @@ use constant TESTS => {
               # longdescs.isprivate translates to "1 0", so no bugs should
               # show up.
               'longdescs.isprivate' => { contains => [] },
+              percentage_complete => { contains => [4,5] },
               # 1.0 0.0 exludes bug 5.
               # XXX However, it also shouldn't match 2, 3, or 4, because
               # they contain at least one comment with 0.0 work_time.
index 3e7fd2521d5d4773910a2cc385821b8f73f03ef9..532936e91b8245540ca4e6cfc8f836dd7ae7a409 100644 (file)
@@ -166,7 +166,9 @@ sub transformed_value_was_equal {
 sub bug_is_contained {
     my ($self, $number) = @_;
     my $contains = $self->test->{contains};
-    if ($self->transformed_value_was_equal($number)) {
+    if ($self->transformed_value_was_equal($number)
+        and !$self->test->{override}->{$self->field}->{contains})
+    {
         $contains = $self->test->{if_equal}->{contains};
     }
     return grep($_ == $number, @$contains) ? 1 : 0;
@@ -482,11 +484,6 @@ sub _substr_value {
             $substr_size += length($field);
         }
         my $string = substr($value, 0, $substr_size);
-        # Make percentage_complete substrings strings match integers uniquely,
-        # by searching for the full decimal number.
-        if ($field eq 'percentage_complete' and length($string) < $substr_size) {
-            $string .= ".000";
-        }
         return $string;
     }
     return substr($value, $substr_size);