Alzabo

 view release on metacpan or  search on metacpan

lib/Alzabo/Runtime/Schema.pm  view on Meta::CPAN

                            @{ $p{select} } : $p{select} ) :
                          $p{distinct} ?
                          @{ $p{distinct} } :
                          @tables );

    my $sql = Alzabo::Runtime::sqlmaker( $self, \%p );

    my @select_cols;
    if ( $p{distinct} )
    {
        my %distinct = map { $_ => 1 } @{ $p{distinct} };

        # hack so distinct is not treated as a function, just a
        # bareword in the SQL
        @select_cols = ( 'DISTINCT',
                         map { ( $_->primary_key,
                                 $_->prefetch ?
                                 $_->columns( $_->prefetch ) :
                                 () ) }
                         @{ $p{distinct} }
                       );

        foreach my $t (@select_tables)
        {
            next if $distinct{$t};
            push @select_cols, $t->primary_key;

            push @select_cols, $t->columns( $t->prefetch ) if $t->prefetch;
        }

        if ( $p{order_by} && $sql->distinct_requires_order_by_in_select )
        {
            my %select_cols = map { $_ => 1 } @select_cols;
            push @select_cols, grep { ref } @{ $p{order_by} };
        }

        @select_tables = ( @{ $p{distinct} }, grep { ! $distinct{$_} } @select_tables );
    }
    else
    {
        @select_cols =
            ( map { ( $_->primary_key,
                      $_->prefetch ?
                      $_->columns( $_->prefetch ) :
                      () ) }
              @select_tables );
    }

    $sql->select(@select_cols);

    $self->_join_all_tables( sql => $sql,
                             join => $p{join} );

    Alzabo::Runtime::process_where_clause( $sql, $p{where} ) if exists $p{where};

    Alzabo::Runtime::process_order_by_clause( $sql, $p{order_by} )
        if $p{order_by};

    $sql->limit( ref $p{limit} ? @{ $p{limit} } : $p{limit} ) if $p{limit};

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    my $statement = $self->driver->statement( sql => $sql->sql,
                                              bind => $sql->bind );

    if (@select_tables == 1)
    {
        return Alzabo::Runtime::RowCursor->new
                   ( statement => $statement,
                     table => $select_tables[0]->real_table,
                   );
    }
    else
    {
        return Alzabo::Runtime::JoinCursor->new
                   ( statement => $statement,
                     tables => [ map { $_->real_table } @select_tables ],
                   );
    }
}

sub row_count
{
    my $self = shift;
    my %p = @_;

    return $self->function( select => Alzabo::Runtime::sqlmaker( $self, \%p )->COUNT('*'),
                            %p,
                          );
}

sub function
{
    my $self = shift;
    my %p = @_;

    my $sql = $self->_select_sql(%p);

    my $method =
        Alzabo::Utils::is_arrayref( $p{select} ) && @{ $p{select} } > 1 ? 'rows' : 'column';

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->driver->$method( sql => $sql->sql,
                                   bind => $sql->bind );
}

sub select
{
    my $self = shift;

    my $sql = $self->_select_sql(@_);

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->driver->statement( sql => $sql->sql,
                                     bind => $sql->bind );
}

use constant _SELECT_SQL_SPEC => { join => { type => ARRAYREF | OBJECT,
                                             optional => 1 },
                                   tables => { type => ARRAYREF | OBJECT,
                                               optional => 1 },
                                   select => { type => SCALAR | ARRAYREF | OBJECT,
                                               optional => 1 },
                                   where => { type => ARRAYREF,
                                              optional => 1 },
                                   group_by => { type => ARRAYREF | HASHREF | OBJECT,
                                                 optional => 1 },
                                   order_by => { type => ARRAYREF | HASHREF | OBJECT,
                                                 optional => 1 },
                                   having => { type => ARRAYREF,
                                               optional => 1 },
                                   limit => { type => SCALAR | ARRAYREF,
                                              optional => 1 },
                                   quote_identifiers => { type => BOOLEAN,
                                                          optional => 1 },
                                 };

sub _select_sql
{
    my $self = shift;
    my %p = validate( @_, _SELECT_SQL_SPEC );

    $p{join} ||= delete $p{tables};
    $p{join} = [ $p{join} ] unless Alzabo::Utils::is_arrayref( $p{join} );

    my @tables;

    if ( Alzabo::Utils::is_arrayref( $p{join}->[0] ) )
    {
        # flattens the nested structure and produces a unique set of
        # tables
        @tables = values %{ { map { $_ => $_ }
                              grep { Alzabo::Utils::safe_isa( 'Alzabo::Table', $_ ) }
                              map { @$_ } @{ $p{join} } } };
    }
    else
    {
        @tables = grep { Alzabo::Utils::safe_isa( 'Alzabo::Table', $_ ) } @{ $p{join} };
    }

    my @funcs = Alzabo::Utils::is_arrayref( $p{select} ) ? @{ $p{select} } : $p{select};

    my $sql = ( Alzabo::Runtime::sqlmaker( $self, \%p )->
                select(@funcs) );

    $self->_join_all_tables( sql => $sql,
                             join => $p{join} );

    Alzabo::Runtime::process_where_clause( $sql, $p{where} )
        if exists $p{where};



( run in 1.935 second using v1.01-cache-2.11-cpan-39bf76dae61 )