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 )