Commit cfd62f22 authored by Per Cederqvist's avatar Per Cederqvist

Imported Bugzilla 4.2.6.

parent a8dbdd44
No preview for this file type
......@@ -730,6 +730,17 @@ sub run_create_validators {
my $class = shift;
my $params = $class->SUPER::run_create_validators(@_);
# Add classification for checking mandatory fields which depend on it
$params->{classification} = $params->{product}->classification->name;
my @mandatory_fields = @{ Bugzilla->fields({ is_mandatory => 1,
enter_bug => 1,
obsolete => 0 }) };
foreach my $field (@mandatory_fields) {
$class->_check_field_is_mandatory($params->{$field->name}, $field,
$params);
}
my $product = delete $params->{product};
$params->{product_id} = $product->id;
my $component = delete $params->{component};
......@@ -754,18 +765,11 @@ sub run_create_validators {
delete $params->{resolution};
delete $params->{lastdiffed};
delete $params->{bug_id};
delete $params->{classification};
Bugzilla::Hook::process('bug_end_of_create_validators',
{ params => $params });
my @mandatory_fields = @{ Bugzilla->fields({ is_mandatory => 1,
enter_bug => 1,
obsolete => 0 }) };
foreach my $field (@mandatory_fields) {
$class->_check_field_is_mandatory($params->{$field->name}, $field,
$params);
}
return $params;
}
......
......@@ -202,7 +202,7 @@ use Memoize;
# CONSTANTS
#
# Bugzilla version
use constant BUGZILLA_VERSION => "4.2.5";
use constant BUGZILLA_VERSION => "4.2.6";
# Location of the remote and local XML files to track new releases.
use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml';
......
......@@ -405,8 +405,10 @@ sub sql_string_until {
}
sub sql_in {
my ($self, $column_name, $in_list_ref) = @_;
return " $column_name IN (" . join(',', @$in_list_ref) . ") ";
my ($self, $column_name, $in_list_ref, $negate) = @_;
return " $column_name "
. ($negate ? "NOT " : "")
. "IN (" . join(',', @$in_list_ref) . ") ";
}
sub sql_fulltext_search {
......
......@@ -325,9 +325,8 @@ sub bz_setup_database {
# hard to fix later. We do this up here because none of the code below
# works if InnoDB is off. (Particularly if we've already converted the
# tables to InnoDB.)
my ($innodb_on) = @{$self->selectcol_arrayref(
q{SHOW VARIABLES LIKE '%have_innodb%'}, {Columns=>[2]})};
if ($innodb_on ne 'YES') {
my %engines = @{$self->selectcol_arrayref('SHOW ENGINES', {Columns => [1,2]})};
if (!$engines{InnoDB} || $engines{InnoDB} !~ /^(YES|DEFAULT)$/) {
die install_string('mysql_innodb_disabled');
}
......
......@@ -74,7 +74,7 @@ sub new {
my $dsn = "dbi:Oracle:host=$host;sid=$dbname";
$dsn .= ";port=$port" if $port;
my $attrs = { FetchHashKeyName => 'NAME_lc',
LongReadLen => max(Bugzilla->params->{'maxattachmentsize'},
LongReadLen => max(Bugzilla->params->{'maxattachmentsize'} || 0,
MIN_LONG_READ_LEN) * 1024,
};
my $self = $class->db_new({ dsn => $dsn, user => $user,
......@@ -216,16 +216,16 @@ sub sql_position {
}
sub sql_in {
my ($self, $column_name, $in_list_ref) = @_;
my ($self, $column_name, $in_list_ref, $negate) = @_;
my @in_list = @$in_list_ref;
return $self->SUPER::sql_in($column_name, $in_list_ref) if $#in_list < 1000;
return $self->SUPER::sql_in($column_name, $in_list_ref, $negate) if $#in_list < 1000;
my @in_str;
while (@in_list) {
my $length = $#in_list + 1;
my $splice = $length > 1000 ? 1000 : $length;
my @sub_in_list = splice(@in_list, 0, $splice);
push(@in_str,
$self->SUPER::sql_in($column_name, \@sub_in_list));
$self->SUPER::sql_in($column_name, \@sub_in_list, $negate));
}
return "( " . join(" OR ", @in_str) . " )";
}
......@@ -550,7 +550,9 @@ sub bz_setup_database {
. " RETURN NUMBER IS BEGIN RETURN LENGTH(COLUMN_NAME); END;");
# Create types for group_concat
$self->do("DROP TYPE T_GROUP_CONCAT");
my $type_exists = $self->selectrow_array("SELECT 1 FROM user_types
WHERE type_name = 'T_GROUP_CONCAT'");
$self->do("DROP TYPE T_GROUP_CONCAT") if $type_exists;
$self->do("CREATE OR REPLACE TYPE T_CLOB_DELIM AS OBJECT "
. "( p_CONTENT CLOB, p_DELIMITER VARCHAR2(256)"
. ", MAP MEMBER FUNCTION T_CLOB_DELIM_ToVarchar return VARCHAR2"
......
......@@ -183,6 +183,7 @@ sub is_set_on_bug {
# This allows bug/create/create.html.tmpl to pass in a hashref that
# looks like a bug object.
my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name};
$value = $value->name if blessed($value);
return 0 if !defined $value;
if ($self->field->type == FIELD_TYPE_BUG_URLS
......
......@@ -3538,9 +3538,6 @@ sub _migrate_user_tags {
VALUES (?, ?)');
my $sth_nq = $dbh->prepare('UPDATE namedqueries SET query = ?
WHERE id = ?');
my $sth_nq_footer = $dbh->prepare(
'DELETE FROM namedqueries_link_in_footer
WHERE user_id = ? AND namedquery_id = ?');
if (scalar @$tags) {
print install_string('update_queries_to_tags'), "\n";
......@@ -3580,13 +3577,11 @@ sub _migrate_user_tags {
next if !$bug_id;
$sth_bug_tag->execute($bug_id, $tag_id);
}
# Existing tags may be used in whines, or shared with
# other users. So we convert them rather than delete them.
$uri->query_param('tag', $tag_name);
$sth_nq->execute($uri->query, $query_id);
# But we don't keep showing them in the footer.
$sth_nq_footer->execute($user_id, $query_id);
}
$dbh->bz_commit_transaction();
......
......@@ -554,26 +554,6 @@ sub print_module_instructions {
( (!$output and @{$check_results->{missing}})
or ($output and $check_results->{any_missing}) ) ? 1 : 0;
# We only print the PPM repository note if we have to.
my $perl_ver = sprintf('%vd', $^V);
if ($need_module_instructions && ON_ACTIVESTATE && vers_cmp($perl_ver, '5.12') < 0) {
# URL when running Perl 5.8.x.
my $url_to_theory58S = 'http://theoryx5.uwinnipeg.ca/ppms';
# Packages for Perl 5.10 are not compatible with Perl 5.8.
if (vers_cmp($perl_ver, '5.10') > -1) {
$url_to_theory58S = 'http://cpan.uwinnipeg.ca/PPMPackages/10xx/';
}
print colored(
install_string('ppm_repo_add',
{ theory_url => $url_to_theory58S }),
COLOR_ERROR);
# ActivePerls older than revision 819 require an additional command.
if (ON_ACTIVESTATE < 819) {
print install_string('ppm_repo_up');
}
}
if ($need_module_instructions or @{ $check_results->{apache} }) {
# If any output was required, we want to close the "table"
print "*" x TABLE_WIDTH . "\n";
......
......@@ -290,12 +290,14 @@ use constant OPERATOR_FIELD_OVERRIDE => {
},
dependson => MULTI_SELECT_OVERRIDE,
keywords => MULTI_SELECT_OVERRIDE,
'flagtypes.name' => MULTI_SELECT_OVERRIDE,
'flagtypes.name' => {
_non_changed => \&_flagtypes_nonchanged,
},
longdesc => {
%{ MULTI_SELECT_OVERRIDE() },
changedby => \&_long_desc_changedby,
changedbefore => \&_long_desc_changedbefore_after,
changedafter => \&_long_desc_changedbefore_after,
_non_changed => \&_long_desc_nonchanged,
},
'longdescs.count' => {
changedby => \&_long_desc_changedby,
......@@ -690,8 +692,16 @@ sub sql {
my ($self) = @_;
return $self->{sql} if $self->{sql};
my $dbh = Bugzilla->dbh;
my ($joins, $clause) = $self->_charts_to_conditions();
if (!$clause->as_string
&& !Bugzilla->params->{'search_allow_no_criteria'}
&& !$self->{allow_unlimited})
{
ThrowUserError('buglist_parameters_required');
}
my $select = join(', ', $self->_sql_select);
my $from = $self->_sql_from($joins);
my $where = $self->_sql_where($clause);
......@@ -1191,14 +1201,7 @@ sub _sql_where {
# SQL a bit more readable for debugging.
my $where = join("\n AND ", $self->_standard_where);
my $clause_sql = $main_clause->as_string;
if ($clause_sql) {
$where .= "\n AND " . $clause_sql;
}
elsif (!Bugzilla->params->{'search_allow_no_criteria'}
&& !$self->{allow_unlimited})
{
ThrowUserError('buglist_parameters_required');
}
$where .= "\n AND " . $clause_sql if $clause_sql;
return $where;
}
......@@ -1689,6 +1692,7 @@ sub _handle_chart {
value => $string_value,
all_values => $value,
joins => [],
condition => $condition,
);
$search_args{quoted} = $self->_quote_unless_numeric(\%search_args);
# This should add a "term" selement to %search_args.
......@@ -1861,8 +1865,14 @@ sub _quote_unless_numeric {
}
sub build_subselect {
my ($outer, $inner, $table, $cond) = @_;
return "$outer IN (SELECT $inner FROM $table WHERE $cond)";
my ($outer, $inner, $table, $cond, $negate) = @_;
# Execute subselects immediately to avoid dependent subqueries, which are
# large performance hits on MySql
my $q = "SELECT DISTINCT $inner FROM $table WHERE $cond";
my $dbh = Bugzilla->dbh;
my $list = $dbh->selectcol_arrayref($q);
return $negate ? "1=1" : "1=2" unless @$list;
return $dbh->sql_in($outer, $list, $negate);
}
# Used by anyexact to get the list of input values. This allows us to
......@@ -2327,14 +2337,50 @@ sub _long_desc_changedbefore_after {
}
}
sub _long_desc_nonchanged {
my ($self, $args) = @_;
my ($chart_id, $operator, $value, $joins) =
@$args{qw(chart_id operator value joins)};
my $dbh = Bugzilla->dbh;
my $table = "longdescs_$chart_id";
my $join_args = {
chart_id => $chart_id,
sequence => $chart_id,
field => 'longdesc',
full_field => "$table.thetext",
operator => $operator,
value => $value,
all_values => $value,
quoted => $dbh->quote($value),
joins => [],
};
$self->_do_operator_function($join_args);
# If the user is not part of the insiders group, they cannot see
# private comments
if (!$self->_user->is_insider) {
$join_args->{term} .= " AND $table.isprivate = 0";
}
my $join = {
table => 'longdescs',
as => $table,
extra => [ $join_args->{term} ],
};
push(@$joins, $join);
$args->{term} = "$table.comment_id IS NOT NULL";
}
sub _content_matches {
my ($self, $args) = @_;
my ($chart_id, $joins, $fields, $operator, $value) =
@$args{qw(chart_id joins fields operator value)};
my $dbh = Bugzilla->dbh;
# "content" is an alias for columns containing text for which we
# can search a full-text index and retrieve results by relevance,
# can search a full-text index and retrieve results by relevance,
# currently just bug comments (and summaries to some degree).
# There's only one way to search a full-text index, so we only
# accept the "matches" operator, which is specific to full-text
......@@ -2582,6 +2628,53 @@ sub _multiselect_multiple {
}
}
sub _flagtypes_nonchanged {
my ($self, $args) = @_;
my ($chart_id, $operator, $value, $joins, $condition) =
@$args{qw(chart_id operator value joins condition)};
my $dbh = Bugzilla->dbh;
# For 'not' operators, we need to negate the whole term.
# If you search for "Flags" (does not contain) "approval+" we actually want
# to return *bugs* that don't contain an approval+ flag. Without rewriting
# the negation we'll search for *flags* which don't contain approval+.
if ($operator =~ s/^not//) {
$args->{operator} = $operator;
$condition->operator($operator);
$condition->negate(1);
}
my $subselect_args = {
chart_id => $chart_id,
sequence => $chart_id,
field => 'flagtypes.name',
full_field => $dbh->sql_string_concat("flagtypes_$chart_id.name", "flags_$chart_id.status"),
operator => $operator,
value => $value,
all_values => $value,
quoted => $dbh->quote($value),
joins => [],
};
$self->_do_operator_function($subselect_args);
my $subselect_term = $subselect_args->{term};
# don't call build_subselect as this must run as a true sub-select
$args->{term} = "EXISTS (
SELECT 1
FROM bugs bugs_$chart_id
LEFT JOIN attachments AS attachments_$chart_id
ON bugs_$chart_id.bug_id = attachments_$chart_id.bug_id
LEFT JOIN flags AS flags_$chart_id
ON bugs_$chart_id.bug_id = flags_$chart_id.bug_id
AND (flags_$chart_id.attach_id = attachments_$chart_id.attach_id
OR flags_$chart_id.attach_id IS NULL)
LEFT JOIN flagtypes AS flagtypes_$chart_id
ON flags_$chart_id.type_id = flagtypes_$chart_id.id
WHERE bugs_$chart_id.bug_id = bugs.bug_id
AND $subselect_term
)";
}
sub _multiselect_nonchanged {
my ($self, $args) = @_;
my ($chart_id, $joins, $field, $operator) =
......@@ -2659,8 +2752,7 @@ sub _multiselect_term {
my $term = $args->{term};
$term .= $args->{_extra_where} || '';
my $select = $args->{_select_field} || 'bug_id';
my $not_sql = $not ? "NOT " : '';
return "bugs.bug_id ${not_sql}IN (SELECT $select FROM $table WHERE $term)";
return build_subselect("bugs.bug_id", $select, $table, $term, $not);
}
###############################
......
......@@ -93,25 +93,29 @@ sub walk_conditions {
sub as_string {
my ($self) = @_;
my @strings;
foreach my $child (@{ $self->children }) {
next if $child->isa(__PACKAGE__) && !$child->has_translated_conditions;
next if $child->isa('Bugzilla::Search::Condition')
&& !$child->translated;
if (!$self->{sql}) {
my @strings;
foreach my $child (@{ $self->children }) {
next if $child->isa(__PACKAGE__) && !$child->has_translated_conditions;
next if $child->isa('Bugzilla::Search::Condition')
&& !$child->translated;
my $string = $child->as_string;
if ($self->joiner eq 'AND') {
$string = "( $string )" if $string =~ /OR/;
}
else {
$string = "( $string )" if $string =~ /AND/;
my $string = $child->as_string;
next unless $string;
if ($self->joiner eq 'AND') {
$string = "( $string )" if $string =~ /OR/;
}
else {
$string = "( $string )" if $string =~ /AND/;
}
push(@strings, $string);
}
push(@strings, $string);
my $sql = join(' ' . $self->joiner . ' ', @strings);
$sql = "NOT( $sql )" if $sql && $self->negate;
$self->{sql} = $sql;
}
my $sql = join(' ' . $self->joiner . ' ', @strings);
$sql = "NOT( $sql )" if $sql && $self->negate;
return $sql;
return $self->{sql};
}
# Search.pm converts URL parameters to Clause objects. This helps do the
......
......@@ -32,9 +32,16 @@ sub new {
}
sub field { return $_[0]->{field} }
sub operator { return $_[0]->{operator} }
sub value { return $_[0]->{value} }
sub operator {
my ($self, $value) = @_;
if (@_ == 2) {
$self->{operator} = $value;
}
return $self->{operator};
}
sub fov {
my ($self) = @_;
return ($self->field, $self->operator, $self->value);
......
......@@ -377,9 +377,14 @@ sub _handle_field_names {
# Flag and requestee shortcut
if ($or_operand =~ /^(?:flag:)?([^\?]+\?)([^\?]*)$/) {
addChart('flagtypes.name', 'substring', $1, $negate);
$chart++; $and = $or = 0; # Next chart for boolean AND
addChart('requestees.login_name', 'substring', $2, $negate);
my ($flagtype, $requestee) = ($1, $2);
addChart('flagtypes.name', 'substring', $flagtype, $negate);
if ($requestee) {
# AND
$chart++;
$and = $or = 0;
addChart('requestees.login_name', 'substring', $requestee, $negate);
}
return 1;
}
......
......@@ -2428,7 +2428,8 @@ the database again. Used mostly by L<Bugzilla::Product>.
=item C<can_enter_product($product_name, $warn)>
Description: Returns 1 if the user can enter bugs into the specified product.
Description: Returns a product object if the user can enter bugs into the
specified product.
If the user cannot enter bugs into the product, the behavior of
this method depends on the value of $warn:
- if $warn is false (or not given), a 'false' value is returned;
......@@ -2439,7 +2440,7 @@ the database again. Used mostly by L<Bugzilla::Product>.
must be thrown if the user cannot enter bugs
into the specified product.
Returns: 1 if the user can enter bugs into the product,
Returns: A product object if the user can enter bugs into the product,
0 if the user cannot enter bugs into the product and if $warn
is false (an error is thrown if $warn is true).
......
......@@ -399,12 +399,23 @@ sub history {
sub search {
my ($self, $params) = @_;
if ( defined($params->{offset}) and !defined($params->{limit}) ) {
ThrowCodeError('param_required',
{ param => 'limit', function => 'Bug.search()' });
}
my $max_results = Bugzilla->params->{max_search_results};
unless (defined $params->{limit} && $params->{limit} == 0) {
if (!defined $params->{limit} || $params->{limit} > $max_results) {
$params->{limit} = $max_results;
}
}
else {
delete $params->{limit};
delete $params->{offset};
}
$params = Bugzilla::Bug::map_fields($params);
delete $params->{WHERE};
......@@ -431,7 +442,17 @@ sub search {
my $clause = join(' OR ', @likes);
$params->{WHERE}->{"($clause)"} = [map { "\%$_\%" } @strings];
}
# If no other parameters have been passed other than limit and offset
# and a WHERE parameter was not created earlier, then we throw error
# if system is configured to do so.
if (!$params->{WHERE}
&& !grep(!/(limit|offset)/i, keys %$params)
&& !Bugzilla->params->{search_allow_no_criteria})
{
ThrowUserError('buglist_parameters_required');
}
# We want include_fields and exclude_fields to be passed to
# _bug_to_hash but not to Bugzilla::Bug->match so we copy the
# params and delete those before passing to Bugzilla::Bug->match.
......@@ -2074,13 +2095,16 @@ May not be an array.
=item C<limit>
C<int> Limit the number of results returned to C<int> records.
C<int> Limit the number of results returned to C<int> records. If the limit
is more than zero and higher than the maximum limit set by the administrator,
then the maximum limit will be used instead. If you set the limit equal to zero,
then all matching results will be returned instead.
=item C<offset>
C<int> Used in conjunction with the C<limit> argument, C<offset> defines
the starting position for the search. For example, given a search that
would return 100 bugs, setting C<limit> to 10 and C<offset> to 10 would return
C<int> Used in conjunction with the C<limit> argument, C<offset> defines
the starting position for the search. For example, given a search that
would return 100 bugs, setting C<limit> to 10 and C<offset> to 10 would return
bugs 11 through 20 from the set of 100.
=item C<op_sys>
......@@ -2166,10 +2190,16 @@ log in and I<then> call this method.
=item B<Errors>
Currently, this function doesn't throw any special errors (other than
the ones that all webservice functions can throw). If you specify
an invalid value for a particular field, you just won't get any results
for that value.
If you specify an invalid value for a particular field, you just won't
get any results for that value.
=over
=item 1000 (Parameters Required)
You may not search without any search terms.
=back
=item B<History>
......@@ -2182,6 +2212,10 @@ for that value.
=item The C<reporter> input parameter was renamed to C<creator>
in Bugzilla B<4.0>.
=item In B<4.2.6> and newer, added the ability to return all results if
C<limit> is set equal to zero. Otherwise maximum results returned are limited
by system configuration.
=back
=back
......
......@@ -168,6 +168,9 @@ use constant WS_ERROR_CODE => {
invalid_regexp => 803,
invalid_group_name => 804,
# Search errors are 1000-1100
buglist_parameters_required => 1000,
# Errors thrown by the WebService itself. The ones that are negative
# conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
......
This diff is collapsed.
......@@ -7,11 +7,11 @@
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
REL="HOME"
TITLE="The Bugzilla Guide - 4.2.5
TITLE="The Bugzilla Guide - 4.2.6
Release"
HREF="index.html"><LINK