Package: libdbi-ruby1.8
Version: 0.4.3-2
Severity: normal
Tags: patch

If return is made from within DatabaseHandle#transaction block, commit isn't
done.

I've attached 2 programs:
1. transaction_return - insert data into the test table (and, optionally, run
   the second transaction);
2. check - print test table data.

$./transaction_return && ./check
inserting `100' into public.test
0 rows in public.test

Without execution of the second transaction, commit isn't done.

$./transaction_return 1 && ./check
inserting `100' into public.test
executing empty transaction
1 rows in public.test
id: 100

When the second transaction is executed, INSERT done in the previous
transaction is commited. From this it can be concluded that neither commit
nor rollback is done when `return' is executed from within a transaction
method block.

I'm attaching a patch that ensures that commit happens even if control
is transferred out of the transaction method. The patch also enhances
transaction method to return value returned by block when it could be used
in the calling code (i.e. when no exception happened and no return statement
executed).

P.S. I've looked at libdbi-ruby source code in Lenny and based on this assume
it's also affected.

-- System Information:
Debian Release: squeeze/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)

Kernel: Linux 2.6.32-5-686 (SMP w/1 CPU core)
Locale: LANG=en_NZ.UTF-8, LC_CTYPE=en_NZ.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages libdbi-ruby1.8 depends on:
ii  libdeprecated-ruby1.8        2.0.1-2     Library for handling deprecated co
ii  libruby1.8                   1.8.7.299-1 Libraries necessary to run Ruby 1.

libdbi-ruby1.8 recommends no packages.

Versions of packages libdbi-ruby1.8 suggests:
ii  libdbi-ruby                   0.4.3-2    Database Independent Interface for

-- no debconf information
#!/usr/bin/ruby

require 'dbi'

@db = DBI.connect 'DBI:Pg:test', 'postgres'

rows = @db.select_all('SELECT * FROM public.test')
puts "#{rows.size} rows in public.test"
rows.each { |r|
  puts "id: #{r.first}"
}
#!/usr/bin/ruby

require 'dbi'

second_transaction = $*.first && $*.first == '1' ? true : false

@db = DBI.connect 'DBI:Pg:test', 'postgres'
@db['AutoCommit'] = false

if @db.select_all("SELECT tablename FROM pg_tables " \
	    "WHERE schemaname = 'public' AND tablename = 'test'").size == 1
  @db.do 'DROP TABLE public.test'
end

@db.do <<EOF
  CREATE TABLE public.test (
    id INTEGER NOT NULL
  );
EOF

def newId(id)
  @db.transaction { |db|
    puts "inserting `#{id}' into public.test"
    db.do('INSERT INTO public.test VALUES($1)', id)
    return 42
  }
end

newId(100)

exit unless second_transaction

puts "executing empty transaction"
@db.transaction { |db| }
--- database.rb 2010-07-19 19:27:38.000000000 +1200
+++ libdbi-ruby-0.4.3/lib/dbi/handles/database.rb       2010-07-19 
19:35:13.000000000 +1200
@@ -208,11 +208,16 @@
 
             commit
             begin
-                yield self
+                ret = yield self
                 commit
+                done = true
+                ret
             rescue Exception
                 rollback
+                done = true
                 raise
+            ensure
+                commit unless done
             end
         end
 

Reply via email to