Hello. I posted this solution only as a workaround.
If it goes to core then it would be much better to implement a callback
in Storage::DBI after $sql is generated but before $sth is generated.
(for example, $sql = $self->tune_sql($sql, $attrs) in sub _dbh_execute)
This would be the last change for Storage::DBI::* to implement some
database-specific features
like
Pg's for update/share
mysql's replace into / insert ignore / delayed updates / case-insensitive
select, etc

$attrs are needed to be passed to _dbh_execute to do that.

To DBIC developers: could you implement this (or similar) please?

2007/7/4, Daisuke Maki <[EMAIL PROTECTED]>:

Hi,

I've been looking for a way to do SELECT FOR UPDATE via DBIx::Class, and
found this:

http://lists.rawmode.org/pipermail/dbix-class/2007-March/003568.html

I don't think this is implemented yet, so I've given it a try.

Attached is a patch (with tests) against trunk. The tests requires an
extra module, Sys::SigAction, so the new tests will be skipped if
Sys::SigAction is not available.

Let me know if it look reasonable.

Regards,
--d

Index: t/72pg.t
===================================================================
--- t/72pg.t    (revision 3567)
+++ t/72pg.t    (working copy)
@@ -27,7 +27,7 @@
plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this
test'
. ' (note: creates and drops tables named artist and casecheck!)' unless
($dsn && $user);

-plan tests => 8;
+plan tests => 14;

DBICTest::Schema->load_classes( 'Casecheck' );
my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
@@ -87,6 +87,83 @@
my $uc_name_info = $schema->source('Casecheck')->column_info( 'uc_name' );
is( $uc_name_info->{size}, 3, "Case insensitive matching info for
'uc_name'" );

+# Test SELECT ... FOR UPDATE
+my $HaveSysSigAction = eval "require Sys::SigAction" && !$@;
+if ($HaveSysSigAction) {
+    Sys::SigAction->import( 'set_sig_handler' );
+}
+
+SKIP: {
+    skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
+    # create a new schema
+    my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
+    $schema2->source("Artist")->name("testschema.artist");
+
+    $schema->txn_do( sub {
+        my $artist = $schema->resultset('Artist')->search(
+            {
+                artistid => 1
+            },
+            {
+                locking => 'update'
+            }
+        )->first;
+        is($artist->artistid, 1, "select for update returns artistid =
1");
+
+        my $artist_from_schema2;
+        my $error_ok = 0;
+        eval {
+            my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout"
} );
+            alarm(2);
+            $artist_from_schema2 =
$schema2->resultset('Artist')->find(1);
+            $artist_from_schema2->name('fooey');
+            $artist_from_schema2->update;
+            alarm(0);
+        };
+        if (my $e = $@) {
+            $error_ok = $e =~ /DBICTestTimeout/;
+        }
+
+        # Make sure that an error was raised, and that the update failed
+        ok($error_ok, "update from second schema times out");
+        ok($artist_from_schema2->is_column_changed('name'), "'name'
column is still dirty from second schema");
+    });
+}
+
+SKIP: {
+    skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
+    # create a new schema
+    my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
+    $schema2->source("Artist")->name("testschema.artist");
+
+    $schema->txn_do( sub {
+        my $artist = $schema->resultset('Artist')->search(
+            {
+                artistid => 1
+            },
+        )->first;
+        is($artist->artistid, 1, "select for update returns artistid =
1");
+
+        my $artist_from_schema2;
+        my $error_ok = 0;
+        eval {
+            my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout"
} );
+            alarm(2);
+            $artist_from_schema2 =
$schema2->resultset('Artist')->find(1);
+            $artist_from_schema2->name('fooey');
+            $artist_from_schema2->update;
+            alarm(0);
+        };
+        if (my $e = $@) {
+            $error_ok = $e =~ /DBICTestTimeout/;
+        }
+
+        # Make sure that an error was raised, and that the update failed
+        ok(! $error_ok, "update from second schema DOES NOT timeout");
+        ok(! $artist_from_schema2->is_column_changed('name'), "'name'
column is NOT dirty from second schema");
+    });
+}
+
END {
    if($dbh) {
        $dbh->do("DROP TABLE testschema.artist;");
Index: lib/DBIx/Class/Storage/DBI/Pg.pm
===================================================================
--- lib/DBIx/Class/Storage/DBI/Pg.pm    (revision 3567)
+++ lib/DBIx/Class/Storage/DBI/Pg.pm    (working copy)
@@ -7,12 +7,35 @@

use base qw/DBIx::Class::Storage::DBI/;

+our $_LOCKING = undef;
+
# __PACKAGE__->load_components(qw/PK::Auto/);

# Warn about problematic versions of DBD::Pg
warn "DBD::Pg 1.49 is strongly recommended"
  if ($DBD::Pg::VERSION < 1.49);

+sub _select {
+    my $self = shift;
+
+    my $locking = delete $_[3]->{locking};
+    local $_LOCKING = $locking && $locking =~ /^(?:update|share)$/ ?
$locking : undef;
+    $self->next::method(@_);
+}
+
+sub sth {
+    my ($self, $sql) = @_;
+
+    if ($_LOCKING) {
+        # XXX Should really be checking if this is a select statement
+        $sql .= $_LOCKING eq 'update' ? 'FOR UPDATE' :
+                $_LOCKING eq 'share'  ? 'FOR SHARE'  :
+                ''
+        ;
+    }
+    $self->SUPER::sth($sql);
+}
+
sub _dbh_last_insert_id {
  my ($self, $dbh, $seq) = @_;
  $dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq});

_______________________________________________
List: http://lists.rawmode.org/cgi-bin/mailman/listinfo/dbix-class
Wiki: http://dbix-class.shadowcatsystems.co.uk/
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/trunk/DBIx-Class/
Searchable Archive:
http://www.mail-archive.com/dbix-class@lists.rawmode.org/

_______________________________________________
List: http://lists.rawmode.org/cgi-bin/mailman/listinfo/dbix-class
Wiki: http://dbix-class.shadowcatsystems.co.uk/
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/trunk/DBIx-Class/
Searchable Archive: http://www.mail-archive.com/dbix-class@lists.rawmode.org/

Reply via email to