sequel_core 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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