1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
require_relative "../adapters/spec_helper"
describe Sequel::Database do
before do
@db = DB
end
it "should provide disconnect functionality" do
@db.disconnect
@db.pool.size.must_equal 0
@db.test_connection
@db.pool.size.must_equal 1
end
it "should provide disconnect functionality after preparing a statement" do
@db.create_table!(:items){Integer :i}
@db[:items].prepare(:first, :a).call
@db.disconnect
@db.pool.size.must_equal 0
@db.drop_table?(:items)
end
if DB.respond_to?(:with_advisory_lock) && DB.adapter_scheme != :ado
lock_id = 1357
it "#with_advisory_lock should raise if it cannot acquire a lock" do
e = nil
o = Object.new
DB.with_advisory_lock(lock_id) do
Thread.new do
begin
DB.with_advisory_lock(lock_id)
rescue Sequel::AdvisoryLockError => e
end
end.join
e.must_be_kind_of Sequel::AdvisoryLockError
o
end.must_equal o
end
it "#with_advisory_lock with :wait option should wait until an advisory lock is acquired" do
q = Queue.new
q2 = Queue.new
t = Thread.new do
q2.pop
q.push nil
DB.with_advisory_lock(lock_id, wait: true) do
q.push 1
end
end
DB.with_advisory_lock(lock_id) do
q2.push(nil)
q.pop
# Not guaranteed the wait: true call above is called before
# the block exits, but Thread.pass makes it more likely
Thread.pass
end
t.join
q.pop.must_equal 1
end
end
it "should raise Sequel::DatabaseError on invalid SQL" do
proc{@db << "S"}.must_raise(Sequel::DatabaseError)
end
it "should have Sequel::DatabaseError#sql give the SQL causing the error" do
(@db << "SELECT") rescue (e = $!)
e.sql.must_equal "SELECT"
end if ENV['SEQUEL_ERROR_SQL']
describe "constraint violations" do
before do
@db.drop_table?(:test2, :test)
end
after do
@db.drop_table?(:test2, :test)
end
cspecify "should raise Sequel::UniqueConstraintViolation when a unique constraint is violated", [:jdbc, :sqlite] do
@db.create_table!(:test){String :a, :unique=>true, :null=>false}
@db[:test].insert('1')
proc{@db[:test].insert('1')}.must_raise(Sequel::UniqueConstraintViolation)
@db[:test].insert('2')
proc{@db[:test].update(:a=>'1')}.must_raise(Sequel::UniqueConstraintViolation)
end
cspecify "should raise Sequel::UniqueConstraintViolation when a unique constraint is violated for composite primary keys", [:jdbc, :sqlite] do
@db.create_table!(:test){String :a; String :b; primary_key [:a, :b]}
@db[:test].insert(:a=>'1', :b=>'2')
proc{@db[:test].insert(:a=>'1', :b=>'2')}.must_raise(Sequel::UniqueConstraintViolation)
@db[:test].insert(:a=>'3', :b=>'4')
proc{@db[:test].update(:a=>'1', :b=>'2')}.must_raise(Sequel::UniqueConstraintViolation)
end
cspecify "should raise Sequel::CheckConstraintViolation when a check constraint is violated", [proc{|db| !db.mariadb? || db.server_version <= 100200}, :mysql], [proc{|db| db.sqlite_version < 30802}, :sqlite] do
@db.create_table!(:test){String :a; check Sequel.~(:a=>'1')}
proc{@db[:test].insert('1')}.must_raise(Sequel::CheckConstraintViolation)
@db[:test].insert('2')
proc{@db[:test].insert('1')}.must_raise(Sequel::CheckConstraintViolation)
end
cspecify "should raise Sequel::ForeignKeyConstraintViolation when a foreign key constraint is violated", [:jdbc, :sqlite] do
@db.create_table!(:test, :engine=>:InnoDB){primary_key :id}
@db.create_table!(:test2, :engine=>:InnoDB){foreign_key :tid, :test}
proc{@db[:test2].insert(:tid=>1)}.must_raise(Sequel::ForeignKeyConstraintViolation)
@db[:test].insert
@db[:test2].insert(:tid=>1)
proc{@db[:test2].where(:tid=>1).update(:tid=>3)}.must_raise(Sequel::ForeignKeyConstraintViolation)
proc{@db[:test].where(:id=>1).delete}.must_raise(Sequel::ForeignKeyConstraintViolation)
end
cspecify "should raise Sequel::NotNullConstraintViolation when a not null constraint is violated", [:jdbc, :sqlite] do
@db.create_table!(:test){Integer :a, :null=>false}
proc{@db[:test].insert(:a=>nil)}.must_raise(Sequel::NotNullConstraintViolation)
unless @db.database_type == :mysql
# Broken mysql silently changes NULL here to 0, and doesn't raise an exception.
@db[:test].insert(2)
proc{@db[:test].update(:a=>nil)}.must_raise(Sequel::NotNullConstraintViolation)
end
end
end
it "should store underlying wrapped exception in Sequel::DatabaseError" do
begin
@db << "SELECT"
rescue Sequel::DatabaseError=>e
if defined?(Java::JavaLang::Exception)
(e.wrapped_exception.is_a?(Exception) || e.wrapped_exception.is_a?(Java::JavaLang::Exception)).must_equal true
else
e.wrapped_exception.must_be_kind_of(Exception)
end
end
end
it "should not have the connection pool swallow non-StandardError based exceptions" do
proc{@db.pool.hold{raise Interrupt, "test"}}.must_raise(Interrupt)
end
it "should be able to disconnect connections more than once without exceptions" do
conn = @db.synchronize{|c| c}
@db.disconnect
@db.disconnect_connection(conn)
@db.disconnect_connection(conn)
end
it "should provide ability to check connections for validity" do
conn = @db.synchronize{|c| c}
@db.valid_connection?(conn).must_equal true
@db.disconnect
@db.valid_connection?(conn).must_equal false
end
end
|