Commit 776fe686 authored by Max Kanat-Alexander's avatar Max Kanat-Alexander

Bug 578278: Search.pm: Fully generate the SELECT clause inside of an accessor

r=mkanat, a=mkanat
parent 546b2df2
...@@ -292,7 +292,7 @@ use constant SPECIAL_ORDER => { ...@@ -292,7 +292,7 @@ use constant SPECIAL_ORDER => {
}; };
# Certain columns require other columns to come before them # Certain columns require other columns to come before them
# in _display_columns, and should be put there if they're not there. # in _select_columns, and should be put there if they're not there.
use constant COLUMN_DEPENDS => { use constant COLUMN_DEPENDS => {
classification => ['product'], classification => ['product'],
percentage_complete => ['actual_time', 'remaining_time'], percentage_complete => ['actual_time', 'remaining_time'],
...@@ -531,31 +531,85 @@ sub _multi_select_fields { ...@@ -531,31 +531,85 @@ sub _multi_select_fields {
return $self->{multi_select_fields}; return $self->{multi_select_fields};
} }
# These are the fields the user has chosen to display on the buglist. ###############################
sub _display_columns { # Internal Accessors: Columns #
my ($self, $columns) = @_; ###############################
if ($columns) {
my @actual_columns; # These are the fields the user has chosen to display on the buglist,
foreach my $column (@$columns) { # exactly as they were passed to new().
if (my $add_first = COLUMN_DEPENDS->{$column}) { sub _input_columns { @{ $_[0]->{'fields'} || [] } }
push(@actual_columns, @$add_first);
} # These are columns that are also going to be in the SELECT for one reason
push(@actual_columns, $column); # or another, but weren't actually requested by the caller.
sub _extra_columns {
my ($self) = @_;
# Everything that's going to be in the ORDER BY must also be
# in the SELECT. We make a copy of input_order here so that modifications
# to extra_columns don't modify input_order.
$self->{extra_columns} ||= [ $self->_input_order ];
return @{ $self->{extra_columns} };
}
# For search functions to modify extra_columns. It doesn't matter if
# people push the same column onto this array multiple times, because
# _select_columns will call "uniq" on its final result.
sub _add_extra_column {
my ($self, $column) = @_;
push(@{ $self->{extra_columns} }, $column);
}
# These are the columns that we're going to be actually SELECTing.
sub _select_columns {
my ($self) = @_;
return @{ $self->{select_columns} } if $self->{select_columns};
my @select_columns;
foreach my $column ($self->_input_columns, $self->_extra_columns) {
if (my $add_first = COLUMN_DEPENDS->{$column}) {
push(@select_columns, @$add_first);
} }
$self->{display_columns} = [uniq @actual_columns]; push(@select_columns, $column);
} }
return $self->{display_columns} || [];
$self->{select_columns} = [uniq @select_columns];
return @{ $self->{select_columns} };
} }
# JOIN statements for the display columns. This should not be called # JOIN statements for the display columns. This should not be called
# Until the moment it is needed, because _display_columns might be # Until the moment it is needed, because _select_columns might be
# modified by the charts. # modified by the charts.
sub _display_column_joins { sub _select_column_joins {
my ($self) = @_; my ($self) = @_;
$self->{display_column_joins} ||= $self->_build_display_column_joins(); $self->{display_column_joins} ||= $self->_build_select_column_joins();
return @{ $self->{display_column_joins} }; return @{ $self->{display_column_joins} };
} }
# This takes _select_columns and translates it into the actual SQL that
# will go into the SELECT clause.
sub _sql_select {
my ($self) = @_;
my @sql_fields;
foreach my $column ($self->_select_columns) {
my $alias = $column;
# Aliases cannot contain dots in them. We convert them to underscores.
$alias =~ s/\./_/g;
my $sql = ($column eq EMPTY_COLUMN)
? EMPTY_COLUMN : COLUMNS->{$column}->{name} . " AS $alias";
push(@sql_fields, $sql);
}
return @sql_fields;
}
#############################
# Internal Accessors: Order #
#############################
# The "order" that was requested by the consumer, exactly as it was
# requested.
sub _input_order { @{ $_[0]->{'order'} || [] } }
# A hashref that describes all the special stuff that has to be done
# for various fields if they go into the ORDER BY clause.
sub _special_order { sub _special_order {
my ($self) = @_; my ($self) = @_;
return $self->{special_order} if $self->{special_order}; return $self->{special_order} if $self->{special_order};
...@@ -583,10 +637,10 @@ sub _special_order { ...@@ -583,10 +637,10 @@ sub _special_order {
# Helpers for Internal Accessors # # Helpers for Internal Accessors #
################################## ##################################
sub _build_display_column_joins { sub _build_select_column_joins {
my ($self) = @_; my ($self) = @_;
my @joins; my @joins;
foreach my $field (@{ $self->_display_columns }) { foreach my $field ($self->_select_columns) {
my @column_join = $self->_column_join($field); my @column_join = $self->_column_join($field);
push(@joins, @column_join); push(@joins, @column_join);
} }
...@@ -648,12 +702,10 @@ sub new { ...@@ -648,12 +702,10 @@ sub new {
sub init { sub init {
my $self = shift; my $self = shift;
my $fields = $self->_display_columns($self->{'fields'});
my $params = $self->{'params'}; my $params = $self->{'params'};
$params->convert_old_params(); $params->convert_old_params();
$self->{'user'} ||= Bugzilla->user; $self->{'user'} ||= Bugzilla->user;
my $user = $self->{'user'}; my $user = $self->{'user'};
my @inputorder = @{ $self->{'order'} || [] }; my @inputorder = @{ $self->{'order'} || [] };
my @orderby; my @orderby;
...@@ -665,14 +717,7 @@ sub init { ...@@ -665,14 +717,7 @@ sub init {
my @andlist; my @andlist;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
# All items that are in the ORDER BY must be in the SELECT.
foreach my $orderitem (@inputorder) {
my $column_name = split_order_term($orderitem);
if (!grep($_ eq $column_name, @$fields)) {
push(@$fields, $column_name);
}
}
# If the user has selected all of either status or resolution, change to # If the user has selected all of either status or resolution, change to
# selecting none. This is functionally equivalent, but quite a lot faster. # selecting none. This is functionally equivalent, but quite a lot faster.
...@@ -1119,7 +1164,6 @@ sub init { ...@@ -1119,7 +1164,6 @@ sub init {
where => \@wherepart, where => \@wherepart,
having => \@having, having => \@having,
group_by => \@groupby, group_by => \@groupby,
fields => $fields,
); );
# This should add a "term" selement to %search_args. # This should add a "term" selement to %search_args.
$self->do_search_function(\%search_args); $self->do_search_function(\%search_args);
...@@ -1175,7 +1219,7 @@ sub init { ...@@ -1175,7 +1219,7 @@ sub init {
my %suppseen = ("bugs" => 1); my %suppseen = ("bugs" => 1);
my $suppstring = "bugs"; my $suppstring = "bugs";
my @supplist = (" "); my @supplist = (" ");
foreach my $str ($self->_display_column_joins, @supptables) { foreach my $str ($self->_select_column_joins, @supptables) {
if ($str =~ /^(LEFT|INNER|RIGHT)\s+JOIN/i) { if ($str =~ /^(LEFT|INNER|RIGHT)\s+JOIN/i) {
$str =~ /^(.*?)\s+ON\s+(.*)$/i; $str =~ /^(.*?)\s+ON\s+(.*)$/i;
...@@ -1197,16 +1241,8 @@ sub init { ...@@ -1197,16 +1241,8 @@ sub init {
# Make sure we create a legal SQL query. # Make sure we create a legal SQL query.
@andlist = ("1 = 1") if !@andlist; @andlist = ("1 = 1") if !@andlist;
my @sql_fields;
foreach my $field (@$fields) { my $query = "SELECT " . join(', ', $self->_sql_select) .
my $alias = $field;
# Aliases cannot contain dots in them. We convert them to underscores.
$alias =~ s/\./_/g;
my $sql_field = ($field eq EMPTY_COLUMN) ? EMPTY_COLUMN
: COLUMNS->{$field}->{name} . " AS $alias";
push(@sql_fields, $sql_field);
}
my $query = "SELECT " . join(', ', @sql_fields) .
" FROM $suppstring" . " FROM $suppstring" .
" LEFT JOIN bug_group_map " . " LEFT JOIN bug_group_map " .
" ON bug_group_map.bug_id = bugs.bug_id "; " ON bug_group_map.bug_id = bugs.bug_id ";
...@@ -1234,7 +1270,7 @@ sub init { ...@@ -1234,7 +1270,7 @@ sub init {
} }
# For some DBs, every field in the SELECT must be in the GROUP BY. # For some DBs, every field in the SELECT must be in the GROUP BY.
foreach my $field (@$fields) { foreach my $field ($self->_select_columns) {
# These fields never go into the GROUP BY (bug_id goes in # These fields never go into the GROUP BY (bug_id goes in
# explicitly, below). # explicitly, below).
my @skip_group_by = (EMPTY_COLUMN, my @skip_group_by = (EMPTY_COLUMN,
...@@ -1940,11 +1976,9 @@ sub _percentage_complete { ...@@ -1940,11 +1976,9 @@ sub _percentage_complete {
push(@$joins, "LEFT JOIN longdescs AS $table " . push(@$joins, "LEFT JOIN longdescs AS $table " .
"ON $table.bug_id = bugs.bug_id"); "ON $table.bug_id = bugs.bug_id");
# We need remaining_time in _display_columns, otherwise we can't use # We need remaining_time in _select_columns, otherwise we can't use
# it in the expression for creating percentage_complete. # it in the expression for creating percentage_complete.
if (!grep { $_ eq 'remaining_time' } @$fields) { $self->_add_extra_column('remaining_time');
push(@$fields, 'remaining_time');
}
$self->_do_operator_function($args); $self->_do_operator_function($args);
push(@$having, $args->{term}); push(@$having, $args->{term});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment