From: Peter Rabbitson Date: Wed, 24 Mar 2010 13:10:33 +0000 (+0000) Subject: Really fix INSERT RETURNING - simply make it a flag on the storage and keep the machi... X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=38d5ea9f06646beb01422d3d6a2994f1601bf35e;p=dbsrgits%2FDBIx-Class-Historic.git Really fix INSERT RETURNING - simply make it a flag on the storage and keep the machinery in core --- diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index 7716985..ceb38d5 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -363,7 +363,7 @@ sub insert { my $updated_cols = $source->storage->insert( $source, { $self->get_columns }, - keys %auto_pri + (keys %auto_pri) && $source->storage->can_insert_returning ? { returning => [ sort { $auto_pri{$a} cmp $auto_pri{$b} } keys %auto_pri ] } : () , diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 74d39bc..00c8eb8 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -33,7 +33,10 @@ __PACKAGE__->mk_group_accessors('simple' => @storage_options); # default cursor class, overridable in connect_info attributes __PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor'); -__PACKAGE__->mk_group_accessors('inherited' => qw/sql_maker_class/); +__PACKAGE__->mk_group_accessors('inherited' => qw/ + sql_maker_class + can_insert_returning +/); __PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks'); @@ -1387,13 +1390,32 @@ sub _prefetch_insert_auto_nextvals { sub insert { my $self = shift; - my ($source, $to_insert) = @_; + my ($source, $to_insert, $opts) = @_; my $updated_cols = $self->_prefetch_insert_auto_nextvals (@_); my $bind_attributes = $self->source_bind_attributes($source); - $self->_execute('insert' => [], $source, $bind_attributes, $to_insert); + my ($rv, $sth) = $self->_execute('insert' => [], $source, $bind_attributes, $to_insert, $opts); + + if ($opts->{returning}) { + my @ret_cols = @{$opts->{returning}}; + + my @ret_vals = eval { + local $SIG{__WARN__} = sub {}; + my @r = $sth->fetchrow_array; + $sth->finish; + @r; + }; + + my %ret; + @ret{@ret_cols} = @ret_vals if (@ret_vals); + + $updated_cols = { + %$updated_cols, + %ret, + }; + } return $updated_cols; } diff --git a/lib/DBIx/Class/Storage/DBI/InsertReturning.pm b/lib/DBIx/Class/Storage/DBI/InsertReturning.pm deleted file mode 100644 index f110cb5..0000000 --- a/lib/DBIx/Class/Storage/DBI/InsertReturning.pm +++ /dev/null @@ -1,64 +0,0 @@ -package DBIx::Class::Storage::DBI::InsertReturning; - -use strict; -use warnings; - -use base qw/DBIx::Class::Storage::DBI/; -use mro 'c3'; - -=head1 NAME - -DBIx::Class::Storage::DBI::InsertReturning - Storage component for RDBMSes -supporting INSERT ... RETURNING - -=head1 DESCRIPTION - -Provides Auto-PK and -L support for -databases supporting the C syntax. - -=cut - -sub insert { - my $self = shift; - my ($source, $to_insert, $opts) = @_; - - return $self->next::method (@_) unless ($opts && $opts->{returning}); - - my $updated_cols = $self->_prefetch_insert_auto_nextvals ($source, $to_insert); - - my $bind_attributes = $self->source_bind_attributes($source); - my ($rv, $sth) = $self->_execute (insert => [], $source, $bind_attributes, $to_insert, $opts); - - if (my @ret_cols = @{$opts->{returning}}) { - - my @ret_vals = eval { - local $SIG{__WARN__} = sub {}; - my @r = $sth->fetchrow_array; - $sth->finish; - @r; - }; - - my %ret; - @ret{@ret_cols} = @ret_vals if (@ret_vals); - - $updated_cols = { - %$updated_cols, - %ret, - }; - } - - return $updated_cols; -} - -=head1 AUTHOR - -See L and L - -=head1 LICENSE - -You may distribute this code under the same terms as Perl itself. - -=cut - -1; diff --git a/lib/DBIx/Class/Storage/DBI/Pg.pm b/lib/DBIx/Class/Storage/DBI/Pg.pm index 46eee6f..1b5e6d8 100644 --- a/lib/DBIx/Class/Storage/DBI/Pg.pm +++ b/lib/DBIx/Class/Storage/DBI/Pg.pm @@ -5,7 +5,6 @@ use warnings; use base qw/ DBIx::Class::Storage::DBI::MultiColumnIn - DBIx::Class::Storage::DBI::InsertReturning /; use mro 'c3'; @@ -17,13 +16,19 @@ use Context::Preserve (); warn __PACKAGE__.": DBD::Pg 2.9.2 or greater is strongly recommended\n" if ($DBD::Pg::VERSION < 2.009002); # pg uses (used?) version::qv() +sub can_insert_returning { + # FIXME !!! + # pg before 8.2 doesn't support this, need to check version + return 1; +} + sub with_deferred_fk_checks { my ($self, $sub) = @_; my $txn_scope_guard = $self->txn_scope_guard; $self->_do_query('SET CONSTRAINTS ALL DEFERRED'); - + my $sg = Scope::Guard->new(sub { $self->_do_query('SET CONSTRAINTS ALL IMMEDIATE'); }); @@ -32,18 +37,37 @@ sub with_deferred_fk_checks { after => sub { $txn_scope_guard->commit }); } +sub last_insert_id { + my ($self,$source,@cols) = @_; + + my @values; + + for my $col (@cols) { + my $seq = ( $source->column_info($col)->{sequence} ||= $self->dbh_do('_dbh_get_autoinc_seq', $source, $col) ) + or $self->throw_exception( sprintf( + 'could not determine sequence for column %s.%s, please consider adding a schema-qualified sequence to its column info', + $source->name, + $col, + )); + + push @values, $self->_dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq}); + } + + return @values; +} + sub _sequence_fetch { my ($self, $function, $sequence) = @_; $self->throw_exception('No sequence to fetch') unless $sequence; - + my ($val) = $self->_get_dbh->selectrow_array( sprintf "select $function('%s')", $sequence ); return $val; -} +} sub _dbh_get_autoinc_seq { my ($self, $dbh, $source, $col) = @_; diff --git a/t/72pg.t b/t/72pg.t index 9251caa..ee6db29 100644 --- a/t/72pg.t +++ b/t/72pg.t @@ -265,11 +265,13 @@ my $st = $schema->resultset('SequenceTest')->create({ name => 'foo', pkid1 => 55 is($st->pkid1, 55, "Auto-PK for sequence without default: First primary key set manually"); -######## test non-integer non-serial auto-pk +######## test non-serial auto-pk -$schema->source('TimestampPrimaryKey')->name('dbic_t_schema.timestamp_primary_key_test'); -my $row = $schema->resultset('TimestampPrimaryKey')->create({}); -ok $row->id; +if ($schema->storage->can_insert_returning) { + $schema->source('TimestampPrimaryKey')->name('dbic_t_schema.timestamp_primary_key_test'); + my $row = $schema->resultset('TimestampPrimaryKey')->create({}); + ok $row->id; +} ######## test with_deferred_fk_checks