$rollback_guard ||= $source->storage->txn_scope_guard
}
+ ## PK::Auto
+ my %auto_pri;
+ my $auto_idx = 0;
+ for ($self->primary_columns) {
+ if (
+ not defined $self->get_column($_)
+ ||
+ (ref($self->get_column($_)) eq 'SCALAR')
+ ) {
+ my $col_info = $source->column_info($_);
+ $auto_pri{$_} = $auto_idx++ unless $col_info->{auto_nextval}; # auto_nextval's are pre-fetched in the storage
+ }
+ }
+
MULTICREATE_DEBUG and do {
no warnings 'uninitialized';
warn "MC $self inserting (".join(', ', $self->get_columns).")\n";
};
- my $updated_cols = $source->storage->insert($source, { $self->get_columns });
+ my $updated_cols = $source->storage->insert(
+ $source,
+ { $self->get_columns },
+ keys %auto_pri
+ ? { returning => [ sort { $auto_pri{$a} <=> $auto_pri{$b} } keys %auto_pri ] }
+ : ()
+ ,
+ );
+
foreach my $col (keys %$updated_cols) {
$self->store_column($col, $updated_cols->{$col});
+ delete $auto_pri{$col};
}
- ## PK::Auto
- my @auto_pri = grep {
- (not defined $self->get_column($_))
- ||
- (ref($self->get_column($_)) eq 'SCALAR')
- } $self->primary_columns;
-
- if (@auto_pri) {
- MULTICREATE_DEBUG and warn "MC $self fetching missing PKs ".join(', ', @auto_pri)."\n";
+ if (keys %auto_pri) {
+ my @missing = sort { $auto_pri{$a} <=> $auto_pri{$b} } keys %auto_pri;
+ MULTICREATE_DEBUG and warn "MC $self fetching missing PKs ".join(', ', @missing )."\n";
my $storage = $self->result_source->storage;
$self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" )
unless $storage->can('last_insert_id');
- my @ids = $storage->last_insert_id($self->result_source,@auto_pri);
+ my @ids = $storage->last_insert_id($self->result_source, @missing);
$self->throw_exception( "Can't get last insert id" )
- unless (@ids == @auto_pri);
- $self->store_column($auto_pri[$_] => $ids[$_]) for 0 .. $#ids;
+ unless (@ids == @missing);
+ $self->store_column($missing[$_] => $ids[$_]) for 0 .. $#missing;
}
$self->{_dirty_columns} = {};
$self->dbh_do('_dbh_execute', @_); # retry over disconnects
}
-sub insert {
+sub _prefetch_insert_auto_nextvals {
my ($self, $source, $to_insert) = @_;
- my $ident = $source->from;
- my $bind_attributes = $self->source_bind_attributes($source);
-
- my $updated_cols = {};
+ my $upd = {};
foreach my $col ( $source->columns ) {
if ( !defined $to_insert->{$col} ) {
my $col_info = $source->column_info($col);
if ( $col_info->{auto_nextval} ) {
- $updated_cols->{$col} = $to_insert->{$col} = $self->_sequence_fetch(
+ $upd->{$col} = $to_insert->{$col} = $self->_sequence_fetch(
'nextval',
$col_info->{sequence} ||=
$self->_dbh_get_autoinc_seq($self->_get_dbh, $source, $col)
}
}
+ return $upd;
+}
+
+sub insert {
+ my $self = shift;
+ my ($source, $to_insert) = @_;
+
+ my $updated_cols = $self->_prefetch_insert_auto_nextvals (@_);
+
+ my $bind_attributes = $self->source_bind_attributes($source);
+
$self->_execute('insert' => [], $source, $bind_attributes, $to_insert);
return $updated_cols;
use base qw/DBIx::Class::Storage::DBI/;
use mro 'c3';
-__PACKAGE__->mk_group_accessors(simple => qw/
- _returning_cols
-/);
-
=head1 NAME
DBIx::Class::Storage::DBI::InsertReturning - Storage component for RDBMSes
Provides Auto-PK and
L<is_auto_increment|DBIx::Class::ResultSource/is_auto_increment> support for
-databases supporting the C<INSERT ... RETURNING> syntax. Currently
-L<PostgreSQL|DBIx::Class::Storage::DBI::Pg> and
-L<Firebird|DBIx::Class::Storage::DBI::InterBase>.
+databases supporting the C<INSERT ... RETURNING> syntax.
=cut
-sub _prep_for_execute {
+sub insert {
my $self = shift;
- my ($op, $extra_bind, $ident, $args) = @_;
-
- if ($op eq 'insert') {
- $self->_returning_cols([]);
-
- my %pk;
- @pk{$ident->primary_columns} = ();
-
- my @auto_inc_cols = grep {
- my $inserting = $args->[0]{$_};
-
- ($ident->column_info($_)->{is_auto_increment}
- || exists $pk{$_})
- && (
- (not defined $inserting)
- ||
- (ref $inserting eq 'SCALAR' && $$inserting =~ /^null\z/i)
- )
- } $ident->columns;
+ my ($source, $to_insert, $opts) = @_;
- if (@auto_inc_cols) {
- $args->[1]{returning} = \@auto_inc_cols;
+ return $self->next::method (@_) unless ($opts && $opts->{returning});
- $self->_returning_cols->[0] = \@auto_inc_cols;
- }
- }
-
- return $self->next::method(@_);
-}
+ my $updated_cols = $self->_prefetch_insert_auto_nextvals ($source, $to_insert);
-sub _execute {
- my $self = shift;
- my ($op) = @_;
+ my $bind_attributes = $self->source_bind_attributes($source);
+ my ($rv, $sth) = $self->_execute (insert => [], $source, $bind_attributes, $to_insert, $opts);
- my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
+ if (my @ret_cols = @{$opts->{returning}}) {
- if ($op eq 'insert' && $self->_returning_cols) {
- local $@;
- my (@returning_cols) = eval {
+ my @ret_vals = eval {
local $SIG{__WARN__} = sub {};
- $sth->fetchrow_array
+ my @r = $sth->fetchrow_array;
+ $sth->finish;
+ @r;
};
- $self->_returning_cols->[1] = \@returning_cols;
- $sth->finish;
- }
- return wantarray ? ($rv, $sth, @bind) : $rv;
-}
+ my %ret;
+ @ret{@ret_cols} = @ret_vals if (@ret_vals);
-sub insert {
- my $self = shift;
-
- my $updated_cols = $self->next::method(@_);
-
- if ($self->_returning_cols->[0]) {
- my %returning_cols;
- @returning_cols{ @{ $self->_returning_cols->[0] } } = @{ $self->_returning_cols->[1] };
-
- $updated_cols = { %$updated_cols, %returning_cols };
+ $updated_cols = {
+ %$updated_cols,
+ %ret,
+ };
}
return $updated_cols;
}
-sub last_insert_id {
- my ($self, $source, @cols) = @_;
- my @result;
-
- my %returning_cols;
- @returning_cols{ @{ $self->_returning_cols->[0] } } =
- @{ $self->_returning_cols->[1] };
-
- push @result, $returning_cols{$_} for @cols;
-
- return @result;
-}
-
=head1 AUTHOR
See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>