File: database_test.rb

package info (click to toggle)
ruby-sequel 5.97.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,188 kB
  • sloc: ruby: 123,115; makefile: 3
file content (153 lines) | stat: -rw-r--r-- 5,627 bytes parent folder | download
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