Alzabo

 view release on metacpan or  search on metacpan

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


    my $vals = delete $p{values} || {};

    my $schema = $self->schema;

    my @pk = $self->primary_key;
    foreach my $pk (@pk)
    {
        unless ( exists $vals->{ $pk->name } )
        {
            if ($pk->sequenced)
            {
                $vals->{ $pk->name } = $schema->driver->next_sequence_number($pk);
            }
            else
            {
                params_exception
                    ( "No value provided for primary key (" .
                      $pk->name . ") and no sequence is available." );
            }
        }
    }

    foreach my $c ($self->columns)
    {
        next if $c->is_primary_key;

        unless ( defined $vals->{ $c->name } || $c->nullable || defined $c->default )
        {
            not_nullable_exception
                ( error => $c->name . " column in " . $self->name . " table cannot be null.",
                  column_name => $c->name,
                  table_name  => $c->table->name,
                  schema_name => $c->table->schema->name,
                );
        }

        delete $vals->{ $c->name }
            if ! defined $vals->{ $c->name } && defined $c->default;
    }

    my @fk;
    @fk = $self->all_foreign_keys
        if $schema->referential_integrity;

    my $sql = ( Alzabo::Runtime::sqlmaker( $self->schema, \%p )->
                insert->
                into($self, $self->columns( sort keys %$vals ) )->
                values( map { $self->column($_) => $vals->{$_} } sort keys %$vals ) );

    my %id;

    $schema->begin_work if @fk;
    eval
    {
        foreach my $fk (@fk)
        {
            $fk->register_insert( map { $_->name => $vals->{ $_->name } } $fk->columns_from );
        }

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

        $self->schema->driver->do( sql => $sql->sql,
                                   bind => $sql->bind );

        foreach my $pk (@pk)
        {
            $id{ $pk->name } = ( defined $vals->{ $pk->name } ?
                                 $vals->{ $pk->name } :
                                 $schema->driver->get_last_id($self) );
        }

        # must come after call to ->get_last_id for MySQL because the
        # id will no longer be available after the transaction ends.
        $schema->commit if @fk;
    };
    if (my $e = $@)
    {
        eval { $schema->rollback };

        rethrow_exception $e;
    }

    return unless defined wantarray || $p{potential_row};

    return $self->row_by_pk( pk => \%id, %p );
}

sub insert_handle
{
    my $self = shift;

    logic_exception "Can't make rows for tables without a primary key"
        unless $self->primary_key;

    my %p = @_;
    %p = validate( @_,
                   { ( map { $_ => { optional => 1 } } keys %p ),
                     columns => { type => ARRAYREF, default => [] },
                     values  => { type => HASHREF, default => {} },
                     quote_identifiers => { type => BOOLEAN,
                                            optional => 1 },
                   },
                 );

    my %func_vals;
    my %static_vals;

    if ( $p{values} )
    {
        my $v = delete $p{values};
        while ( my ( $name, $val ) = each %$v )
        {
            if ( Alzabo::Utils::safe_isa( $val, 'Alzabo::SQLMaker::Function' ) )
            {
                $func_vals{$name} = $val;
            }
            else
            {
                $static_vals{$name} = $val

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

    my $pk_val = $p{pk};

    my @pk = $self->primary_key;

    params_exception
        'Incorrect number of pk values provided.  ' . scalar @pk . ' are needed.'
            if ref $pk_val && @pk != scalar keys %$pk_val;

    if (@pk > 1)
    {
        params_exception
            ( 'Primary key for ' . $self->name . ' is more than one column.' .
              '  Please provide multiple key values as a hashref.' )
                unless ref $pk_val;

        foreach my $pk (@pk)
        {
            params_exception 'No value provided for primary key ' . $pk->name . '.'
                unless defined $pk_val->{ $pk->name };
        }
    }

    return $self->_make_row( %p,
                             table => $self,
                           );
}

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

    my $class = $p{row_class} ? delete $p{row_class} : $self->_row_class;

    return $class->new(%p);
}

sub _row_class { 'Alzabo::Runtime::Row' }

sub row_by_id
{
    my $self = shift;
    my %p = @_;
    validate( @_, { row_id => { type => SCALAR },
                    ( map { $_ => { optional => 1 } } keys %p ) } );

    my (undef, undef, %pk) = split ';:;_;:;', delete $p{row_id};

    return $self->row_by_pk( %p, pk => \%pk );
}

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

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

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

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

    return $self->_cursor_by_sql( %p, sql => $sql );
}

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

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

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

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

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

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

    my @return = $self->schema->driver->one_row( sql => $sql->sql,
                                                 bind => $sql->bind )
        or return;

    my @pk = $self->primary_key;

    my (%pk, %prefetch);

    @pk{ map { $_->name } @pk } = splice @return, 0, scalar @pk;

    # Must be some prefetch pieces
    if (@return)
    {
        @prefetch{ $self->prefetch } = @return;
    }

    return $self->row_by_pk( pk => \%pk,
                             prefetch => \%prefetch,
                           );
}

sub all_rows
{
    my $self = shift;

    my $sql = $self->_make_sql;

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

    return $self->_cursor_by_sql( @_, sql => $sql );
}

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

    logic_exception "Can't make rows for tables without a primary key"
        unless $self->primary_key;

    my $sql = ( Alzabo::Runtime::sqlmaker( $self->schema, \%p )->
                select( $self->primary_key,
                        $self->prefetch ? $self->columns( $self->prefetch ) : () )->
                from( $self ) );

    return $sql;
}

sub _cursor_by_sql
{
    my $self = shift;

    my %p = @_;
    validate( @_, { sql => { isa => 'Alzabo::SQLMaker' },
                    order_by => { type => ARRAYREF | HASHREF | OBJECT,
                                  optional => 1 },
                    limit => { type => SCALAR | ARRAYREF,
                               optional => 1 },
                    ( map { $_ => { optional => 1 } } keys %p ) } );

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

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

    my $statement = $self->schema->driver->statement( sql => $p{sql}->sql,
                                                      bind => $p{sql}->bind,
                                                      limit => $p{sql}->get_limit );

    return Alzabo::Runtime::RowCursor->new( statement => $statement,
                                            table => $self,
                                          );
}

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

    logic_exception "Can't make rows for tables without a primary key"
        unless $self->primary_key;

    my $class = $p{row_class} ? delete $p{row_class} : $self->_row_class;

    return $class->new( %p,
                        state => 'Alzabo::Runtime::RowState::Potential',
                        table => $self,
                      );
}

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

    my $count = Alzabo::Runtime::sqlmaker( $self->schema, \%p )->COUNT('*');

    return $self->function( select => $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->schema->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->schema->driver->statement( sql => $sql->sql,
                                             bind => $sql->bind );
}

use constant
    _SELECT_SQL_SPEC => { select => { type => SCALAR | ARRAYREF | OBJECT },
                          where  => { type => ARRAYREF | OBJECT,
                                      optional => 1 },
                          order_by => { type => ARRAYREF | HASHREF | OBJECT,
                                        optional => 1 },
                          group_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 );

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

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

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

sub set_prefetch
{
    my $self = shift;

    $self->{prefetch} = $self->_canonize_prefetch(@_);
}

sub _canonize_prefetch
{
    my $self = shift;



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