Data-ObjectDriver

 view release on metacpan or  search on metacpan

lib/Data/ObjectDriver/Driver/DBI.pm  view on Meta::CPAN

# $Id$

package Data::ObjectDriver::Driver::DBI;
use strict;
use warnings;

use base qw( Data::ObjectDriver Class::Accessor::Fast );

use DBI;
use Carp ();
use Data::ObjectDriver::Errors;
use Data::ObjectDriver::SQL;
use Data::ObjectDriver::Driver::DBD;
use Data::ObjectDriver::Iterator;
use Scalar::Util 'blessed';

my $ForkSafe = _is_fork_safe();
my %Handles;

sub _is_fork_safe {
    return if exists $ENV{DOD_FORK_SAFE} and !$ENV{DOD_FORK_SAFE};
    eval { require POSIX::AtFork; 1 } or return;
    eval { require Scalar::Util; Scalar::Util->import('weaken'); 1 } or return;
    return 1;
}

__PACKAGE__->mk_accessors(qw( dsn username password connect_options get_dbh dbd prefix reuse_dbh force_no_prepared_cache));


sub init {
    my $driver = shift;
    my %param = @_;
    for my $key (keys %param) {
        $driver->$key($param{$key});
    }
    if(!exists $param{dbd}) {
        ## Create a DSN-specific driver (e.g. "mysql").
        my $type;
        if (my $dsn = $driver->dsn) {
            ($type) = $dsn =~ /^dbi:(\w*)/i;
        } elsif (my $dbh = $driver->dbh) {
            $type = $dbh->{Driver}{Name};
        } elsif (my $getter = $driver->get_dbh) {
## Ugly. Shouldn't have to connect just to get the driver name.
            my $dbh = $getter->();
            $type = $dbh->{Driver}{Name};
        }
        $driver->dbd(Data::ObjectDriver::Driver::DBD->new($type));
    }

    if ($ForkSafe) {
        # Purge cached handles
        weaken(my $driver_weaken = $driver);
        POSIX::AtFork->add_to_child(sub {
            return unless $driver_weaken;
            $driver_weaken->{dbh} = undef;
            %Handles = ();
        });
    }

    $driver;
}

sub generate_pk {
    my $driver = shift;
    if (my $generator = $driver->pk_generator) {
        return $generator->(@_);
    }
}

# Some versions of SQLite require the undefing to finalise properly
sub _close_sth {
    my $sth = shift;
    $sth->finish;
    undef $sth;
}

# Some versions of SQLite have problems with prepared caching due to finalisation order
sub _prepare_cached {
    my $driver = shift;
    my $dbh    = shift;
    my $sql    = shift;
    return ($driver->dbd->can_prepare_cached_statements)? $dbh->prepare_cached($sql) : $dbh->prepare($sql);
}

sub init_db {
    my $driver = shift;
    my $dbh;
    if ($driver->reuse_dbh) {
        $dbh = $Handles{$driver->dsn};
    }
    unless ($dbh) {
        eval {
            $dbh = DBI->connect($driver->dsn, $driver->username, $driver->password,
                { RaiseError => 1, PrintError => 0, AutoCommit => 1,
                ( $ForkSafe ? ( AutoInactiveDestroy => 1 ) : () ),
                %{$driver->connect_options || {}} })
                or Carp::croak("Connection error: " . $DBI::errstr);
        };
        if ($@) {
            Carp::croak($@);
        }
    }
    if ($driver->reuse_dbh) {
        $Handles{$driver->dsn} = $dbh;
    }
    $driver->dbd->init_dbh($dbh);
    $driver->{__dbh_init_by_driver} = 1;
    return $dbh;
}

sub dbh {
    my $driver = shift;
    if (@_) {
        my $dbh = $driver->{dbh} = shift;
        if (!$dbh && $driver->reuse_dbh) {



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