X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=c7f0ec8870243f107a9c7a4461f7f0b2a1880b1f;hb=052b8ce2ec154827133b0b08042487c5617300aa;hp=29fc1bfb384212a91ee4da47da958d334ed58765;hpb=7d216b101775c472b012b257d848f50d78b193c8;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 29fc1bf..c7f0ec8 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -10,7 +10,6 @@ use mro 'c3'; use Carp::Clan qw/^DBIx::Class|^Try::Tiny/; use DBI; use DBIx::Class::Storage::DBI::Cursor; -use DBIx::Class::Storage::Statistics; use Scalar::Util qw/refaddr weaken reftype blessed/; use Data::Dumper::Concise 'Dumper'; use Sub::Name 'subname'; @@ -54,10 +53,13 @@ __PACKAGE__->mk_group_accessors('simple' => @storage_options); # will get the same rdbms version). _determine_supports_X does not need to # exist on a driver, as we ->can for it before calling. -my @capabilities = (qw/insert_returning placeholders typeless_placeholders/); +my @capabilities = (qw/insert_returning placeholders typeless_placeholders join_optimizer/); __PACKAGE__->mk_group_accessors( dbms_capability => map { "_supports_$_" } @capabilities ); -__PACKAGE__->mk_group_accessors( use_dbms_capability => map { "_use_$_" } @capabilities ); +__PACKAGE__->mk_group_accessors( use_dbms_capability => map { "_use_$_" } (@capabilities ) ); +# on by default, not strictly a capability (pending rewrite) +__PACKAGE__->_use_join_optimizer (1); +sub _determine_supports_join_optimizer { 1 }; # Each of these methods need _determine_driver called before itself # in order to function reliably. This is a purely DRY optimization @@ -168,9 +170,9 @@ sub new { sub _arm_global_destructor { my $self = shift; - my $key = Scalar::Util::refaddr ($self); + my $key = refaddr ($self); $seek_and_destroy{$key} = $self; - Scalar::Util::weaken ($seek_and_destroy{$key}); + weaken ($seek_and_destroy{$key}); } END { @@ -1519,7 +1521,7 @@ sub _fix_bind_params { if ( defined( $_ && $_->[1] ) ) { map { qq{'$_'}; } @{$_}[ 1 .. $#$_ ]; } - else { q{'NULL'}; } + else { q{NULL}; } } @bind; } @@ -1587,41 +1589,62 @@ sub _execute { $self->dbh_do('_dbh_execute', @_); # retry over disconnects } -sub _prefetch_insert_auto_nextvals { +sub insert { my ($self, $source, $to_insert) = @_; - my $upd = {}; - - foreach my $col ( $source->columns ) { - if ( !defined $to_insert->{$col} ) { - my $col_info = $source->column_info($col); - - if ( $col_info->{auto_nextval} ) { - $upd->{$col} = $to_insert->{$col} = $self->_sequence_fetch( - 'nextval', - $col_info->{sequence} ||= + my $colinfo = $source->columns_info; + + # mix with auto-nextval marked values (a bit of a speed hit, but + # no saner way to handle this yet) + my $auto_nextvals = {} ; + for my $col (keys %$colinfo) { + if ( + $colinfo->{$col}{auto_nextval} + and + ( + ! exists $to_insert->{$col} + or + ref $to_insert->{$col} eq 'SCALAR' + ) + ) { + $auto_nextvals->{$col} = $self->_sequence_fetch( + 'nextval', + ( $colinfo->{$col}{sequence} ||= $self->_dbh_get_autoinc_seq($self->_get_dbh, $source, $col) - ); - } + ), + ); } } - return $upd; -} + # fuse the values + $to_insert = { %$to_insert, %$auto_nextvals }; -sub insert { - my $self = shift; - my ($source, $to_insert, $opts) = @_; + # list of primary keys we try to fetch from the database + # both not-exsists and scalarrefs are considered + my %fetch_pks; + %fetch_pks = ( map + { $_ => scalar keys %fetch_pks } # so we can preserve order for prettyness + grep + { ! exists $to_insert->{$_} or ref $to_insert->{$_} eq 'SCALAR' } + $source->primary_columns + ); - my $updated_cols = $self->_prefetch_insert_auto_nextvals (@_); + my $sqla_opts; + if ($self->_use_insert_returning) { + + # retain order as declared in the resultsource + for (sort { $fetch_pks{$a} <=> $fetch_pks{$b} } keys %fetch_pks ) { + push @{$sqla_opts->{returning}}, $_; + } + } my $bind_attributes = $self->source_bind_attributes($source); - my ($rv, $sth) = $self->_execute('insert' => [], $source, $bind_attributes, $to_insert, $opts); + my ($rv, $sth) = $self->_execute('insert' => [], $source, $bind_attributes, $to_insert, $sqla_opts); - if ($opts->{returning}) { - my @ret_cols = @{$opts->{returning}}; + my %returned_cols = %$auto_nextvals; + if (my $retlist = $sqla_opts->{returning}) { my @ret_vals = try { local $SIG{__WARN__} = sub {}; my @r = $sth->fetchrow_array; @@ -1629,18 +1652,13 @@ sub insert { @r; }; - my %ret; - @ret{@ret_cols} = @ret_vals if (@ret_vals); - - $updated_cols = { - %$updated_cols, - %ret, - }; + @returned_cols{@$retlist} = @ret_vals if @ret_vals; } - return $updated_cols; + return \%returned_cols; } + ## Currently it is assumed that all values passed will be "normal", i.e. not ## scalar refs, or at least, all the same type as the first set, the statement is ## only prepped once. @@ -1831,15 +1849,14 @@ sub _dbh_execute_inserts_with_no_binds { } catch { $err = shift; + }; + + # Make sure statement is finished even if there was an exception. + try { + $sth->finish } - finally { - # Make sure statement is finished even if there was an exception. - try { - $sth->finish - } - catch { - $err = shift unless defined $err; - }; + catch { + $err = shift unless defined $err; }; $self->throw_exception($err) if defined $err; @@ -2100,11 +2117,13 @@ sub source_bind_attributes { my ($self, $source) = @_; my $bind_attributes; - foreach my $column ($source->columns) { - my $data_type = $source->column_info($column)->{data_type} || ''; - $bind_attributes->{$column} = $self->bind_attribute_by_data_type($data_type) - if $data_type; + my $colinfo = $source->columns_info; + + for my $col (keys %$colinfo) { + if (my $dt = $colinfo->{$col}{data_type} ) { + $bind_attributes->{$col} = $self->bind_attribute_by_data_type($dt) + } } return $bind_attributes;