Alzabo
view release on metacpan or search on metacpan
lib/Alzabo/Driver.pm view on Meta::CPAN
package Alzabo::Driver;
use strict;
use vars qw($VERSION);
use Alzabo::Exceptions;
use Class::Factory::Util;
use DBI;
use Params::Validate qw( validate validate_pos UNDEF SCALAR ARRAYREF );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = 2.0;
1;
sub new
{
shift;
my %p = @_;
eval "use Alzabo::Driver::$p{rdbms}";
Alzabo::Exception::Eval->throw( error => $@ ) if $@;
my $self = "Alzabo::Driver::$p{rdbms}"->new(@_);
$self->{schema} = $p{schema};
return $self;
}
sub available { __PACKAGE__->subclasses }
sub _ensure_valid_dbh
{
my $self = shift;
unless ( $self->{dbh} )
{
my $sub = (caller(1))[3];
Alzabo::Exception::Driver->throw( error => "Cannot call $sub before calling connect." );
}
$self->{dbh} = $self->_dbi_connect( $self->{connect_params} )
if $$ != $self->{connect_pid};
}
sub quote
{
my $self = shift;
$self->_ensure_valid_dbh;
return $self->{dbh}->quote(@_);
}
sub quote_identifier
{
my $self = shift;
$self->_ensure_valid_dbh;
return $self->{dbh}->quote_identifier(@_);
}
sub rows
{
my $self = shift;
$self->_ensure_valid_dbh;
lib/Alzabo/Driver.pm view on Meta::CPAN
$self->_ensure_valid_dbh;
my $sth = $self->_prepare_and_execute(%p);
my %hash;
eval
{
my @row = $sth->fetchrow_array;
@hash{ @{ $sth->{NAME_uc} } } = @row if @row;
$sth->finish;
};
if ($@)
{
my @bind = exists $p{bind} ? ( ref $p{bind} ? $p{bind} : [$p{bind}] ) : ();
Alzabo::Exception::Driver->throw( error => $@,
sql => $p{sql},
bind => \@bind );
}
return %hash;
}
sub column
{
my $self = shift;
my %p = @_;
$self->_ensure_valid_dbh;
my $sth = $self->_prepare_and_execute(%p);
my @data;
eval
{
my @row;
$sth->bind_columns( \ (@row[ 0..$#{ $sth->{NAME_lc} } ] ) );
push @data, $row[0] while ($sth->fetch);
$sth->finish;
};
if ($@)
{
my @bind = exists $p{bind} ? ( ref $p{bind} ? $p{bind} : [$p{bind}] ) : ();
Alzabo::Exception::Driver->throw( error => $@,
sql => $p{sql},
bind => \@bind );
}
return wantarray ? @data : $data[0];
}
use constant _PREPARE_AND_EXECUTE_SPEC => { sql => { type => SCALAR },
bind => { type => UNDEF | SCALAR | ARRAYREF,
optional => 1 },
};
sub _prepare_and_execute
{
my $self = shift;
validate( @_, _PREPARE_AND_EXECUTE_SPEC );
my %p = @_;
Alzabo::Exception::Driver->throw( error => "Attempt to access the database without database handle. Was ->connect called?" )
unless $self->{dbh};
my @bind = exists $p{bind} ? ( ref $p{bind} ? @{ $p{bind} } : $p{bind} ) : ();
my $sth;
eval
{
$sth = $self->{dbh}->prepare( $p{sql} );
$sth->execute(@bind);
};
if ($@)
{
Alzabo::Exception::Driver->throw( error => $@,
sql => $p{sql},
bind => \@bind );
}
return $sth;
}
sub do
{
my $self = shift;
my %p = @_;
$self->_ensure_valid_dbh;
my $sth = $self->_prepare_and_execute(%p);
my $rows;
eval
{
$rows = $sth->rows;
$sth->finish;
};
if ($@)
{
my @bind = exists $p{bind} ? ( ref $p{bind} ? $p{bind} : [$p{bind}] ) : ();
Alzabo::Exception::Driver->throw( error => $@,
sql => $p{sql},
bind => \@bind );
}
return $rows;
}
sub tables
{
my $self = shift;
$self->_ensure_valid_dbh;
my @t = eval { $self->{dbh}->tables( '', '', '%', 'table' ); };
Alzabo::Exception::Driver->throw( error => $@ ) if $@;
return @t;
}
lib/Alzabo/Driver.pm view on Meta::CPAN
$self->_ensure_valid_dbh;
return Alzabo::DriverStatement->new( dbh => $self->{dbh},
@_ );
}
sub statement_no_execute
{
my $self = shift;
$self->_ensure_valid_dbh;
return Alzabo::DriverStatement->new_no_execute( dbh => $self->{dbh},
@_ );
}
sub func
{
my $self = shift;
$self->_ensure_valid_dbh;
my @r;
eval
{
if (wantarray)
{
@r = $self->{dbh}->func(@_);
return @r;
}
else
{
$r[0] = $self->{dbh}->func(@_);
return $r[0];
}
};
Alzabo::Exception::Driver->throw( error => $self->{dbh}->errstr )
if $self->{dbh}->errstr;
}
sub DESTROY
{
my $self = shift;
$self->disconnect;
}
sub disconnect
{
my $self = shift;
$self->{dbh}->disconnect if $self->{dbh};
delete $self->{dbh};
}
sub handle
{
my $self = shift;
if (@_)
{
validate_pos( @_, { isa => 'DBI::db' } );
$self->{dbh} = shift;
}
return $self->{dbh};
}
sub rdbms_version
{
shift()->_virtual;
}
sub connect
{
shift()->_virtual;
}
sub supports_referential_integrity
{
shift()->_virtual;
}
sub create_database
{
shift()->_virtual;
}
sub drop_database
{
shift()->_virtual;
}
sub next_sequence_number
{
shift()->_virtual;
}
sub begin_work
{
my $self = shift;
$self->_ensure_valid_dbh;
$self->{tran_count} = 0 unless defined $self->{tran_count};
$self->{dbh}->begin_work if $self->{dbh}->{AutoCommit};
$self->{tran_count}++;
}
sub rollback
{
my $self = shift;
$self->_ensure_valid_dbh;
$self->{tran_count} = undef;
eval { $self->{dbh}->rollback unless $self->{dbh}->{AutoCommit} };
Alzabo::Exception::Driver->throw( error => $@ ) if $@;
lib/Alzabo/Driver.pm view on Meta::CPAN
sub commit
{
my $self = shift;
$self->_ensure_valid_dbh;
my $callee = (caller(1))[3];
# More commits than begin_tran. Not correct.
if ( defined $self->{tran_count} )
{
$self->{tran_count}--;
}
else
{
my $caller = (caller(1))[3];
require Carp;
Carp::cluck( "$caller called commit without corresponding begin_work call\n" );
}
# Don't actually commit until we reach 'uber-commit'
return if $self->{tran_count};
unless ( $self->{dbh}->{AutoCommit} )
{
$self->{dbh}->commit;
}
$self->{dbh}->{AutoCommit} = 1;
$self->{tran_count} = undef;
}
sub get_last_id
{
shift()->_virtual;
}
sub driver_id
{
shift()->_virtual;
}
sub _virtual
{
my $self = shift;
my $sub = (caller(1))[3];
Alzabo::Exception::VirtualMethod->throw( error =>
"$sub is a virtual method and must be subclassed in " . ref $self );
}
package Alzabo::DriverStatement;
use strict;
use vars qw($VERSION);
use Alzabo::Exceptions;
use DBI;
use Params::Validate qw( validate UNDEF SCALAR ARRAYREF );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = '0.1';
sub new
{
my $self = shift->new_no_execute(@_);
$self->execute;
return $self;
}
use constant NEW_NO_EXECUTE_SPEC => { dbh => { can => 'prepare' },
sql => { type => SCALAR },
bind => { type => SCALAR | ARRAYREF,
optional => 1 },
limit => { type => UNDEF | ARRAYREF,
optional => 1 },
};
sub new_no_execute
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p = validate( @_, NEW_NO_EXECUTE_SPEC );
my $self = bless {}, $class;
$self->{limit} = $p{limit} ? $p{limit}[0] : 0;
$self->{offset} = $p{limit} && $p{limit}[1] ? $p{limit}[1] : 0;
$self->{rows_fetched} = 0;
eval
{
$self->{sth} = $p{dbh}->prepare( $p{sql} );
$self->{bind} = exists $p{bind} ? ( ref $p{bind} ? $p{bind} : [ $p{bind} ] ) : [];
};
Alzabo::Exception::Driver->throw( error => $@,
sql => $p{sql},
bind => $self->{bind} ) if $@;
return $self;
}
sub execute
{
my $self = shift;
eval
{
$self->{sth}->finish if $self->{sth}->{Active};
$self->{rows_fetched} = 0;
$self->{sth}->execute( @_ ? @_ : @{ $self->{bind} } );
$self->{result} = [];
$self->{count} = 0;
$self->{sth}->bind_columns
( \ ( @{ $self->{result} }[ 0..$#{ $self->{sth}->{NAME_lc} } ] ) );
};
Alzabo::Exception::Driver->throw( error => $@,
sql => $self->{sth}{Statement},
bind => $self->{bind} ) if $@;
}
sub execute_no_result
{
my $self = shift;
eval
{
$self->{sth}->execute(@_);
};
Alzabo::Exception::Driver->throw( error => $@,
sql => $self->{sth}{Statement},
bind => $self->{bind} ) if $@;
}
sub next
{
my $self = shift;
my %p = @_;
( run in 0.840 second using v1.01-cache-2.11-cpan-5837b0d9d2c )