$attrs->{alias} ||= 'me';
- # default selection list
- $attrs->{columns} = [ $source->resolve->columns ]
- unless List::Util::first { exists $attrs->{$_} } qw/columns cols select as _trailing_select/;
-
# Creation of {} and bless separated to mitigate RH perl bug
# see https://bugzilla.redhat.com/show_bug.cgi?id=196836
my $self = {
}
my $call_attrs = {};
- $call_attrs = pop(@_) if (
- @_ > 1 and ( ! defined $_[-1] or ref $_[-1] eq 'HASH' )
- );
+ if (@_ > 1) {
+ if (ref $_[-1] eq 'HASH') {
+ # copy for _normalize_selection
+ $call_attrs = { %{ pop @_ } };
+ }
+ elsif (! defined $_[-1] ) {
+ pop @_; # search({}, undef)
+ }
+ }
# see if we can keep the cache (no $rs changes)
my $cache;
my $old_having = delete $old_attrs->{having};
my $old_where = delete $old_attrs->{where};
+ my $new_attrs = { %$old_attrs };
- # start with blind overwriting merge
- my $new_attrs = { %{$old_attrs}, %{$call_attrs} };
-
- # join/prefetch use their own crazy merging heuristics
- foreach my $key (qw/join prefetch/) {
- $new_attrs->{$key} = $self->_merge_joinpref_attr($old_attrs->{$key}, $call_attrs->{$key})
- if exists $call_attrs->{$key};
- }
-
- # stack binds together
- $new_attrs->{bind} = [ @{ $old_attrs->{bind} || [] }, @{ $call_attrs->{bind} || [] } ];
-
- # take care of selects (only if anything changed)
+ # take care of call attrs (only if anything is changing)
if (keys %$call_attrs) {
$self->throw_exception ('_trailing_select is not a public attribute - do not use it in search()')
- if exists $call_attrs->{_trailing_select};
+ if ( exists $call_attrs->{_trailing_select} or exists $call_attrs->{'+_trailing_select'} );
+
+ my @selector_attrs = qw/select as columns cols +select +as +columns include_columns _trailing_select +_trailing_select/;
+
+ # Normalize the selector list (operates on the passed-in attr structure)
+ # Need to do it on every chain instead of only once on _resolved_attrs, in
+ # order to separate 'as'-ed from blind 'select's
+ $self->_normalize_selection ($call_attrs);
- my @selector_attrs = qw/select as columns cols +select +as +columns include_columns/;
+ # start with blind overwriting merge, exclude selector attrs
+ $new_attrs = { %{$old_attrs}, %{$call_attrs} };
+ delete @{$new_attrs}{@selector_attrs};
# reset the current selector list if new selectors are supplied
if (List::Util::first { exists $call_attrs->{$_} } qw/columns cols select as/) {
- # the new/old acrobatics is because of the merger in the next loop
- for ($new_attrs, $old_attrs) {
- delete @{$_}{@selector_attrs, '_trailing_select'};
- }
+ delete @{$old_attrs}{@selector_attrs};
}
for (@selector_attrs) {
}
}
- # Normalize the selector list (operates on the passed-in attr structure)
- # Need to do it on every chain instead of only once on _resolved_attrs, in
- # order to separate 'as'-ed from blind 'select's
- $self->_normalize_selection ($new_attrs);
+
+ # join/prefetch use their own crazy merging heuristics
+ foreach my $key (qw/join prefetch/) {
+ $new_attrs->{$key} = $self->_merge_joinpref_attr($old_attrs->{$key}, $call_attrs->{$key})
+ if exists $call_attrs->{$key};
+ }
+
+ # stack binds together
+ $new_attrs->{bind} = [ @{ $old_attrs->{bind} || [] }, @{ $call_attrs->{bind} || [] } ];
}
sub _normalize_selection {
my ($self, $attrs) = @_;
- # merge all balanced selectors into the 'columns' stack, deleting the rest
- foreach my $key (qw/+columns include_columns/) {
- $attrs->{columns} = $self->_merge_attr($attrs->{columns}, delete $attrs->{$key})
- if exists $attrs->{$key};
- }
+ # legacy syntax
+ $attrs->{'+columns'} = $self->_merge_attr($attrs->{'+columns'}, delete $attrs->{include_columns})
+ if exists $attrs->{include_columns};
+ # Keep the X vs +X separation until _resolved_attrs time - this allows to
+ # delay the decision on whether to use a default select list ($rsrc->columns)
+ # allowing stuff like the remove_columns helper to work
+ #
# select/as +select/+as pairs need special handling - the amount of select/as
# elements in each pair does *not* have to be equal (think multicolumn
# selectors like distinct(foo, bar) ). If the selector is bare (no 'as'
}
@$sel = @new_sel;
- $attrs->{'_trailing_select'} = $self->_merge_attr($attrs->{'_trailing_select'}, \@new_trailing)
+ $attrs->{"${pref}_trailing_select"} = $self->_merge_attr($attrs->{"${pref}_trailing_select"}, \@new_trailing)
if @new_trailing;
}
elsif (@$as < @$sel) {
}
# now see what the result for this pair looks like:
-
if (@$as == @$sel) {
+
# if balanced - treat as a columns entry
- $attrs->{columns} = $self->_merge_attr(
- $attrs->{columns},
+ $attrs->{"${pref}columns"} = $self->_merge_attr(
+ $attrs->{"${pref}columns"},
{ map { $as->[$_] => $sel->[$_] } ( 0 .. $#$as ) }
);
}
else {
# unbalanced - shove in select/as, not subject to deduplication in _resolved_attrs
- $attrs->{select} = $self->_merge_attr($attrs->{select}, $sel);
- $attrs->{as} = $self->_merge_attr($attrs->{as}, $as);
+ $attrs->{"${pref}select"} = $self->_merge_attr($attrs->{"${pref}select"}, $sel);
+ $attrs->{"${pref}as"} = $self->_merge_attr($attrs->{"${pref}as"}, $as);
}
}
- # simplify
- delete $attrs->{$_} for grep { $attrs->{$_} and ! @{$attrs->{$_}} } qw/select as columns/;
}
sub _stack_cond {
my $source = $self->result_source;
my $alias = $attrs->{alias};
- # take care of any selector merging
- $self->_normalize_selection ($attrs);
+ # one last pass of normalization
+ $self->_normalize_selection($attrs);
+
+ # default selection list
+ $attrs->{columns} = [ $source->columns ]
+ unless List::Util::first { exists $attrs->{$_} } qw/columns cols select as _trailing_select/;
+
+ # merge selectors together
+ for (qw/columns select as _trailing_select/) {
+ $attrs->{$_} = $self->_merge_attr($attrs->{$_}, $attrs->{"+$_"})
+ if $attrs->{$_} or $attrs->{"+$_"};
+ }
# disassemble columns
my (@sel, @as);
}
- push @sel, @{$attrs->{_trailing_select}}
+ push @{ $attrs->{select} }, @{$attrs->{_trailing_select}}
if $attrs->{_trailing_select};
# if both page and offset are specified, produce a combined offset
return [$_[0], @{$_[1]}]
},
HASH => sub {
+ return [] if !defined $_[0] and !keys %{$_[1]};
return [ $_[1] ] if !defined $_[0];
return [ $_[0] ] if !keys %{$_[1]};
return [$_[0], $_[1]]
},
HASH => {
SCALAR => sub {
+ return [] if !keys %{$_[0]} and !defined $_[1];
return [ $_[0] ] if !defined $_[1];
return [ $_[1] ] if !keys %{$_[0]};
return [$_[0], $_[1]]
},
ARRAY => sub {
+ return [] if !keys %{$_[0]} and !@{$_[1]};
return [ $_[0] ] if !@{$_[1]};
return $_[1] if !keys %{$_[0]};
return $_[1] if __HM_DEDUP and List::Util::first { $_ eq $_[0] } @{$_[1]};
return [ $_[0], @{$_[1]} ];
},
HASH => sub {
+ return [] if !keys %{$_[0]} and !keys %{$_[1]};
return [ $_[0] ] if !keys %{$_[1]};
return [ $_[1] ] if !keys %{$_[0]};
return [ $_[0] ] if $_[0] eq $_[1];