[Dbix-class] Insert or Update (was ANNOUNCE: 0.08099_08)
Peter Corlett
abuse at cabal.org.uk
Mon Apr 13 13:46:54 GMT 2009
On 13 Apr 2009, at 11:20, Peter Corlett wrote:
[...]
> So it looks like this still needs savepoints to do correctly. Of
> course, one could always instead do a heroically complex bodge job
> that looks really impressive but doesn't actually work.
And in the interests of STFUAWSC:
diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm
index 816b374..a8d501b 100644
--- a/lib/DBIx/Class/ResultSet.pm
+++ b/lib/DBIx/Class/ResultSet.pm
@@ -2103,13 +2103,43 @@ sub update_or_create {
my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
my $cond = ref $_[0] eq 'HASH' ? shift : {@_};
+ my $schema = $self->result_source->schema;
+
+ # Savepoints only work within a transaction, so we wrap ourselves
into a
+ # transaction to make sure.
+ $schema->txn_begin;
+
+ # Start a savepoint. This fails if the underlying storage doesn't
support
+ # savepoints, so we fall through to the previus implementation.
+ eval { $schema->svp_begin; };
+ unless($@) {
+ # now try an INSERT
+ my $row = eval { $self->create($cond); };
+ if($@) {
+ # If the INSERT failed, this suggests a failed constraint check
due to
+ # duplicate keys. So we rollback the savepoint and do a
+ # SELECT-modify-UPDATE instead. We add the SELECT ... FOR UPDATE
+ # option to block any parallel queries on the same row.
+ $schema->svp_rollback;
+ $row = $self->find($cond, { %$attrs, for => 'update'} )
+ or die "Atomic update_or_create failed";
+ $row->update($cond);
+ }
+ $schema->svp_release;
+ $schema->txn_commit;
+ return $row;
+ }
+
my $row = $self->find($cond, $attrs);
if (defined $row) {
$row->update($cond);
+ $schema->txn_commit;
return $row;
}
- return $self->create($cond);
+ $row = $self->create($cond);
+ $schema->txn_commit;
+ return $row;
}
=head2 get_cache
More information about the DBIx-Class
mailing list