my $new_attrs = { %{$our_attrs}, %{$attrs} };
# merge new attrs into inherited
- foreach my $key (qw/join prefetch +select +as/) {
+ foreach my $key (qw/join prefetch +select +as bind/) {
next unless exists $attrs->{$key};
$new_attrs->{$key} = $self->_merge_attr($our_attrs->{$key}, $attrs->{$key});
}
if (ref $query eq 'ARRAY') {
foreach my $subquery (@$query) {
next unless ref $subquery; # -or
-# warn "ARRAY: " . Dumper $subquery;
$collapsed = $self->_collapse_query($subquery, $collapsed);
}
}
elsif (ref $query eq 'HASH') {
if (keys %$query and (keys %$query)[0] eq '-and') {
foreach my $subquery (@{$query->{-and}}) {
-# warn "HASH: " . Dumper $subquery;
$collapsed = $self->_collapse_query($subquery, $collapsed);
}
}
else {
-# warn "LEAF: " . Dumper $query;
foreach my $col (keys %$query) {
my $value = $query->{$col};
$collapsed->{$col}{$value}++;
with to find the number of elements. If passed arguments, does a search
on the resultset and counts the results of that.
-Note: When using C<count> with C<group_by>, L<DBIx::Class> emulates C<GROUP BY>
-using C<COUNT( DISTINCT( columns ) )>. Some databases (notably SQLite) do
-not support C<DISTINCT> with multiple columns. If you are using such a
-database, you should only use columns from the main table in your C<group_by>
-clause.
-
=cut
sub count {
sub _count { # Separated out so pager can get the full count
my $self = shift;
- my $select = { count => '*' };
-
my $attrs = { %{$self->_resolved_attrs} };
- if (my $group_by = delete $attrs->{group_by}) {
- delete $attrs->{having};
- my @distinct = (ref $group_by ? @$group_by : ($group_by));
- # todo: try CONCAT for multi-column pk
- my @pk = $self->result_source->primary_columns;
- if (@pk == 1) {
- my $alias = $attrs->{alias};
- foreach my $column (@distinct) {
- if ($column =~ qr/^(?:\Q${alias}.\E)?$pk[0]$/) {
- @distinct = ($column);
- last;
- }
- }
- }
- $select = { count => { distinct => \@distinct } };
+ if (my $group_by = $attrs->{group_by}) {
+ delete $attrs->{order_by};
+
+ $attrs->{select} = $group_by;
+ $attrs->{from} = [ { 'mesub' => (ref $self)->new($self->result_source, $attrs)->cursor->as_query } ];
+ delete $attrs->{where};
}
- $attrs->{select} = $select;
+ $attrs->{select} = { count => '*' };
$attrs->{as} = [qw/count/];
- # offset, order by and page are not needed to count. record_filter is cdbi
- delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
+ # offset, order by, group by, where and page are not needed to count. record_filter is cdbi
+ delete $attrs->{$_} for qw/rows offset order_by group_by page pager record_filter/;
my $tmp_rs = (ref $self)->new($self->result_source, $attrs);
my ($count) = $tmp_rs->cursor->next;
# No-op. No condition, we're updating/deleting everything
return $cond unless ref $full_cond;
- foreach my $pk ($self->result_source->primary_columns) {
- $cond->{$pk} = { IN => $self->get_column($pk)->as_query({ skip_parens => 1 }) };
+ # Some attributes when present require a subquery
+ # This might not work on some database (mysql), but...
+ # it won't work without the subquery either so who cares
+ if (grep { defined $self->{attrs}{$_} } qw/join seen_join from rows group_by/) {
+
+ foreach my $pk ($self->result_source->primary_columns) {
+ $cond->{$pk} = { IN => $self->get_column($pk)->as_query };
+ }
+
+ return $cond;
}
+ if (ref $full_cond eq 'ARRAY') {
+ $cond = [
+ map {
+ my %hash;
+ foreach my $key (keys %{$_}) {
+ $key =~ /([^.]+)$/;
+ $hash{$1} = $_->{$key};
+ }
+ \%hash;
+ } @{$full_cond}
+ ];
+ }
+ elsif (ref $full_cond eq 'HASH') {
+ if ((keys %{$full_cond})[0] eq '-and') {
+ $cond->{-and} = [];
+ my @cond = @{$full_cond->{-and}};
+ for (my $i = 0; $i < @cond; $i++) {
+ my $entry = $cond[$i];
+ my $hash;
+ if (ref $entry eq 'HASH') {
+ $hash = $self->_cond_for_update_delete($entry);
+ }
+ else {
+ $entry =~ /([^.]+)$/;
+ $hash->{$1} = $cond[++$i];
+ }
+ push @{$cond->{-and}}, $hash;
+ }
+ }
+ else {
+ foreach my $key (keys %{$full_cond}) {
+ $key =~ /([^.]+)$/;
+ $cond->{$1} = $full_cond->{$key};
+ }
+ }
+ }
+ else {
+ $self->throw_exception("Can't update/delete on resultset with condition unless hash or array");
+ }
+
return $cond;
}
if (ref $cond eq 'ARRAY') {
foreach my $subcond (@$cond) {
next unless ref $subcond; # -or
-# warn "ARRAY: " . Dumper $subcond;
$collapsed = $self->_collapse_cond($subcond, $collapsed);
}
}
elsif (ref $cond eq 'HASH') {
if (keys %$cond and (keys %$cond)[0] eq '-and') {
foreach my $subcond (@{$cond->{-and}}) {
-# warn "HASH: " . Dumper $subcond;
$collapsed = $self->_collapse_cond($subcond, $collapsed);
}
}
else {
-# warn "LEAF: " . Dumper $cond;
foreach my $col (keys %$cond) {
my $value = $cond->{$col};
$collapsed->{$col} = $value;
=over 4
-=item Arguments: \%opts
+=item Arguments: none
=item Return Value: \[ $sql, @bind ]
B<NOTE>: This feature is still experimental.
-The query returned will be surrounded by parentheses, e.g:
-
- ( SELECT cdid FROM cd WHERE title LIKE '%Hits%' )
-
-This behaviour can be changed by passing special options:
-
- $rs->get_column('cdid')->as_query({ skip_parens => 1 });
-
=cut
sub as_query { return shift->cursor->as_query(@_) }