Alzabo

 view release on metacpan or  search on metacpan

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


    $self->{maintain_integrity} = $val if defined $val;
}

sub set_quote_identifiers
{
    my $self = shift;
    my $val = shift;

    $self->{quote_identifiers} = $val if defined $val;
}

sub connect
{
    my $self = shift;

    my %p;
    $p{user} = $self->user if defined $self->user;
    $p{password} = $self->password if defined $self->password;
    $p{host} = $self->host if defined $self->host;
    $p{port} = $self->port if defined $self->port;
    $self->driver->connect( %p, @_ );

#    $self->set_referential_integrity( ! $self->driver->supports_referential_integrity );
}

sub disconnect
{
    my $self = shift;

    $self->driver->disconnect;
}

sub one_row
{
    # could be replaced with something potentially more efficient
    return shift->join(@_)->next;
}

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

sub join
{
    my $self = shift;
    my %p = validate( @_, JOIN_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} };
    }

    if ( $p{distinct} )
    {
        $p{distinct} =
            Alzabo::Utils::is_arrayref( $p{distinct} ) ? $p{distinct} : [ $p{distinct} ];
    }

    if ( $p{order_by} )
    {
        $p{order_by} =
            Alzabo::Utils::is_arrayref( $p{order_by} )
            ? $p{order_by}
            : $p{order_by}
            ? [ $p{order_by} ]
            : undef;
    }

    # We go in this order:  $p{select}, $p{distinct}, @tables
    my @select_tables = ( $p{select} ?
                          ( Alzabo::Utils::is_arrayref( $p{select} ) ?
                            @{ $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} }
                       );

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

    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};

    Alzabo::Runtime::process_group_by_clause( $sql, $p{group_by} )
        if exists $p{group_by};

    Alzabo::Runtime::process_having_clause( $sql, $p{having} )
        if exists $p{having};

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

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

    return $sql;
}

use constant _JOIN_ALL_TABLES_SPEC => { join => { type => ARRAYREF },
                                        sql  => { isa => 'Alzabo::SQLMaker' } };

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

    my @from;
    my @joins;

    # outer join given as only join
    $p{join} = [ $p{join} ] unless ref $p{join}->[0];

    # A structure like:
    #
    # [ [ $t_1 => $t_2 ],
    #   [ $t_1 => $t_3, $fk ],
    #   [ left_outer_join => $t_3 => $t_4 ],
    #   [ left_outer_join => $t_3 => $t_5, undef, [ $where_clause ] ]
    #
    if ( Alzabo::Utils::is_arrayref( $p{join}->[0] ) )
    {
        my %map;
        my %tables;

        foreach my $set ( @{ $p{join} } )
        {
            # we take some care not to change the contents of $set,
            # because the caller may reuse the variable being
            # referenced, and changes here could break that.

            # XXX - improve
            params_exception
                'The table map must contain only two tables per array reference'
                    if @$set > 5;

            my @tables;
            if ( ! ref $set->[0] )
            {
                $set->[0] =~ /^(right|left|full)_outer_join$/i
                    or params_exception "Invalid join type: $set->[0]";

                @tables = @$set[1,2];

                push @from, [ $1, @tables, @$set[3, 4] ];
            }
            else
            {
                @tables = @$set[0,1];

                push @from, grep { ! exists $tables{ $_->alias_name } } @tables;
                push @joins, [ @tables, $set->[2] ];
            }

            # Track the tables we've seen
            @tables{ $tables[0]->alias_name, $tables[1]->alias_name } = (1, 1);

            # Track their relationships
            push @{ $map{ $tables[0]->alias_name } }, $tables[1]->alias_name;
            push @{ $map{ $tables[1]->alias_name } }, $tables[0]->alias_name;
        }

        # just get one key to start with
        my ($key) = (each %tables)[0];
        delete $tables{$key};
        my @t = @{ delete $map{$key} };



( run in 0.564 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )