sequel_core 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +74 -0
  2. data/COPYING +1 -0
  3. data/README +17 -6
  4. data/Rakefile +16 -21
  5. data/lib/sequel_core.rb +18 -28
  6. data/lib/sequel_core/adapters/ado.rb +3 -15
  7. data/lib/sequel_core/adapters/dbi.rb +1 -14
  8. data/lib/sequel_core/adapters/informix.rb +3 -3
  9. data/lib/sequel_core/adapters/jdbc.rb +2 -2
  10. data/lib/sequel_core/adapters/mysql.rb +39 -59
  11. data/lib/sequel_core/adapters/odbc.rb +18 -38
  12. data/lib/sequel_core/adapters/openbase.rb +1 -17
  13. data/lib/sequel_core/adapters/oracle.rb +1 -19
  14. data/lib/sequel_core/adapters/postgres.rb +20 -60
  15. data/lib/sequel_core/adapters/sqlite.rb +4 -8
  16. data/lib/sequel_core/connection_pool.rb +150 -0
  17. data/lib/sequel_core/core_ext.rb +41 -0
  18. data/lib/sequel_core/core_sql.rb +35 -38
  19. data/lib/sequel_core/database.rb +20 -17
  20. data/lib/sequel_core/dataset.rb +49 -80
  21. data/lib/sequel_core/dataset/callback.rb +11 -13
  22. data/lib/sequel_core/dataset/convenience.rb +18 -136
  23. data/lib/sequel_core/dataset/pagination.rb +81 -0
  24. data/lib/sequel_core/dataset/sequelizer.rb +5 -4
  25. data/lib/sequel_core/dataset/sql.rb +43 -33
  26. data/lib/sequel_core/deprecated.rb +200 -0
  27. data/lib/sequel_core/exceptions.rb +0 -14
  28. data/lib/sequel_core/object_graph.rb +199 -0
  29. data/lib/sequel_core/pretty_table.rb +27 -24
  30. data/lib/sequel_core/schema/generator.rb +16 -4
  31. data/lib/sequel_core/schema/sql.rb +5 -3
  32. data/lib/sequel_core/worker.rb +1 -1
  33. data/spec/adapters/informix_spec.rb +1 -47
  34. data/spec/adapters/mysql_spec.rb +85 -54
  35. data/spec/adapters/oracle_spec.rb +1 -57
  36. data/spec/adapters/postgres_spec.rb +66 -49
  37. data/spec/adapters/sqlite_spec.rb +4 -29
  38. data/spec/connection_pool_spec.rb +358 -0
  39. data/spec/core_sql_spec.rb +24 -19
  40. data/spec/database_spec.rb +13 -9
  41. data/spec/dataset_spec.rb +59 -78
  42. data/spec/object_graph_spec.rb +202 -0
  43. data/spec/pretty_table_spec.rb +1 -9
  44. data/spec/schema_generator_spec.rb +7 -1
  45. data/spec/schema_spec.rb +27 -0
  46. data/spec/sequelizer_spec.rb +2 -2
  47. data/spec/spec_helper.rb +4 -2
  48. metadata +16 -57
  49. data/lib/sequel_core/array_keys.rb +0 -322
  50. data/lib/sequel_core/model.rb +0 -8
  51. data/spec/array_keys_spec.rb +0 -682
@@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
2
  require File.join(File.dirname(__FILE__), '../spec_helper.rb')
3
3
 
4
4
  unless defined?(SQLITE_DB)
5
- SQLITE_DB = Sequel('sqlite:/')
5
+ SQLITE_DB = Sequel.connect('sqlite:/')
6
6
  end
7
7
 
8
8
  SQLITE_DB.create_table :items do
@@ -18,7 +18,7 @@ SQLITE_DB.create_table(:time) {timestamp :t}
18
18
 
19
19
  context "An SQLite database" do
20
20
  setup do
21
- @db = Sequel('sqlite:/')
21
+ @db = Sequel.connect('sqlite:/')
22
22
  end
23
23
 
24
24
  specify "should provide a list of existing tables" do
@@ -98,7 +98,7 @@ context "An SQLite database" do
98
98
 
99
99
  proc {@db.transaction do
100
100
  @db.create_table(:v) {text :name}
101
- rollback!
101
+ raise Sequel::Error::Rollback
102
102
  end}.should_not raise_error
103
103
  # no commit
104
104
  @db.tables.should == [:t]
@@ -116,7 +116,7 @@ context "An SQLite database" do
116
116
  proc {@db.transaction do
117
117
  @db.create_table(:v) {text :name}
118
118
  @db.transaction do
119
- rollback! # should roll back the top-level transaction
119
+ raise Sequel::Error::Rollback # should roll back the top-level transaction
120
120
  end
121
121
  end}.should_not raise_error
122
122
  # no commit
@@ -288,31 +288,6 @@ context "SQLite::Dataset#update" do
288
288
  end
289
289
  end
290
290
 
291
- context "An SQLite dataset in array tuples mode" do
292
- setup do
293
- @d = SQLITE_DB[:items]
294
- @d.delete # remove all records
295
-
296
- Sequel.use_array_tuples
297
- end
298
-
299
- teardown do
300
- Sequel.use_hash_tuples
301
- end
302
-
303
- specify "should return the correct records" do
304
- @d.to_a.should == []
305
- @d << {:name => 'abc', :value => 1.23}
306
- @d << {:name => 'abc', :value => 4.56}
307
- @d << {:name => 'def', :value => 7.89}
308
- @d.select(:name, :value).to_a.sort_by {|h| h[:value]}.should == [
309
- ['abc', 1.23],
310
- ['abc', 4.56],
311
- ['def', 7.89]
312
- ]
313
- end
314
- end
315
-
316
291
  context "SQLite dataset" do
317
292
  setup do
318
293
  SQLITE_DB.create_table :test do
@@ -0,0 +1,358 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "An empty ConnectionPool" do
4
+ setup do
5
+ @cpool = ConnectionPool.new
6
+ end
7
+
8
+ specify "should have no available connections" do
9
+ @cpool.available_connections.should == []
10
+ end
11
+
12
+ specify "should have no allocated connections" do
13
+ @cpool.allocated.should == {}
14
+ end
15
+
16
+ specify "should have a created_count of zero" do
17
+ @cpool.created_count.should == 0
18
+ end
19
+ end
20
+
21
+ context "A connection pool handling connections" do
22
+ setup do
23
+ @max_size = 2
24
+ @cpool = ConnectionPool.new(@max_size) {:got_connection}
25
+ end
26
+
27
+ specify "#hold should increment #created_count" do
28
+ @cpool.hold do
29
+ @cpool.created_count.should == 1
30
+ @cpool.hold {@cpool.created_count.should == 1}
31
+ end
32
+ end
33
+
34
+ specify "#hold should add the connection to the #allocated hash" do
35
+ @cpool.hold do
36
+ @cpool.allocated.size.should == 1
37
+
38
+ @cpool.allocated.values.should == [:got_connection]
39
+ end
40
+ end
41
+
42
+ specify "#hold should yield a new connection" do
43
+ @cpool.hold {|conn| conn.should == :got_connection}
44
+ end
45
+
46
+ specify "a connection should be de-allocated after it has been used in #hold" do
47
+ @cpool.hold {}
48
+ @cpool.allocated.size.should == 0
49
+ end
50
+
51
+ specify "#hold should return the value of its block" do
52
+ @cpool.hold {:block_return}.should == :block_return
53
+ end
54
+
55
+ specify "#make_new should not make more than max_size connections" do
56
+ @cpool.send(:make_new).should == :got_connection
57
+ @cpool.send(:make_new).should == :got_connection
58
+ @cpool.send(:make_new).should == nil
59
+ @cpool.created_count.should == 2
60
+ end
61
+ end
62
+
63
+ class DummyConnection
64
+ @@value = 0
65
+ def initialize
66
+ @@value += 1
67
+ end
68
+
69
+ def value
70
+ @@value
71
+ end
72
+ end
73
+
74
+ context "ConnectionPool#hold" do
75
+ setup do
76
+ @pool = ConnectionPool.new {DummyConnection.new}
77
+ end
78
+
79
+ specify "should pass the result of the connection maker proc to the supplied block" do
80
+ res = nil
81
+ @pool.hold {|c| res = c}
82
+ res.should be_a_kind_of(DummyConnection)
83
+ res.value.should == 1
84
+ @pool.hold {|c| res = c}
85
+ res.should be_a_kind_of(DummyConnection)
86
+ res.value.should == 1 # the connection maker is invoked only once
87
+ end
88
+
89
+ specify "should be re-entrant by the same thread" do
90
+ cc = nil
91
+ @pool.hold {|c| @pool.hold {|c| @pool.hold {|c| cc = c}}}
92
+ cc.should be_a_kind_of(DummyConnection)
93
+ end
94
+
95
+ specify "should catch exceptions and reraise them" do
96
+ proc {@pool.hold {|c| c.foobar}}.should raise_error(NoMethodError)
97
+ end
98
+
99
+ specify "should handle Exception errors (normally not caught by rescue)" do
100
+ err = nil
101
+ begin
102
+ @pool.hold {raise Exception}
103
+ rescue => e
104
+ err = e
105
+ end
106
+ err.should be_a_kind_of(RuntimeError)
107
+ end
108
+ end
109
+
110
+ context "ConnectionPool#connection_proc" do
111
+ setup do
112
+ @pool = ConnectionPool.new
113
+ end
114
+
115
+ specify "should be nil if no block is supplied to the pool" do
116
+ @pool.connection_proc.should be_nil
117
+ proc {@pool.hold {}}.should raise_error
118
+ end
119
+
120
+ specify "should be mutable" do
121
+ @pool.connection_proc = proc {'herro'}
122
+ res = nil
123
+ proc {@pool.hold {|c| res = c}}.should_not raise_error
124
+ res.should == 'herro'
125
+ end
126
+ end
127
+
128
+ context "A connection pool with a max size of 1" do
129
+ setup do
130
+ @invoked_count = 0
131
+ @pool = ConnectionPool.new(1) {@invoked_count += 1; 'herro'}
132
+ end
133
+
134
+ specify "should let only one thread access the connection at any time" do
135
+ cc,c1, c2 = nil
136
+
137
+ t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; while c == 'herro';sleep 0.1;end}}
138
+ sleep 0.2
139
+ cc.should == 'herro'
140
+ c1.should == 'herro'
141
+
142
+ t2 = Thread.new {@pool.hold {|c| c2 = c.dup; while c == 'hello';sleep 0.1;end}}
143
+ sleep 0.2
144
+
145
+ # connection held by t1
146
+ t1.should be_alive
147
+ t2.should be_alive
148
+
149
+ cc.should == 'herro'
150
+ c1.should == 'herro'
151
+ c2.should be_nil
152
+
153
+ @pool.available_connections.should be_empty
154
+ @pool.allocated.should == {t1 => cc}
155
+
156
+ cc.gsub!('rr', 'll')
157
+ sleep 0.5
158
+
159
+ # connection held by t2
160
+ t1.should_not be_alive
161
+ t2.should be_alive
162
+
163
+ c2.should == 'hello'
164
+
165
+ @pool.available_connections.should be_empty
166
+ @pool.allocated.should == {t2 => cc}
167
+
168
+ cc.gsub!('ll', 'rr')
169
+ sleep 0.5
170
+
171
+ #connection released
172
+ t2.should_not be_alive
173
+
174
+ cc.should == 'herro'
175
+
176
+ @invoked_count.should == 1
177
+ @pool.size.should == 1
178
+ @pool.available_connections.should == [cc]
179
+ @pool.allocated.should be_empty
180
+ end
181
+
182
+ specify "should let the same thread reenter #hold" do
183
+ c1, c2, c3 = nil
184
+ @pool.hold do |c|
185
+ c1 = c
186
+ @pool.hold do |c|
187
+ c2 = c
188
+ @pool.hold do |c|
189
+ c3 = c
190
+ end
191
+ end
192
+ end
193
+ c1.should == 'herro'
194
+ c2.should == 'herro'
195
+ c3.should == 'herro'
196
+
197
+ @invoked_count.should == 1
198
+ @pool.size.should == 1
199
+ @pool.available_connections.size.should == 1
200
+ @pool.allocated.should be_empty
201
+ end
202
+ end
203
+
204
+ context "A connection pool with a max size of 5" do
205
+ setup do
206
+ @invoked_count = 0
207
+ @pool = ConnectionPool.new(5) {@invoked_count += 1}
208
+ end
209
+
210
+ specify "should let five threads simultaneously access separate connections" do
211
+ cc = {}
212
+ threads = []
213
+ stop = nil
214
+
215
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
216
+ sleep 0.2
217
+ threads.each {|t| t.should be_alive}
218
+ cc.size.should == 5
219
+ @invoked_count.should == 5
220
+ @pool.size.should == 5
221
+ @pool.available_connections.should be_empty
222
+ @pool.allocated.should == {threads[0] => 1, threads[1] => 2, threads[2] => 3,
223
+ threads[3] => 4, threads[4] => 5}
224
+
225
+ threads[0].raise "your'e dead"
226
+ sleep 0.1
227
+ threads[3].raise "your'e dead too"
228
+
229
+ sleep 0.1
230
+
231
+ @pool.available_connections.should == [1, 4]
232
+ @pool.allocated.should == {threads[1] => 2, threads[2] => 3, threads[4] => 5}
233
+
234
+ stop = true
235
+ sleep 0.2
236
+
237
+ @pool.available_connections.size.should == 5
238
+ @pool.allocated.should be_empty
239
+ end
240
+
241
+ specify "should block threads until a connection becomes available" do
242
+ cc = {}
243
+ threads = []
244
+ stop = nil
245
+
246
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
247
+ sleep 0.2
248
+ threads.each {|t| t.should be_alive}
249
+ @pool.available_connections.should be_empty
250
+
251
+ 3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c}}}
252
+
253
+ sleep 0.2
254
+ threads[5].should be_alive
255
+ threads[6].should be_alive
256
+ threads[7].should be_alive
257
+ cc.size.should == 5
258
+ cc[5].should be_nil
259
+ cc[6].should be_nil
260
+ cc[7].should be_nil
261
+
262
+ stop = true
263
+ sleep 0.3
264
+
265
+ threads.each {|t| t.should_not be_alive}
266
+
267
+ @pool.size.should == 5
268
+ @invoked_count.should == 5
269
+ @pool.available_connections.size.should == 5
270
+ @pool.allocated.should be_empty
271
+ end
272
+ end
273
+
274
+ context "ConnectionPool#disconnect" do
275
+ setup do
276
+ @count = 0
277
+ @pool = ConnectionPool.new(5) {{:id => @count += 1}}
278
+ end
279
+
280
+ specify "should invoke the given block for each available connection" do
281
+ threads = []
282
+ stop = nil
283
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
284
+ while @pool.size < 5
285
+ sleep 0.2
286
+ end
287
+ stop = true
288
+ sleep 1
289
+ threads.each {|t| t.join}
290
+
291
+ @pool.size.should == 5
292
+ @pool.available_connections.size.should == 5
293
+ @pool.available_connections.each {|c| c[:id].should_not be_nil}
294
+ conns = []
295
+ @pool.disconnect {|c| conns << c}
296
+ conns.size.should == 5
297
+ end
298
+
299
+ specify "should remove all available connections" do
300
+ threads = []
301
+ stop = nil
302
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
303
+ while @pool.size < 5
304
+ sleep 0.2
305
+ end
306
+ stop = true
307
+ sleep 1
308
+ threads.each {|t| t.join}
309
+
310
+ @pool.size.should == 5
311
+ @pool.disconnect
312
+ @pool.size.should == 0
313
+ end
314
+
315
+ specify "should not touch connections in use" do
316
+ threads = []
317
+ stop = nil
318
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
319
+ while @pool.size < 5
320
+ sleep 0.2
321
+ end
322
+ stop = true
323
+ sleep 1
324
+ threads.each {|t| t.join}
325
+
326
+ @pool.size.should == 5
327
+
328
+ @pool.hold do |conn|
329
+ @pool.available_connections.size.should == 4
330
+ @pool.available_connections.each {|c| c.should_not be(conn)}
331
+ conns = []
332
+ @pool.disconnect {|c| conns << c}
333
+ conns.size.should == 4
334
+ end
335
+ @pool.size.should == 1
336
+ end
337
+ end
338
+
339
+ context "SingleThreadedPool" do
340
+ setup do
341
+ @pool = SingleThreadedPool.new {1234}
342
+ end
343
+
344
+ specify "should provide a #hold method" do
345
+ conn = nil
346
+ @pool.hold {|c| conn = c}
347
+ conn.should == 1234
348
+ end
349
+
350
+ specify "should provide a #disconnect method" do
351
+ @pool.hold {|c|}
352
+ @pool.conn.should == 1234
353
+ conn = nil
354
+ @pool.disconnect {|c| conn = c}
355
+ conn.should == 1234
356
+ @pool.conn.should be_nil
357
+ end
358
+ end
@@ -86,74 +86,62 @@ context "String#split_sql" do
86
86
  end
87
87
  end
88
88
 
89
- context "#DESC/#desc" do
89
+ context "#desc" do
90
90
  setup do
91
91
  @ds = Sequel::Dataset.new(nil)
92
92
  end
93
93
 
94
94
  specify "should format a DESC clause for a column ref" do
95
- :test.DESC.to_s(@ds).should == 'test DESC'
96
95
  :test.desc.to_s(@ds).should == 'test DESC'
97
96
 
98
- :items__price.DESC.to_s(@ds).should == 'items.price DESC'
99
97
  :items__price.desc.to_s(@ds).should == 'items.price DESC'
100
98
  end
101
99
 
102
100
  specify "should format a DESC clause for a function" do
103
- :avg[:test].DESC.to_s(@ds).should == 'avg(test) DESC'
104
101
  :avg[:test].desc.to_s(@ds).should == 'avg(test) DESC'
105
102
  end
106
103
 
107
104
  specify "should format a DESC clause for a literal value" do
108
- 1.DESC.to_s(@ds).should == '1 DESC'
109
105
  'abc'.desc.to_s(@ds).should == "'abc' DESC"
110
106
  end
111
107
  end
112
108
 
113
- context "#ASC/#asc" do
109
+ context "#asc" do
114
110
  setup do
115
111
  @ds = Sequel::Dataset.new(nil)
