X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=74fadd9d91eb451bd8d73777db92491c6e129dcd;hb=299d8683826c74c030d6657ab4f21d95a711c228;hp=af2431e972a77703abd46257d1ba427d1acb3c40;hpb=e60dc79fcd4d6318e83584b826526e65048b86a9;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index af2431e..74fadd9 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -13,10 +13,15 @@ use Scalar::Util qw/blessed weaken/; __PACKAGE__->mk_group_accessors('simple' => qw/_connect_info _dbi_connect_info _dbh _sql_maker _sql_maker_opts - _conn_pid _conn_tid disable_sth_caching cursor on_connect_do - transaction_depth unsafe _dbh_autocommit/ + _conn_pid _conn_tid disable_sth_caching on_connect_do + on_disconnect_do transaction_depth unsafe _dbh_autocommit/ ); +__PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor'); + +__PACKAGE__->mk_group_accessors('inherited' => qw/sql_maker_class/); +__PACKAGE__->sql_maker_class('DBIC::SQL::Abstract'); + BEGIN { package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :( @@ -81,6 +86,15 @@ sub select { my ($sql, @ret) = $self->SUPER::select( $table, $self->_recurse_fields($fields), $where, $order, @rest ); + $sql .= + $self->{for} ? + ( + $self->{for} eq 'update' ? ' FOR UPDATE' : + $self->{for} eq 'shared' ? ' FOR SHARE' : + '' + ) : + '' + ; return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql; } @@ -311,7 +325,6 @@ documents DBI-specific methods and behaviors. sub new { my $new = shift->next::method(@_); - $new->cursor("DBIx::Class::Storage::DBI::Cursor"); $new->transaction_depth(0); $new->_sql_maker_opts({}); $new->{_in_dbh_do} = 0; @@ -346,9 +359,30 @@ connection-specific options: =item on_connect_do -This can be set to an arrayref of literal sql statements, which will -be executed immediately after making the connection to the database -every time we [re-]connect. +Specifies things to do immediately after connecting or re-connecting to +the database. Its value may contain: + +=over + +=item an array reference + +This contains SQL statements to execute in order. Each element contains +a string or a code reference that returns a string. + +=item a code reference + +This contains some code to execute. Unlike code references within an +array reference, its return value is ignored. + +=back + +=item on_disconnect_do + +Takes arguments in the same form as L and executes them +immediately before disconnecting from the database. + +Note, this only runs if you explicitly call L on the +storage object. =item disable_sth_caching @@ -480,7 +514,10 @@ sub connect_info { my $last_info = $dbi_info->[-1]; if(ref $last_info eq 'HASH') { $last_info = { %$last_info }; # so delete is non-destructive - for my $storage_opt (qw/on_connect_do disable_sth_caching unsafe/) { + my @storage_option = qw( + on_connect_do on_disconnect_do disable_sth_caching unsafe cursor_class + ); + for my $storage_opt (@storage_option) { if(my $value = delete $last_info->{$storage_opt}) { $self->$storage_opt($value); } @@ -647,6 +684,9 @@ sub disconnect { my ($self) = @_; if( $self->connected ) { + my $connection_do = $self->on_disconnect_do; + $self->_do_connection_actions($connection_do) if ref($connection_do); + $self->_dbh->rollback unless $self->_dbh_autocommit; $self->_dbh->disconnect; $self->_dbh(undef); @@ -665,6 +705,7 @@ sub connected { } else { $self->_verify_pid; + return 0 if !$self->_dbh; } return ($dbh->FETCH('Active') && $dbh->ping); } @@ -677,7 +718,7 @@ sub connected { sub _verify_pid { my ($self) = @_; - return if $self->_conn_pid == $$; + return if defined $self->_conn_pid && $self->_conn_pid == $$; $self->_dbh->{InactiveDestroy} = 1; $self->_dbh(undef); @@ -716,7 +757,8 @@ sub _sql_maker_args { sub sql_maker { my ($self) = @_; unless ($self->_sql_maker) { - $self->_sql_maker(new DBIC::SQL::Abstract( $self->_sql_maker_args )); + my $sql_maker_class = $self->sql_maker_class; + $self->_sql_maker($sql_maker_class->new( $self->_sql_maker_args )); } return $self->_sql_maker; } @@ -738,17 +780,44 @@ sub _populate_dbh { } } - # if on-connect sql statements are given execute them - foreach my $sql_statement (@{$self->on_connect_do || []}) { - $self->debugobj->query_start($sql_statement) if $self->debug(); - $self->_dbh->do($sql_statement); - $self->debugobj->query_end($sql_statement) if $self->debug(); - } + my $connection_do = $self->on_connect_do; + $self->_do_connection_actions($connection_do) if ref($connection_do); $self->_conn_pid($$); $self->_conn_tid(threads->tid) if $INC{'threads.pm'}; } +sub _do_connection_actions { + my $self = shift; + my $connection_do = shift; + + if (ref $connection_do eq 'ARRAY') { + $self->_do_query($_) foreach @$connection_do; + } + elsif (ref $connection_do eq 'CODE') { + $connection_do->(); + } + + return $self; +} + +sub _do_query { + my ($self, $action) = @_; + + if (ref $action eq 'CODE') { + $action = $action->($self); + $self->_do_query($_) foreach @$action; + } + else { + my @to_run = (ref $action eq 'ARRAY') ? (@$action) : ($action); + $self->_query_start(@to_run); + $self->_dbh->do(@to_run); + $self->_query_end(@to_run); + } + + return $self; +} + sub _connect { my ($self, @info) = @_; @@ -770,7 +839,7 @@ sub _connect { $dbh = DBI->connect(@info); } - if(!$self->unsafe) { + if($dbh && !$self->unsafe) { my $weak_self = $self; weaken($weak_self); $dbh->{HandleError} = sub { @@ -864,6 +933,40 @@ sub _prep_for_execute { return ($sql, \@bind); } +sub _fix_bind_params { + my ($self, @bind) = @_; + + ### Turn @bind from something like this: + ### ( [ "artist", 1 ], [ "cdid", 1, 3 ] ) + ### to this: + ### ( "'1'", "'1'", "'3'" ) + return + map { + if ( defined( $_ && $_->[1] ) ) { + map { qq{'$_'}; } @{$_}[ 1 .. $#$_ ]; + } + else { q{'NULL'}; } + } @bind; +} + +sub _query_start { + my ( $self, $sql, @bind ) = @_; + + if ( $self->debug ) { + @bind = $self->_fix_bind_params(@bind); + $self->debugobj->query_start( $sql, @bind ); + } +} + +sub _query_end { + my ( $self, $sql, @bind ) = @_; + + if ( $self->debug ) { + @bind = $self->_fix_bind_params(@bind); + $self->debugobj->query_end( $sql, @bind ); + } +} + sub _dbh_execute { my ($self, $dbh, $op, $extra_bind, $ident, $bind_attributes, @args) = @_; @@ -873,11 +976,7 @@ sub _dbh_execute { my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args); - if ($self->debug) { - my @debug_bind = - map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @$bind; - $self->debugobj->query_start($sql, @debug_bind); - } + $self->_query_start( $sql, @$bind ); my $sth = $self->sth($sql,$op); @@ -904,11 +1003,7 @@ sub _dbh_execute { my $rv = $sth->execute(); $self->throw_exception($sth->errstr) if !$rv; - if ($self->debug) { - my @debug_bind = - map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @$bind; - $self->debugobj->query_end($sql, @debug_bind); - } + $self->_query_end( $sql, @$bind ); return (wantarray ? ($rv, $sth, @$bind) : $rv); } @@ -940,10 +1035,7 @@ sub insert_bulk { @colvalues{@$cols} = (0..$#$cols); my ($sql, @bind) = $self->sql_maker->insert($table, \%colvalues); - if ($self->debug) { - my @debug_bind = map { defined $_->[1] ? qq{$_->[1]} : q{'NULL'} } @bind; - $self->debugobj->query_start($sql, @debug_bind); - } + $self->_query_start( $sql, @bind ); my $sth = $self->sth($sql); # @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args @@ -981,10 +1073,7 @@ sub insert_bulk { my $rv = $sth->execute_array({ArrayTupleStatus => $tuple_status}); $self->throw_exception($sth->errstr) if !$rv; - if ($self->debug) { - my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind; - $self->debugobj->query_end($sql, @debug_bind); - } + $self->_query_end( $sql, @bind ); return (wantarray ? ($rv, $sth, @bind) : $rv); } @@ -1009,9 +1098,15 @@ sub delete { sub _select { my ($self, $ident, $select, $condition, $attrs) = @_; my $order = $attrs->{order_by}; + if (ref $condition eq 'SCALAR') { $order = $1 if $$condition =~ s/ORDER BY (.*)$//i; } + + my $for = delete $attrs->{for}; + my $sql_maker = $self->sql_maker; + local $sql_maker->{for} = $for; + if (exists $attrs->{group_by} || $attrs->{having}) { $order = { group_by => $attrs->{group_by}, @@ -1032,6 +1127,7 @@ sub _select { $attrs->{rows} = 2**48 if not defined $attrs->{rows} and defined $attrs->{offset}; push @args, $attrs->{rows}, $attrs->{offset}; } + return $self->_execute(@args); } @@ -1064,7 +1160,7 @@ Handle a SQL select statement. sub select { my $self = shift; my ($ident, $select, $condition, $attrs) = @_; - return $self->cursor->new($self, \@_, $attrs); + return $self->cursor_class->new($self, \@_, $attrs); } sub select_single { @@ -1417,14 +1513,14 @@ sub deploy { next if($line =~ /^BEGIN TRANSACTION/m); next if($line =~ /^COMMIT/m); next if $line =~ /^\s+$/; # skip whitespace only - $self->debugobj->query_start($line) if $self->debug; + $self->_query_start($line); eval { $self->dbh->do($line); # shouldn't be using ->dbh ? }; if ($@) { warn qq{$@ (running "${line}")}; } - $self->debugobj->query_end($line) if $self->debug; + $self->_query_end($line); } } }