X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=02e2e7f630ff8f3ac54cb726fbf3aa21afc5a7c7;hb=19f59b4fd16da149a649da5d7cfa5861ea9db35a;hp=eb923d15b89923a8ab088dd3a96cf17133971ca0;hpb=166c656193b56e08b472c675e92b9076aac03a53;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index eb923d1..02e2e7f 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -44,7 +44,14 @@ DBIx::Class::Storage::DBI - DBI storage handler my $schema = MySchema->connect('dbi:SQLite:my.db'); $schema->storage->debug(1); - $schema->dbh_do("DROP TABLE authors"); + + my @stuff = $schema->storage->dbh_do( + sub { + my ($storage, $dbh, @args) = @_; + $dbh->do("DROP TABLE authors"); + }, + @column_list + ); $schema->resultset('Book')->search({ written_on => $schema->storage->datetime_parser(DateTime->now) @@ -112,6 +119,12 @@ mixed together: %extra_attributes, }]; + $connect_info_args = [{ + dbh_maker => sub { DBI->connect (...) }, + %dbi_attributes, + %extra_attributes, + }]; + This is particularly useful for L based applications, allowing the following config (L style): @@ -125,6 +138,10 @@ following config (L style): +The C/C/C combination can be substituted by the +C key whose value is a coderef that returns a connected +L + =back Please note that the L docs recommend that you always explicitly @@ -337,6 +354,12 @@ L # Connect via subref ->connect_info([ sub { DBI->connect(...) } ]); + # Connect via subref in hashref + ->connect_info([{ + dbh_maker => sub { DBI->connect(...) }, + on_connect_do => 'alter session ...', + }]); + # A bit more complicated ->connect_info( [ @@ -407,8 +430,21 @@ sub connect_info { elsif (ref $args[0] eq 'HASH') { # single hashref (i.e. Catalyst config) %attrs = %{$args[0]}; @args = (); - for (qw/password user dsn/) { - unshift @args, delete $attrs{$_}; + if (my $code = delete $attrs{dbh_maker}) { + @args = $code; + + my @ignored = grep { delete $attrs{$_} } (qw/dsn user password/); + if (@ignored) { + carp sprintf ( + 'Attribute(s) %s in connect_info were ignored, as they can not be applied ' + . "to the result of 'dbh_maker'", + + join (', ', map { "'$_'" } (@ignored) ), + ); + } + } + else { + @args = delete @attrs{qw/dsn user password/}; } } else { # otherwise assume dsn/user/password + \%attrs + \%extra_attrs @@ -527,7 +563,7 @@ sub dbh_do { my $self = shift; my $code = shift; - my $dbh = $self->_dbh; + my $dbh = $self->_get_dbh; return $self->$code($dbh, @_) if $self->{_in_dbh_do} || $self->{transaction_depth}; @@ -538,11 +574,6 @@ sub dbh_do { my $want_array = wantarray; eval { - $self->_verify_pid if $dbh; - if(!$self->_dbh) { - $self->_populate_dbh; - $dbh = $self->_dbh; - } if($want_array) { @result = $self->$code($dbh, @_); @@ -589,8 +620,7 @@ sub txn_do { my $tried = 0; while(1) { eval { - $self->_verify_pid if $self->_dbh; - $self->_populate_dbh if !$self->_dbh; + $self->_get_dbh; $self->txn_begin; if($want_array) { @@ -780,6 +810,7 @@ sub dbh { # this is the internal "get dbh or connect (don't check)" method sub _get_dbh { my $self = shift; + $self->_verify_pid if $self->_dbh; $self->_populate_dbh unless $self->_dbh; return $self->_dbh; } @@ -848,10 +879,18 @@ sub _determine_driver { if ($self->_dbh) { # we are connected $driver = $self->_dbh->{Driver}{Name}; } else { - # try to use dsn to not require being connected, the driver may still - # force a connection in _rebless to determine version - ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i; - $started_unconnected = 1; + # if connect_info is a CODEREF, we have no choice but to connect + if (ref $self->_dbi_connect_info->[0] && + Scalar::Util::reftype($self->_dbi_connect_info->[0]) eq 'CODE') { + $self->_populate_dbh; + $driver = $self->_dbh->{Driver}{Name}; + } + else { + # try to use dsn to not require being connected, the driver may still + # force a connection in _rebless to determine version + ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i; + $started_unconnected = 1; + } } my $storage_class = "DBIx::Class::Storage::DBI::${driver}"; @@ -923,7 +962,7 @@ sub _do_query { my @bind = map { [ undef, $_ ] } @do_args; $self->_query_start($sql, @bind); - $self->_dbh->do($sql, $attrs, @do_args); + $self->_get_dbh->do($sql, $attrs, @do_args); $self->_query_end($sql, @bind); } @@ -1078,6 +1117,12 @@ sub txn_begin { sub _dbh_begin_work { my $self = shift; + + # if the user is utilizing txn_do - good for him, otherwise we need to + # ensure that the $dbh is healthy on BEGIN. + # We do this via ->dbh_do instead of ->dbh, so that the ->dbh "ping" + # will be replaced by a failure of begin_work itself (which will be + # then retried on reconnect) if ($self->{_in_dbh_do}) { $self->_dbh->begin_work; } else { @@ -1238,7 +1283,7 @@ sub _dbh_execute { sub _execute { my $self = shift; - $self->dbh_do('_dbh_execute', @_) + $self->dbh_do('_dbh_execute', @_); # retry over disconnects } sub insert { @@ -1280,13 +1325,18 @@ sub insert { ## only prepped once. sub insert_bulk { my ($self, $source, $cols, $data) = @_; + +# redispatch to insert_bulk method of storage we reblessed into, if necessary + if (not $self->_driver_determined) { + $self->_determine_driver; + goto $self->can('insert_bulk'); + } + my %colvalues; my $table = $source->from; @colvalues{@$cols} = (0..$#$cols); my ($sql, @bind) = $self->sql_maker->insert($table, \%colvalues); - $self->_determine_driver; - $self->_query_start( $sql, @bind ); my $sth = $self->sth($sql); @@ -2018,7 +2068,7 @@ sub _dbh_sth { sub sth { my ($self, $sql) = @_; - $self->dbh_do('_dbh_sth', $sql); + $self->dbh_do('_dbh_sth', $sql); # retry over disconnects } sub _dbh_columns_info_for { @@ -2080,7 +2130,7 @@ sub _dbh_columns_info_for { sub columns_info_for { my ($self, $table) = @_; - $self->dbh_do('_dbh_columns_info_for', $table); + $self->_dbh_columns_info_for ($self->_get_dbh, $table); } =head2 last_insert_id @@ -2106,7 +2156,7 @@ EOE sub last_insert_id { my $self = shift; - $self->dbh_do('_dbh_last_insert_id', @_); + $self->_dbh_last_insert_id ($self->_dbh, @_); } =head2 _native_data_type @@ -2301,9 +2351,8 @@ sub create_ddl_dir { %{$sqltargs || {}} }; - $self->throw_exception(q{Can't create a ddl file without SQL::Translator 0.09003: '} - . $self->_check_sqlt_message . q{'}) - if !$self->_check_sqlt_version; + $self->throw_exception("Can't create a ddl file without SQL::Translator: " . $self->_sqlt_version_error) + if !$self->_sqlt_version_ok; my $sqlt = SQL::Translator->new( $sqltargs ); @@ -2445,9 +2494,8 @@ sub deployment_statements { return join('', @rows); } - $self->throw_exception(q{Can't deploy without SQL::Translator 0.09003: '} - . $self->_check_sqlt_message . q{'}) - if !$self->_check_sqlt_version; + $self->throw_exception("Can't deploy without either SQL::Translator or a ddl_dir: " . $self->_sqlt_version_error ) + if !$self->_sqlt_version_ok; # sources needs to be a parser arg, but for simplicty allow at top level # coming in @@ -2529,26 +2577,10 @@ See L sub build_datetime_parser { my $self = shift; my $type = $self->datetime_parser_type(@_); - eval "use ${type}"; - $self->throw_exception("Couldn't load ${type}: $@") if $@; + $self->ensure_class_loaded ($type); return $type; } -{ - my $_check_sqlt_version; # private - my $_check_sqlt_message; # private - sub _check_sqlt_version { - return $_check_sqlt_version if defined $_check_sqlt_version; - eval 'use SQL::Translator "0.09003"'; - $_check_sqlt_message = $@ || ''; - $_check_sqlt_version = !$@; - } - - sub _check_sqlt_message { - _check_sqlt_version if !defined $_check_sqlt_message; - $_check_sqlt_message; - } -} =head2 is_replicating