diff --git a/lib/plsql/oci_connection.rb b/lib/plsql/oci_connection.rb index a74cd9d8..99e9ec64 100644 --- a/lib/plsql/oci_connection.rb +++ b/lib/plsql/oci_connection.rb @@ -68,14 +68,17 @@ def exec(sql, *bindvars) class Cursor #:nodoc: include Connection::CursorCommon - # stack of open cursors - @@open_cursors = [] attr_reader :raw_cursor + # stack of open cursors per thread + def self.open_cursors + Thread.current[:plsql_oci_cursor_stack] ||= [] + end + def initialize(conn, raw_cursor) @connection = conn @raw_cursor = raw_cursor - @@open_cursors.push self + self.class.open_cursors.push self end def self.new_from_parse(conn, sql) @@ -125,7 +128,7 @@ def close_raw_cursor def close # close all cursors that were created after this one - while (open_cursor = @@open_cursors.pop) && !open_cursor.equal?(self) + while (open_cursor = self.class.open_cursors.pop) && !open_cursor.equal?(self) open_cursor.close_raw_cursor end close_raw_cursor @@ -336,4 +339,4 @@ def ora_date_to_ruby_date(val) end -end \ No newline at end of file +end diff --git a/spec/plsql/schema_spec.rb b/spec/plsql/schema_spec.rb index ff335533..2ff7238b 100644 --- a/spec/plsql/schema_spec.rb +++ b/spec/plsql/schema_spec.rb @@ -220,6 +220,15 @@ class TestModel < TestBaseModel plsql.activerecord_class = TestModel expect(plsql.schema_name).to eq('HR') end + + it "should safely close cursors in threaded environment" do + expect { + t1 = Thread.new { plsql.dbms_lock.sleep(1) }.tap { |t| t.abort_on_exception = true } + t2 = Thread.new { plsql.dbms_lock.sleep(2) }.tap { |t| t.abort_on_exception = true } + [t2, t1].each { |t| t.join } + }.not_to raise_error + end + end if defined?(ActiveRecord) describe "DBMS_OUTPUT logging" do