X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=78d7321de19d780c3a2270cb3a53b565ced7cf24;hb=f83d99c80b593ccf0c445784cda98cb4de0c8564;hp=f97ed8d4bf7976a72d10e18d09a9b336c10e2a7b;hpb=bc0c980086ba429cf33b1d471d1035601727231f;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index f97ed8d..78d7321 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 { @@ -236,7 +263,7 @@ sub throw_exception { croak($msg); } -=head1 NAME +=head1 NAME DBIx::Class::Storage::DBI - DBI storage handler @@ -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. @@ -318,6 +364,12 @@ sub ensure_connected { } } +=head2 dbh + +Returns the dbh - a data base handle of class L. + +=cut + sub dbh { my ($self) = @_; @@ -333,9 +385,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 +447,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 +476,14 @@ 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) { + my $dbh = $self->dbh; + if ($dbh->{AutoCommit}) { + $self->debugfh->print("BEGIN WORK\n") + if ($self->debug); + $dbh->begin_work; + } + } } =head2 txn_commit @@ -403,10 +495,19 @@ 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}; + my $dbh = $self->dbh; + unless ($dbh->{AutoCommit}) { + $self->debugfh->print("COMMIT\n") + if ($self->debug); + $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 +524,22 @@ sub txn_rollback { eval { if ($self->{transaction_depth} == 0) { - $self->dbh->rollback unless $self->dbh->{AutoCommit}; + my $dbh = $self->dbh; + unless ($dbh->{AutoCommit}) { + $self->debugfh->print("ROLLBACK\n") + if ($self->debug); + $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,14 +560,21 @@ 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); - } else { + if ($sth) { + $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."); } return (wantarray ? ($rv, $sth, @bind) : $rv); @@ -534,14 +652,16 @@ Returns database type info for a given table columns. sub columns_info_for { my ($self, $table) = @_; - if ($self->dbh->can('column_info')) { + my $dbh = $self->dbh; + + if ($dbh->can('column_info')) { my %result; - my $old_raise_err = $self->dbh->{RaiseError}; - my $old_print_err = $self->dbh->{PrintError}; - $self->dbh->{RaiseError} = 1; - $self->dbh->{PrintError} = 0; + my $old_raise_err = $dbh->{RaiseError}; + my $old_print_err = $dbh->{PrintError}; + $dbh->{RaiseError} = 1; + $dbh->{PrintError} = 0; eval { - my $sth = $self->dbh->column_info( undef, undef, $table, '%' ); + my $sth = $dbh->column_info( undef, undef, $table, '%' ); $sth->execute(); while ( my $info = $sth->fetchrow_hashref() ){ my %column_info; @@ -553,21 +673,21 @@ sub columns_info_for { $result{$info->{COLUMN_NAME}} = \%column_info; } }; - $self->dbh->{RaiseError} = $old_raise_err; - $self->dbh->{PrintError} = $old_print_err; + $dbh->{RaiseError} = $old_raise_err; + $dbh->{PrintError} = $old_print_err; return \%result if !$@; } my %result; - my $sth = $self->dbh->prepare("SELECT * FROM $table WHERE 1=0"); + my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0"); $sth->execute; my @columns = @{$sth->{NAME_lc}}; for my $i ( 0 .. $#columns ){ my %column_info; my $type_num = $sth->{TYPE}->[$i]; my $type_name; - if(defined $type_num && $self->dbh->can('type_info')) { - my $type_info = $self->dbh->type_info($type_num); + if(defined $type_num && $dbh->can('type_info')) { + my $type_info = $dbh->type_info($type_num); $type_name = $type_info->{TYPE_NAME} if $type_info; } $column_info{data_type} = $type_name ? $type_name : $type_num; @@ -600,7 +720,7 @@ sub deployment_statements { eval "use SQL::Translator"; $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@; eval "use SQL::Translator::Parser::DBIx::Class;"; - $self->throw_exception($@) if $@; + $self->throw_exception($@) if $@; eval "use SQL::Translator::Producer::${type};"; $self->throw_exception($@) if $@; my $tr = SQL::Translator->new(%$sqltargs); @@ -610,11 +730,12 @@ 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 $_"; + } + } } sub DESTROY { shift->disconnect }