Prechádzať zdrojové kódy

Begin and end transactions with Trog::SQLite::transaction

TagIndex had a helper of its own for this, and Data::SQLite::write had a
second copy written inline.  Trog::SQLite 0.004 has transaction, which
does what both of them did, so both call it now.  Makefile.PL asks for
that version.

A write inside a transaction that its caller began still runs in that
transaction, as before.  A failed rollback is no longer logged as a
warning.  The write dies with its own error, as before.

The new test writes a batch in which one post has no id, and expects
none of the batch to land.  It fails when write calls _insert without a
transaction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
troglodyne-bot 1 týždeň pred
rodič
commit
aea40fd7e0
4 zmenil súbory, kde vykonal 8 pridanie a 33 odobranie
  1. 1 1
      Makefile.PL
  2. 1 18
      lib/Trog/Data/SQLite.pm
  3. 2 14
      lib/Trog/SQLite/TagIndex.pm
  4. 4 0
      t/Trog-Data-SQLite.t

+ 1 - 1
Makefile.PL

@@ -21,7 +21,7 @@ WriteMakefile(
     },
     PREREQ_PM => {
         'Trog::TOTP'                          => '1.007',
-        'Trog::SQLite'                        => '0.001',
+        'Trog::SQLite'                        => '0.004',
         'CGI::Cookie'                         => '0',
         'Capture::Tiny'                       => '0',
         'Carp'                                => '0',

+ 1 - 18
lib/Trog/Data/SQLite.pm

@@ -319,24 +319,7 @@ and taking minutes, since SQLite otherwise commits once per row.
 sub write ( $self, $data ) {
     my $dbh = _dbh();
 
-    # Only if we are not already inside someone else's transaction: begin_work
-    # dies rather than nesting.
-    my $mine = $dbh->{AutoCommit} ? $dbh->begin_work : 0;
-
-    local $@;
-    eval {
-        _insert( $dbh, $data );
-        $dbh->commit if $mine;
-        1;
-    } or do {
-        my $error = $@;
-        if ($mine) {
-            local $@;
-            eval { $dbh->rollback; 1 } or WARN("Could not roll back a failed write: $@");
-        }
-        confess $error;
-    };
-
+    Trog::SQLite::transaction( $dbh, sub { _insert( $dbh, $data ) } );
     return 1;
 }
 

+ 2 - 14
lib/Trog/SQLite/TagIndex.pm

@@ -54,7 +54,7 @@ sub tags {
 
 sub add_post ( $post, $data_obj ) {
     my $dbh = _dbh();
-    _in_transaction(
+    Trog::SQLite::transaction(
         $dbh,
         sub {
             # posts_index has no key for INSERT OR IGNORE to skip a duplicate on,
@@ -79,7 +79,7 @@ The HTTP methods are kept, as the schema fills those rather than any post.
 
 sub rebuild ($data_obj) {
     my $dbh = _dbh();
-    _in_transaction(
+    Trog::SQLite::transaction(
         $dbh,
         sub {
             # Cascades to posts_index, routes and post_aliases.
@@ -93,18 +93,6 @@ sub rebuild ($data_obj) {
     return 1;
 }
 
-sub _in_transaction ( $dbh, $work ) {
-    $dbh->begin_work();
-    my $ok = eval { $work->(); 1 };
-    if ( !$ok ) {
-        my $err = $@;
-        $dbh->rollback();
-        die $err;
-    }
-    $dbh->commit();
-    return 1;
-}
-
 sub remove_post ($post) {
     my $dbh = _dbh();
 

+ 4 - 0
t/Trog-Data-SQLite.t

@@ -318,6 +318,10 @@ subtest 'writing' => sub {
 
     like( exception { $sqlite->write( [ { title => 'No Id' } ] ) }, qr/no id/i, 'a post with no id is refused' );
 
+    my $count = $sqlite->count();
+    ok( exception { $sqlite->write( [ post( id => 'p-half', title => 'Half' ), { title => 'No Id' } ] ) }, 'a batch with a post it refuses is refused' );
+    is( $sqlite->count(), $count, 'and none of the batch lands' );
+
     # These are computed per query out of the version history; storing them
     # would bake one query's answer into the post forever.
     my $dbh = Trog::SQLite::dbh( undef, 'data/posts.sqlite' );