116
112
  end
117
113
 
118
114
  specify "should format a ASC clause for a column ref" do
119
- :test.ASC.to_s(@ds).should == 'test ASC'
120
115
  :test.asc.to_s(@ds).should == 'test ASC'
121
116
 
122
- :items__price.ASC.to_s(@ds).should == 'items.price ASC'
123
117
  :items__price.asc.to_s(@ds).should == 'items.price ASC'
124
118
  end
125
119
 
126
120
  specify "should format a ASC clause for a function" do
127
- :avg[:test].ASC.to_s(@ds).should == 'avg(test) ASC'
128
121
  :avg[:test].asc.to_s(@ds).should == 'avg(test) ASC'
129
122
  end
130
123
 
131
124
  specify "should format a ASC clause for a literal value" do
132
- 1.ASC.to_s(@ds).should == '1 ASC'
133
125
  'abc'.asc.to_s(@ds).should == "'abc' ASC"
134
126
  end
135
127
  end
136
128
 
137
- context "#AS/#as" do
129
+ context "#as" do
138
130
  setup do
139
131
  @ds = Sequel::Dataset.new(nil)
140
132
  end
141
133
 
142
134
  specify "should format a AS clause for a column ref" do
143
- :test.AS(:t).to_s(@ds).should == 'test AS t'
144
135
  :test.as(:t).to_s(@ds).should == 'test AS t'
