X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=6e277256900d8adfc3cf0ad9f257a63adc57de51;hb=fed966517e558cfc9ee505d44add2d04e77a7d3d;hp=a6d50c9710e519b87cab7d4a62f2baa0b57a0fc3;hpb=75d079145a507a0e5ff89b2676d383f4fd1a5511;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index a6d50c9..6e27725 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -18,6 +18,7 @@ use base qw/SQL::Abstract::Limit/; sub select { my ($self, $table, $fields, $where, $order, @rest) = @_; + $table = $self->_quote($table) unless ref($table); @rest = (-1) unless defined $rest[0]; local $self->{having_bind} = []; my ($sql, @ret) = $self->SUPER::select( @@ -26,6 +27,27 @@ sub select { return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql; } +sub insert { + my $self = shift; + my $table = shift; + $table = $self->_quote($table) unless ref($table); + $self->SUPER::insert($table, @_); +} + +sub update { + my $self = shift; + my $table = shift; + $table = $self->_quote($table) unless ref($table); + $self->SUPER::update($table, @_); +} + +sub delete { + my $self = shift; + my $table = shift; + $table = $self->_quote($table) unless ref($table); + $self->SUPER::delete($table, @_); +} + sub _emulate_limit { my $self = shift; if ($_[3] == -1) { @@ -90,7 +112,12 @@ sub _table { } elsif (ref $from eq 'HASH') { return $self->_make_as($from); } else { - return $from; + return $from; # would love to quote here but _table ends up getting called + # twice during an ->select without a limit clause due to + # the way S::A::Limit->select works. should maybe consider + # bypassing this and doing S::A::select($self, ...) in + # our select method above. meantime, quoting shims have + # been added to select/insert/update/delete here } } @@ -213,7 +240,7 @@ use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/AccessorGroup/); __PACKAGE__->mk_group_accessors('simple' => - qw/connect_info _dbh _sql_maker _conn_pid _conn_tid debug debugfh + qw/_connect_info _dbh _sql_maker _conn_pid _conn_tid debug debugfh cursor on_connect_do transaction_depth/); sub new { @@ -250,6 +277,25 @@ This class represents the connection to the database =cut +=head2 connect_info + +Connection information arrayref. Can either be the same arguments +one would pass to DBI->connect, or a code-reference which returns +a connected database handle. In either case, there is an optional +final element in the arrayref, which can hold a hashref of +connection-specific Storage::DBI options. These include +C, and the sql_maker options C, +C, and C. Examples: + + ->connect_info([ 'dbi:SQLite:./foo.db' ]); + ->connect_info(sub { DBI->connect(...) }); + ->connect_info([ 'dbi:Pg:dbname=foo', + 'postgres', + '', + { AutoCommit => 0 }, + { quote_char => q{`}, name_sep => q{@} }, + ]); + =head2 on_connect_do Executes the sql statements given as a listref on every db connect. @@ -333,9 +379,40 @@ sub sql_maker { return $self->_sql_maker; } +sub connect_info { + my ($self, $info_arg) = @_; + + if($info_arg) { + my $info = [ @$info_arg ]; # copy because we can alter it + my $last_info = $info->[-1]; + if(ref $last_info eq 'HASH') { + my $used; + if(my $on_connect_do = $last_info->{on_connect_do}) { + $used = 1; + $self->on_connect_do($on_connect_do); + } + for my $sql_maker_opt (qw/limit_dialect quote_char name_sep/) { + if(my $opt_val = $last_info->{$sql_maker_opt}) { + $used = 1; + $self->sql_maker->$sql_maker_opt($opt_val); + } + } + + # remove our options hashref if it was there, to avoid confusing + # DBI in the case the user didn't use all 4 DBI options, as in: + # [ 'dbi:SQLite:foo.db', { quote_char => q{`} } ] + pop(@$info) if $used; + } + + $self->_connect_info($info); + } + + $self->_connect_info; +} + sub _populate_dbh { my ($self) = @_; - my @info = @{$self->connect_info || []}; + my @info = @{$self->_connect_info || []}; $self->_dbh($self->_connect(@info)); my $driver = $self->_dbh->{Driver}->{Name}; eval "require DBIx::Class::Storage::DBI::${driver}"; @@ -364,17 +441,20 @@ sub _connect { $DBI::connect_via = 'connect'; } - if(ref $info[0] eq 'CODE') { - $dbh = &{$info[0]}; - } - else { - $dbh = DBI->connect(@info); - } + eval { + if(ref $info[0] eq 'CODE') { + $dbh = &{$info[0]}; + } + else { + $dbh = DBI->connect(@info); + } + }; $DBI::connect_via = $old_connect_via if $old_connect_via; - $self->throw_exception("DBI Connection failed: $DBI::errstr") - unless $dbh; + if (!$dbh || $@) { + $self->throw_exception("DBI Connection failed: " . ($@ || $DBI::errstr)); + } $dbh; } @@ -390,8 +470,11 @@ an entire code block to be executed transactionally. sub txn_begin { my $self = shift; - $self->dbh->begin_work - if $self->{transaction_depth}++ == 0 and $self->dbh->{AutoCommit}; + if (($self->{transaction_depth}++ == 0) and ($self->dbh->{AutoCommit})) { + $self->debugfh->print("BEGIN WORK\n") + if ($self->debug); + $self->dbh->begin_work; + } } =head2 txn_commit @@ -403,10 +486,18 @@ Issues a commit against the current dbh. sub txn_commit { my $self = shift; if ($self->{transaction_depth} == 0) { - $self->dbh->commit unless $self->dbh->{AutoCommit}; + unless ($self->dbh->{AutoCommit}) { + $self->debugfh->print("COMMIT\n") + if ($self->debug); + $self->dbh->commit; + } } else { - $self->dbh->commit if --$self->{transaction_depth} == 0; + if (--$self->{transaction_depth} == 0) { + $self->debugfh->print("COMMIT\n") + if ($self->debug); + $self->dbh->commit; + } } } @@ -423,12 +514,21 @@ sub txn_rollback { eval { if ($self->{transaction_depth} == 0) { - $self->dbh->rollback unless $self->dbh->{AutoCommit}; + unless ($self->dbh->{AutoCommit}) { + $self->debugfh->print("ROLLBACK\n") + if ($self->debug); + $self->dbh->rollback; + } } else { - --$self->{transaction_depth} == 0 ? - $self->dbh->rollback : + if (--$self->{transaction_depth} == 0) { + $self->debugfh->print("ROLLBACK\n") + if ($self->debug); + $self->dbh->rollback; + } + else { die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new; + } } }; @@ -449,13 +549,20 @@ sub _execute { my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind; $self->debugfh->print("$sql: " . join(', ', @debug_bind) . "\n"); } - my $sth = $self->sth($sql,$op); - $self->throw_exception("no sth generated via sql: $sql") unless $sth; + my $sth = eval { $self->sth($sql,$op) }; + + if (!$sth || $@) { + $self->throw_exception('no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"); + } + @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args my $rv; if ($sth) { - $rv = $sth->execute(@bind) - or $self->throw_exception("Error executing '$sql': " . $sth->errstr); + $rv = eval { $sth->execute(@bind) }; + + if ($@ || !$rv) { + $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr)); + } } else { $self->throw_exception("'$sql' did not generate a statement."); } @@ -610,10 +717,11 @@ sub deployment_statements { sub deploy { my ($self, $schema, $type, $sqltargs) = @_; - my @statements = $self->deployment_statements($schema, $type, $sqltargs); - foreach(split(";\n", @statements)) { - $self->debugfh->print("$_\n") if $self->debug; - $self->dbh->do($_) or warn "SQL was:\n $_"; + foreach my $statement ( $self->deployment_statements($schema, $type, $sqltargs) ) { + for ( split(";\n", $statement)) { + $self->debugfh->print("$_\n") if $self->debug; + $self->dbh->do($_) or warn "SQL was:\n $_"; + } } }