Class::DBI::Plugin::Iterator 0.06

Class::DBI::Plugin::Iterator 0.06

 makamakaさんの指摘による、先読み部分を変更しました。

 他の変更点は、allメソッドの実装方法の変更とか、count時のSQLがエラーになった場合の対策あたり。

 とりあえず当初考えてたことは一通り実装したので、あとはドキュメントを英語に直してCPANに登録するだけですが……。

うまく動かないSQLについて

 ううっ、素早いご指摘。HAVING句があるとまずそうなのはGROUP BYを判別する正規表現を書いていたときに気付いてはいたんですけどね……。

 他にもSELECT DISTINCTだとうまくない気がしてますし、UNIONを使っていてもまずそうです。

Class::DBI::Plugin::Iterator::subquery

package Class::DBI::Plugin::Iterator::subquery;
use strict;
use base qw/Class::DBI::Plugin::Iterator/;

sub count {
    my $self = shift;
    return $self->{_count} if defined $self->{_count};

    my $sql = sprintf 'SELECT COUNT(*) FROM ( %s ) AS __GROUP_BY__', $self->sql;

    eval {
        my $sth = $self->class->db_Main->prepare($sql);
        $sth->execute(@{$self->{_args}});
        $self->{_count} = $sth->fetch->[0];
        $sth->finish;
    };
    if ($@) {
        my $itr = $self->all;
        $self->{_count} = $itr->count;
    }

    $self->{_count};
}

1;

――という感じではどうでしょうかね?

 Plugin::Iteratorはこんな感じで処理してます。

sub count {
    my $self = shift;
    return $self->{_count} if defined $self->{_count};

    my $select_from_regexp = qr/(?si)^\s*SELECT\s+.+?\s+FROM\s+/;
    my $group_check_regexp = qr/(?si)\s+GROUP\s+BY\s+(.+?)(\s+HAVING\s+.+?)?(\s+ORDER\s+BY\s+.+?)?$/;
    my $order_check_regexp = qr/(?si)\s+ORDER\s+BY\s+.+$/;

    my $sql = $self->sql;
    if ($sql =~ $group_check_regexp) {
        unless ($2) {
            my $group_by = $1;
            $sql =~ s/$select_from_regexp/SELECT COUNT(DISTINCT $group_by) FROM /;
            $sql =~ s/$group_check_regexp//;
        }
        else {
            $sql = sprintf 'SELECT COUNT(*) FROM ( %s ) AS __GROUP_BY__', $sql;
        }
    }
    else{
        $sql =~ s/$select_from_regexp/SELECT COUNT(*) FROM /;
        $sql =~ s/$order_check_regexp//;
    }

    eval {
        my $sth = $self->class->db_Main->prepare($sql);
        $sth->execute(@{$self->{_args}});
        $self->{_count} = $sth->fetch->[0];
        $sth->finish;
    };
    if ($@) {
        warn "using \$self->all->count\n";
        $self->{_count} = $self->all->count;
    }

    $self->{_count};
}