145
136
 
146
- :items__price.AS(:p).to_s(@ds).should == 'items.price AS p'
147
137
  :items__price.as(:p).to_s(@ds).should == 'items.price AS p'
148
138
  end
149
139
 
150
140
  specify "should format a AS clause for a function" do
151
- :avg[:test].AS(:avg).to_s(@ds).should == 'avg(test) AS avg'
152
141
  :avg[:test].as(:avg).to_s(@ds).should == 'avg(test) AS avg'
153
142
  end
154
143
 
155
144
  specify "should format a AS clause for a literal value" do
156
- 1.AS(:one).to_s(@ds).should == '1 AS one'
157
145
  'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
158
146
  end
159
147
  end
@@ -184,7 +172,7 @@ context "Column references" do
184
172
  end
185
173
 
186
174
  specify "should be quoted properly in ASC/DESC clauses" do
187
- @ds.literal(:xyz.ASC).should == "`xyz` ASC"
175
+ @ds.literal(:xyz.asc).should == "`xyz` ASC"
188
176
  @ds.literal(:avg[:xyz, 1].desc).should == "avg(`xyz`, 1) DESC"
189
177
  end
190
178
 
@@ -194,14 +182,14 @@ context "Column references" do
194
182
  end
195
183
  end
196
184
 
197
- context "Symbol#ALL/#all" do
185
+ context "Symbol#*" do
198
186
  setup do
199
187
  @ds = Sequel::Dataset.new(nil)
200
188
  end
201
189
 
202
190
  specify "should format a qualified wildcard" do
203
- :xyz.ALL.to_s(@ds).should == 'xyz.*'
204
- :abc.all.to_s(@ds).should == 'abc.*'
191
+ :xyz.*.to_s(@ds).should == 'xyz.*'
192
+ :abc.*.to_s(@ds).should == 'abc.*'
205
193
  end
206
194
  end
207
195
 
@@ -233,6 +221,7 @@ context "Symbol#to_column_ref" do
233
221
  end
234
222
  end
235
223
 
224
+ ### DEPRECATED
236
225
  context "Symbol" do
237
226
  setup do
238
227
  @ds = Sequel::Dataset.new(nil)
@@ -309,3 +298,19 @@ context "String#to_date" do
309
298
  end
310
299
  end
311
300
 
301
+ context "Sequel::SQL::Function#==" do
302
+ specify "should be true for functions with the same name and arguments, false otherwise" do
303
+ a = :date[:t]
304
+ b = :date[:t]
305
+ a.should == b
306
+ (a == b).should == true
307
+ c = :date[:c]
308
+ a.should_not == c
309
+ (a == c).should == false
310
+ d = :time[:c]
311
+ a.should_not == d
312
+ c.should_not == d
313
+ (a == d).should == false
314
+ (c == d).should == false
315
+ end
316
+ end