#
# This code will remove non-selecting/non-restricting joins from
-# {from} specs, aiding the RDBMS query optimizer.
+# {from} specs, aiding the RDBMS query optimizer
#
sub _prune_unused_joins {
- my $self = shift;
+ my ($self) = shift;
+
+ my ($from, $select, $where, $attrs) = @_;
- my $from = shift;
if (ref $from ne 'ARRAY' || ref $from->[0] ne 'HASH' || ref $from->[1] ne 'ARRAY') {
return $from; # only standard {from} specs are supported
}
- my $aliastypes = $self->_resolve_aliastypes_from_select_args($from, @_);
+ my $aliastypes = $self->_resolve_aliastypes_from_select_args(@_);
+
+ # a grouped set will not be affected by amount of rows. Thus any
+ # {multiplying} joins can go
+ delete $aliastypes->{multiplying} if $attrs->{group_by};
+
my @newfrom = $from->[0]; # FROM head is always present
my %need_joins = (map { %{$_||{}} } (values %$aliastypes) );
for my $j (@{$from}[1..$#$from]) {
push @newfrom, $j if (
- ! $j->[0]{-alias} # legacy crap
+ (! $j->[0]{-alias}) # legacy crap
||
$need_joins{$j->[0]{-alias}}
);
return \@newfrom;
}
-
#
# This is the code producing joined subqueries like:
# SELECT me.*, other.* FROM ( SELECT me.* FROM ... ) JOIN other ON ...
}
# construct the inner $from for the subquery
+ # we need to prune first, because this will determine if we need a group_by below
my $inner_from = $self->_prune_unused_joins ($from, $inner_select, $where, $inner_attrs);
# if a multi-type join was needed in the subquery - add a group_by to simulate the
my $group_by_sql = $sql_maker->_order_by({
map { $_ => $attrs->{$_} } qw/group_by having/
});
- my @order_by_chunks = (map
- { ref $_ ? $_->[0] : $_ }
- $sql_maker->_order_by_chunks ($attrs->{order_by})
- );
+ my @order_by_chunks = ($self->_parse_order_by ($attrs->{order_by}) );
# match every alias to the sql chunks above
for my $alias (keys %$alias_list) {
for my $type (keys %$aliases_by_type) {
for my $alias (keys %{$aliases_by_type->{$type}}) {
$aliases_by_type->{$type}{$_} = 1
- for (@{ $alias_list->{$alias}{-join_path} || [] });
+ for (map { keys %$_ } @{ $alias_list->{$alias}{-join_path} || [] });
}
}
# anyway, and deep cloning is just too fucking expensive
# So replace the first hashref in the node arrayref manually
my @new_from = ($from->[0]);
- my $sw_idx = { map { $_ => 1 } @$switch_branch };
+ my $sw_idx = { map { values %$_ => 1 } @$switch_branch };
for my $j (@{$from}[1 .. $#$from]) {
my $jalias = $j->[0]{-alias};
for (my $i = 0; $i < @cond; $i++) {
my $entry = $cond[$i];
my $hash;
- if (ref $entry eq 'HASH') {
+ my $ref = ref $entry;
+ if ($ref eq 'HASH' or $ref eq 'ARRAY') {
$hash = $self->_strip_cond_qualifiers($entry);
}
- else {
+ elsif (! $ref) {
$entry =~ /([^.]+)$/;
$hash->{$1} = $cond[++$i];
}
+ else {
+ $self->throw_exception ("_strip_cond_qualifiers() is unable to handle a condition reftype $ref");
+ }
push @{$cond->{-and}}, $hash;
}
}
return $cond;
}
+sub _parse_order_by {
+ my ($self, $order_by) = @_;
+
+ return scalar $self->sql_maker->_order_by_chunks ($order_by)
+ unless wantarray;
+
+ my $sql_maker = $self->sql_maker;
+ local $sql_maker->{quote_char}; #disable quoting
+ my @chunks;
+ for my $chunk (map { ref $_ ? @$_ : $_ } ($sql_maker->_order_by_chunks ($order_by) ) ) {
+ $chunk =~ s/\s+ (?: ASC|DESC ) \s* $//ix;
+ push @chunks, $chunk;
+ }
+
+ return @chunks;
+}
1;