sequel_core 1.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.
- data/CHANGELOG +1003 -0
- data/COPYING +18 -0
- data/README +81 -0
- data/Rakefile +176 -0
- data/bin/sequel +41 -0
- data/lib/sequel_core.rb +59 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
- data/lib/sequel_core/adapters/ado.rb +100 -0
- data/lib/sequel_core/adapters/db2.rb +158 -0
- data/lib/sequel_core/adapters/dbi.rb +126 -0
- data/lib/sequel_core/adapters/informix.rb +87 -0
- data/lib/sequel_core/adapters/jdbc.rb +108 -0
- data/lib/sequel_core/adapters/mysql.rb +269 -0
- data/lib/sequel_core/adapters/odbc.rb +145 -0
- data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
- data/lib/sequel_core/adapters/openbase.rb +90 -0
- data/lib/sequel_core/adapters/oracle.rb +99 -0
- data/lib/sequel_core/adapters/postgres.rb +519 -0
- data/lib/sequel_core/adapters/sqlite.rb +192 -0
- data/lib/sequel_core/array_keys.rb +296 -0
- data/lib/sequel_core/connection_pool.rb +152 -0
- data/lib/sequel_core/core_ext.rb +59 -0
- data/lib/sequel_core/core_sql.rb +191 -0
- data/lib/sequel_core/database.rb +433 -0
- data/lib/sequel_core/dataset.rb +409 -0
- data/lib/sequel_core/dataset/convenience.rb +321 -0
- data/lib/sequel_core/dataset/sequelizer.rb +354 -0
- data/lib/sequel_core/dataset/sql.rb +586 -0
- data/lib/sequel_core/exceptions.rb +45 -0
- data/lib/sequel_core/migration.rb +191 -0
- data/lib/sequel_core/model.rb +8 -0
- data/lib/sequel_core/pretty_table.rb +73 -0
- data/lib/sequel_core/schema.rb +8 -0
- data/lib/sequel_core/schema/schema_generator.rb +131 -0
- data/lib/sequel_core/schema/schema_sql.rb +131 -0
- data/lib/sequel_core/worker.rb +58 -0
- data/spec/adapters/informix_spec.rb +139 -0
- data/spec/adapters/mysql_spec.rb +330 -0
- data/spec/adapters/oracle_spec.rb +130 -0
- data/spec/adapters/postgres_spec.rb +189 -0
- data/spec/adapters/sqlite_spec.rb +345 -0
- data/spec/array_keys_spec.rb +679 -0
- data/spec/connection_pool_spec.rb +356 -0
- data/spec/core_ext_spec.rb +67 -0
- data/spec/core_sql_spec.rb +301 -0
- data/spec/database_spec.rb +812 -0
- data/spec/dataset_spec.rb +2381 -0
- data/spec/migration_spec.rb +261 -0
- data/spec/pretty_table_spec.rb +66 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema_generator_spec.rb +86 -0
- data/spec/schema_spec.rb +230 -0
- data/spec/sequelizer_spec.rb +448 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/worker_spec.rb +96 -0
- metadata +162 -0
@@ -0,0 +1,356 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
context "An empty ConnectionPool" do
|
4
|
+
setup do
|
5
|
+
@cpool = Sequel::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 = Sequel::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 = Sequel::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 be rescue)" do
|
100
|
+
begin
|
101
|
+
@pool.hold {raise Exception}
|
102
|
+
rescue => e
|
103
|
+
e.should be_a_kind_of(RuntimeError)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "ConnectionPool#connection_proc" do
|
109
|
+
setup do
|
110
|
+
@pool = Sequel::ConnectionPool.new
|
111
|
+
end
|
112
|
+
|
113
|
+
specify "should be nil if no block is supplied to the pool" do
|
114
|
+
@pool.connection_proc.should be_nil
|
115
|
+
proc {@pool.hold {}}.should raise_error
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "should be mutable" do
|
119
|
+
@pool.connection_proc = proc {'herro'}
|
120
|
+
res = nil
|
121
|
+
proc {@pool.hold {|c| res = c}}.should_not raise_error
|
122
|
+
res.should == 'herro'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "A connection pool with a max size of 1" do
|
127
|
+
setup do
|
128
|
+
@invoked_count = 0
|
129
|
+
@pool = Sequel::ConnectionPool.new(1) {@invoked_count += 1; 'herro'}
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "should let only one thread access the connection at any time" do
|
133
|
+
cc,c1, c2 = nil
|
134
|
+
|
135
|
+
t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; while c == 'herro';sleep 0.1;end}}
|
136
|
+
sleep 0.2
|
137
|
+
cc.should == 'herro'
|
138
|
+
c1.should == 'herro'
|
139
|
+
|
140
|
+
t2 = Thread.new {@pool.hold {|c| c2 = c.dup; while c == 'hello';sleep 0.1;end}}
|
141
|
+
sleep 0.2
|
142
|
+
|
143
|
+
# connection held by t1
|
144
|
+
t1.should be_alive
|
145
|
+
t2.should be_alive
|
146
|
+
|
147
|
+
cc.should == 'herro'
|
148
|
+
c1.should == 'herro'
|
149
|
+
c2.should be_nil
|
150
|
+
|
151
|
+
@pool.available_connections.should be_empty
|
152
|
+
@pool.allocated.should == {t1 => cc}
|
153
|
+
|
154
|
+
cc.gsub!('rr', 'll')
|
155
|
+
sleep 0.5
|
156
|
+
|
157
|
+
# connection held by t2
|
158
|
+
t1.should_not be_alive
|
159
|
+
t2.should be_alive
|
160
|
+
|
161
|
+
c2.should == 'hello'
|
162
|
+
|
163
|
+
@pool.available_connections.should be_empty
|
164
|
+
@pool.allocated.should == {t2 => cc}
|
165
|
+
|
166
|
+
cc.gsub!('ll', 'rr')
|
167
|
+
sleep 0.5
|
168
|
+
|
169
|
+
#connection released
|
170
|
+
t2.should_not be_alive
|
171
|
+
|
172
|
+
cc.should == 'herro'
|
173
|
+
|
174
|
+
@invoked_count.should == 1
|
175
|
+
@pool.size.should == 1
|
176
|
+
@pool.available_connections.should == [cc]
|
177
|
+
@pool.allocated.should be_empty
|
178
|
+
end
|
179
|
+
|
180
|
+
specify "should let the same thread reenter #hold" do
|
181
|
+
c1, c2, c3 = nil
|
182
|
+
@pool.hold do |c|
|
183
|
+
c1 = c
|
184
|
+
@pool.hold do |c|
|
185
|
+
c2 = c
|
186
|
+
@pool.hold do |c|
|
187
|
+
c3 = c
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
c1.should == 'herro'
|
192
|
+
c2.should == 'herro'
|
193
|
+
c3.should == 'herro'
|
194
|
+
|
195
|
+
@invoked_count.should == 1
|
196
|
+
@pool.size.should == 1
|
197
|
+
@pool.available_connections.size.should == 1
|
198
|
+
@pool.allocated.should be_empty
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "A connection pool with a max size of 5" do
|
203
|
+
setup do
|
204
|
+
@invoked_count = 0
|
205
|
+
@pool = Sequel::ConnectionPool.new(5) {@invoked_count += 1}
|
206
|
+
end
|
207
|
+
|
208
|
+
specify "should let five threads simultaneously access separate connections" do
|
209
|
+
cc = {}
|
210
|
+
threads = []
|
211
|
+
stop = nil
|
212
|
+
|
213
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
|
214
|
+
sleep 0.2
|
215
|
+
threads.each {|t| t.should be_alive}
|
216
|
+
cc.size.should == 5
|
217
|
+
@invoked_count.should == 5
|
218
|
+
@pool.size.should == 5
|
219
|
+
@pool.available_connections.should be_empty
|
220
|
+
@pool.allocated.should == {threads[0] => 1, threads[1] => 2, threads[2] => 3,
|
221
|
+
threads[3] => 4, threads[4] => 5}
|
222
|
+
|
223
|
+
threads[0].raise "your'e dead"
|
224
|
+
sleep 0.1
|
225
|
+
threads[3].raise "your'e dead too"
|
226
|
+
|
227
|
+
sleep 0.1
|
228
|
+
|
229
|
+
@pool.available_connections.should == [1, 4]
|
230
|
+
@pool.allocated.should == {threads[1] => 2, threads[2] => 3, threads[4] => 5}
|
231
|
+
|
232
|
+
stop = true
|
233
|
+
sleep 0.2
|
234
|
+
|
235
|
+
@pool.available_connections.size.should == 5
|
236
|
+
@pool.allocated.should be_empty
|
237
|
+
end
|
238
|
+
|
239
|
+
specify "should block threads until a connection becomes available" do
|
240
|
+
cc = {}
|
241
|
+
threads = []
|
242
|
+
stop = nil
|
243
|
+
|
244
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
|
245
|
+
sleep 0.2
|
246
|
+
threads.each {|t| t.should be_alive}
|
247
|
+
@pool.available_connections.should be_empty
|
248
|
+
|
249
|
+
3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c}}}
|
250
|
+
|
251
|
+
sleep 0.2
|
252
|
+
threads[5].should be_alive
|
253
|
+
threads[6].should be_alive
|
254
|
+
threads[7].should be_alive
|
255
|
+
cc.size.should == 5
|
256
|
+
cc[5].should be_nil
|
257
|
+
cc[6].should be_nil
|
258
|
+
cc[7].should be_nil
|
259
|
+
|
260
|
+
stop = true
|
261
|
+
sleep 0.3
|
262
|
+
|
263
|
+
threads.each {|t| t.should_not be_alive}
|
264
|
+
|
265
|
+
@pool.size.should == 5
|
266
|
+
@invoked_count.should == 5
|
267
|
+
@pool.available_connections.size.should == 5
|
268
|
+
@pool.allocated.should be_empty
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context "ConnectionPool#disconnect" do
|
273
|
+
setup do
|
274
|
+
@count = 0
|
275
|
+
@pool = Sequel::ConnectionPool.new(5) {{:id => @count += 1}}
|
276
|
+
end
|
277
|
+
|
278
|
+
specify "should invoke the given block for each available connection" do
|
279
|
+
threads = []
|
280
|
+
stop = nil
|
281
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
282
|
+
while @pool.size < 5
|
283
|
+
sleep 0.2
|
284
|
+
end
|
285
|
+
stop = true
|
286
|
+
sleep 1
|
287
|
+
threads.each {|t| t.join}
|
288
|
+
|
289
|
+
@pool.size.should == 5
|
290
|
+
@pool.available_connections.size.should == 5
|
291
|
+
@pool.available_connections.each {|c| c[:id].should_not be_nil}
|
292
|
+
conns = []
|
293
|
+
@pool.disconnect {|c| conns << c}
|
294
|
+
conns.size.should == 5
|
295
|
+
end
|
296
|
+
|
297
|
+
specify "should remove all available connections" do
|
298
|
+
threads = []
|
299
|
+
stop = nil
|
300
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
301
|
+
while @pool.size < 5
|
302
|
+
sleep 0.2
|
303
|
+
end
|
304
|
+
stop = true
|
305
|
+
sleep 1
|
306
|
+
threads.each {|t| t.join}
|
307
|
+
|
308
|
+
@pool.size.should == 5
|
309
|
+
@pool.disconnect
|
310
|
+
@pool.size.should == 0
|
311
|
+
end
|
312
|
+
|
313
|
+
specify "should not touch connections in use" do
|
314
|
+
threads = []
|
315
|
+
stop = nil
|
316
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
317
|
+
while @pool.size < 5
|
318
|
+
sleep 0.2
|
319
|
+
end
|
320
|
+
stop = true
|
321
|
+
sleep 1
|
322
|
+
threads.each {|t| t.join}
|
323
|
+
|
324
|
+
@pool.size.should == 5
|
325
|
+
|
326
|
+
@pool.hold do |conn|
|
327
|
+
@pool.available_connections.size.should == 4
|
328
|
+
@pool.available_connections.each {|c| c.should_not be(conn)}
|
329
|
+
conns = []
|
330
|
+
@pool.disconnect {|c| conns << c}
|
331
|
+
conns.size.should == 4
|
332
|
+
end
|
333
|
+
@pool.size.should == 1
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context "SingleThreadedPool" do
|
338
|
+
setup do
|
339
|
+
@pool = Sequel::SingleThreadedPool.new {1234}
|
340
|
+
end
|
341
|
+
|
342
|
+
specify "should provide a #hold method" do
|
343
|
+
conn = nil
|
344
|
+
@pool.hold {|c| conn = c}
|
345
|
+
conn.should == 1234
|
346
|
+
end
|
347
|
+
|
348
|
+
specify "should provide a #disconnect method" do
|
349
|
+
@pool.hold {|c|}
|
350
|
+
@pool.conn.should == 1234
|
351
|
+
conn = nil
|
352
|
+
@pool.disconnect {|c| conn = c}
|
353
|
+
conn.should == 1234
|
354
|
+
@pool.conn.should be_nil
|
355
|
+
end
|
356
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
context "Enumerable#send_each" do
|
4
|
+
specify "should send the supplied method to each item" do
|
5
|
+
a = ['abbc', 'bbccdd', 'hebtre']
|
6
|
+
a.send_each(:gsub!, 'b', '_')
|
7
|
+
a.should == ['a__c', '__ccdd', 'he_tre']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Range#interval" do
|
12
|
+
specify "should return the interval between the beginning and end of the range" do
|
13
|
+
(1..10).interval.should == 9
|
14
|
+
|
15
|
+
r = rand(100000) + 10
|
16
|
+
t1 = Time.now; t2 = t1 + r
|
17
|
+
(t1..t2).interval.should == r
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Numeric extensions" do
|
22
|
+
setup do
|
23
|
+
Sequel::NumericExtensions.enable
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "should support conversion of minutes to seconds" do
|
27
|
+
1.minute.should == 60
|
28
|
+
3.minutes.should == 180
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should support conversion of hours to seconds" do
|
32
|
+
1.hour.should == 3600
|
33
|
+
3.hours.should == 3600 * 3
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "should support conversion of days to seconds" do
|
37
|
+
1.day.should == 86400
|
38
|
+
3.days.should == 86400 * 3
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should support conversion of weeks to seconds" do
|
42
|
+
1.week.should == 86400 * 7
|
43
|
+
3.weeks.should == 86400 * 7 * 3
|
44
|
+
end
|
45
|
+
|
46
|
+
specify "should provide #ago functionality" do
|
47
|
+
t1 = Time.now
|
48
|
+
t2 = 1.day.ago
|
49
|
+
t1.should > t2
|
50
|
+
((t1 - t2).to_i - 86400).abs.should < 2
|
51
|
+
|
52
|
+
t1 = Time.now
|
53
|
+
t2 = 1.day.before(t1)
|
54
|
+
t2.should == t1 - 1.day
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "should provide #from_now functionality" do
|
58
|
+
t1 = Time.now
|
59
|
+
t2 = 1.day.from_now
|
60
|
+
t1.should < t2
|
61
|
+
((t2 - t1).to_i - 86400).abs.should < 2
|
62
|
+
|
63
|
+
t1 = Time.now
|
64
|
+
t2 = 1.day.since(t1)
|
65
|
+
t2.should == t1 + 1.day
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
context "Array#to_sql" do
|
4
|
+
specify "should concatenate multiple lines into a single string" do
|
5
|
+
"SELECT * \r\nFROM items\r\n WHERE a = 1".split.to_sql. \
|
6
|
+
should == 'SELECT * FROM items WHERE a = 1'
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "should remove superfluous white space and line breaks" do
|
10
|
+
"\tSELECT * \n FROM items ".split.to_sql. \
|
11
|
+
should == 'SELECT * FROM items'
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "should remove ANSI SQL comments" do
|
15
|
+
"SELECT * --comment\r\n FROM items\r\n --comment".split.to_sql. \
|
16
|
+
should == 'SELECT * FROM items'
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "should remove C-style comments" do
|
20
|
+
"SELECT * \r\n /* comment comment\r\n comment\r\n FROM items */\r\n FROM items\r\n--comment".split.to_sql. \
|
21
|
+
should == 'SELECT * FROM items'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "String#to_sql" do
|
26
|
+
specify "should concatenate multiple lines into a single string" do
|
27
|
+
"SELECT * \r\nFROM items\r\nWHERE a = 1".to_sql. \
|
28
|
+
should == 'SELECT * FROM items WHERE a = 1'
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should remove superfluous white space and line breaks" do
|
32
|
+
"\tSELECT * \r\n FROM items ".to_sql. \
|
33
|
+
should == 'SELECT * FROM items'
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "should remove ANSI SQL comments" do
|
37
|
+
"SELECT * --comment \r\n FROM items\r\n --comment".to_sql. \
|
38
|
+
should == 'SELECT * FROM items'
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should remove C-style comments" do
|
42
|
+
"SELECT * \r\n/* comment comment\r\ncomment\r\nFROM items */\r\nFROM items\r\n--comment".to_sql. \
|
43
|
+
should == 'SELECT * FROM items'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "String#lit" do
|
48
|
+
specify "should return an LiteralString object" do
|
49
|
+
'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
|
50
|
+
'xyz'.lit.to_s.should == 'xyz'
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should inhibit string literalization" do
|
54
|
+
db = Sequel::Database.new
|
55
|
+
ds = db[:t]
|
56
|
+
|
57
|
+
ds.update_sql(:stamp => "NOW()".lit).should == \
|
58
|
+
"UPDATE t SET stamp = NOW()"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "String#expr" do
|
63
|
+
specify "should return an LiteralString object" do
|
64
|
+
'xyz'.expr.should be_a_kind_of(Sequel::LiteralString)
|
65
|
+
'xyz'.expr.to_s.should == 'xyz'
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "should inhibit string literalization" do
|
69
|
+
db = Sequel::Database.new
|
70
|
+
ds = db[:t]
|
71
|
+
|
72
|
+
ds.update_sql(:stamp => "NOW()".expr).should == \
|
73
|
+
"UPDATE t SET stamp = NOW()"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "String#split_sql" do
|
78
|
+
specify "should split a string containing multiple statements" do
|
79
|
+
"DROP TABLE a; DROP TABLE c".split_sql.should == \
|
80
|
+
['DROP TABLE a', 'DROP TABLE c']
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should remove comments from the string" do
|
84
|
+
"DROP TABLE a;/* DROP TABLE b; DROP TABLE c;*/DROP TABLE d".split_sql.should == \
|
85
|
+
['DROP TABLE a', 'DROP TABLE d']
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "#DESC/#desc" do
|
90
|
+
setup do
|
91
|
+
@ds = Sequel::Dataset.new(nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
specify "should format a DESC clause for a column ref" do
|
95
|
+
:test.DESC.to_s(@ds).should == 'test DESC'
|
96
|
+
:test.desc.to_s(@ds).should == 'test DESC'
|
97
|
+
|
98
|
+
:items__price.DESC.to_s(@ds).should == 'items.price DESC'
|
99
|
+
:items__price.desc.to_s(@ds).should == 'items.price DESC'
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should format a DESC clause for a function" do
|
103
|
+
:avg[:test].DESC.to_s(@ds).should == 'avg(test) DESC'
|
104
|
+
:avg[:test].desc.to_s(@ds).should == 'avg(test) DESC'
|
105
|
+
end
|
106
|
+
|
107
|
+
specify "should format a DESC clause for a literal value" do
|
108
|
+
1.DESC.to_s(@ds).should == '1 DESC'
|
109
|
+
'abc'.desc.to_s(@ds).should == "'abc' DESC"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "#ASC/#asc" do
|
114
|
+
setup do
|
115
|
+
@ds = Sequel::Dataset.new(nil)
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "should format a ASC clause for a column ref" do
|
119
|
+
:test.ASC.to_s(@ds).should == 'test ASC'
|
120
|
+
:test.asc.to_s(@ds).should == 'test ASC'
|
121
|
+
|
122
|
+
:items__price.ASC.to_s(@ds).should == 'items.price ASC'
|
123
|
+
:items__price.asc.to_s(@ds).should == 'items.price ASC'
|
124
|
+
end
|
125
|
+
|
126
|
+
specify "should format a ASC clause for a function" do
|
127
|
+
:avg[:test].ASC.to_s(@ds).should == 'avg(test) ASC'
|
128
|
+
:avg[:test].asc.to_s(@ds).should == 'avg(test) ASC'
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should format a ASC clause for a literal value" do
|
132
|
+
1.ASC.to_s(@ds).should == '1 ASC'
|
133
|
+
'abc'.asc.to_s(@ds).should == "'abc' ASC"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "#AS/#as" do
|
138
|
+
setup do
|
139
|
+
@ds = Sequel::Dataset.new(nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "should format a AS clause for a column ref" do
|
143
|
+
:test.AS(:t).to_s(@ds).should == 'test AS t'
|
144
|
+
:test.as(:t).to_s(@ds).should == 'test AS t'
|
145
|
+
|
146
|
+
:items__price.AS(:p).to_s(@ds).should == 'items.price AS p'
|
147
|
+
:items__price.as(:p).to_s(@ds).should == 'items.price AS p'
|
148
|
+
end
|
149
|
+
|
150
|
+
specify "should format a AS clause for a function" do
|
151
|
+
:avg[:test].AS(:avg).to_s(@ds).should == 'avg(test) AS avg'
|
152
|
+
:avg[:test].as(:avg).to_s(@ds).should == 'avg(test) AS avg'
|
153
|
+
end
|
154
|
+
|
155
|
+
specify "should format a AS clause for a literal value" do
|
156
|
+
1.AS(:one).to_s(@ds).should == '1 AS one'
|
157
|
+
'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "Column references" do
|
162
|
+
setup do
|
163
|
+
@c = Class.new(Sequel::Dataset) do
|
164
|
+
def quote_column_ref(c); "`#{c}`"; end
|
165
|
+
end
|
166
|
+
@ds = @c.new(nil)
|
167
|
+
end
|
168
|
+
|
169
|
+
specify "should be quoted properly" do
|
170
|
+
@ds.literal(:xyz).should == "`xyz`"
|
171
|
+
@ds.literal(:xyz__abc).should == "xyz.`abc`"
|
172
|
+
|
173
|
+
@ds.literal(:xyz.as(:x)).should == "`xyz` AS `x`"
|
174
|
+
@ds.literal(:xyz__abc.as(:x)).should == "xyz.`abc` AS `x`"
|
175
|
+
|
176
|
+
@ds.literal(:xyz___x).should == "`xyz` AS `x`"
|
177
|
+
@ds.literal(:xyz__abc___x).should == "xyz.`abc` AS `x`"
|
178
|
+
end
|
179
|
+
|
180
|
+
specify "should be quoted properly in SQL functions" do
|
181
|
+
@ds.literal(:avg[:xyz]).should == "avg(`xyz`)"
|
182
|
+
@ds.literal(:avg[:xyz, 1]).should == "avg(`xyz`, 1)"
|
183
|
+
@ds.literal(:avg[:xyz].as(:a)).should == "avg(`xyz`) AS `a`"
|
184
|
+
end
|
185
|
+
|
186
|
+
specify "should be quoted properly in ASC/DESC clauses" do
|
187
|
+
@ds.literal(:xyz.ASC).should == "`xyz` ASC"
|
188
|
+
@ds.literal(:avg[:xyz, 1].desc).should == "avg(`xyz`, 1) DESC"
|
189
|
+
end
|
190
|
+
|
191
|
+
specify "should be quoted properly in a cast function" do
|
192
|
+
@ds.literal(:x.cast_as(:integer)).should == "cast(`x` AS integer)"
|
193
|
+
@ds.literal(:x__y.cast_as(:varchar[20])).should == "cast(x.`y` AS varchar(20))"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "Symbol#ALL/#all" do
|
198
|
+
setup do
|
199
|
+
@ds = Sequel::Dataset.new(nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
specify "should format a qualified wildcard" do
|
203
|
+
:xyz.ALL.to_s(@ds).should == 'xyz.*'
|
204
|
+
:abc.all.to_s(@ds).should == 'abc.*'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "Symbol#to_column_ref" do
|
209
|
+
setup do
|
210
|
+
@ds = Sequel::Dataset.new(nil)
|
211
|
+
end
|
212
|
+
|
213
|
+
specify "should convert qualified symbol notation into dot notation" do
|
214
|
+
:abc__def.to_column_ref(@ds).should == 'abc.def'
|
215
|
+
end
|
216
|
+
|
217
|
+
specify "should convert AS symbol notation into SQL AS notation" do
|
218
|
+
:xyz___x.to_column_ref(@ds).should == 'xyz AS x'
|
219
|
+
:abc__def___x.to_column_ref(@ds).should == 'abc.def AS x'
|
220
|
+
end
|
221
|
+
|
222
|
+
specify "should support names with digits" do
|
223
|
+
:abc2.to_column_ref(@ds).should == 'abc2'
|
224
|
+
:xx__yy3.to_column_ref(@ds).should == 'xx.yy3'
|
225
|
+
:ab34__temp3_4ax.to_column_ref(@ds).should == 'ab34.temp3_4ax'
|
226
|
+
:x1___y2.to_column_ref(@ds).should == 'x1 AS y2'
|
227
|
+
:abc2__def3___ggg4.to_column_ref(@ds).should == 'abc2.def3 AS ggg4'
|
228
|
+
end
|
229
|
+
|
230
|
+
specify "should support upper case and lower case" do
|
231
|
+
:ABC.to_column_ref(@ds).should == 'ABC'
|
232
|
+
:Zvashtoy__aBcD.to_column_ref(@ds).should == 'Zvashtoy.aBcD'
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "Symbol" do
|
237
|
+
setup do
|
238
|
+
@ds = Sequel::Dataset.new(nil)
|
239
|
+
end
|
240
|
+
|
241
|
+
specify "should support MIN for specifying min function" do
|
242
|
+
:abc__def.MIN.to_s(@ds).should == 'min(abc.def)'
|
243
|
+
end
|
244
|
+
|
245
|
+
specify "should support MAX for specifying max function" do
|
246
|
+
:abc__def.MAX.to_s(@ds).should == 'max(abc.def)'
|
247
|
+
end
|
248
|
+
|
249
|
+
specify "should support SUM for specifying sum function" do
|
250
|
+
:abc__def.SUM.to_s(@ds).should == 'sum(abc.def)'
|
251
|
+
end
|
252
|
+
|
253
|
+
specify "should support AVG for specifying avg function" do
|
254
|
+
:abc__def.AVG.to_s(@ds).should == 'avg(abc.def)'
|
255
|
+
end
|
256
|
+
|
257
|
+
specify "should support COUNT for specifying count function" do
|
258
|
+
:abc__def.COUNT.to_s(@ds).should == 'count(abc.def)'
|
259
|
+
end
|
260
|
+
|
261
|
+
specify "should support any other function using upper case letters" do
|
262
|
+
:abc__def.DADA.to_s(@ds).should == 'dada(abc.def)'
|
263
|
+
end
|
264
|
+
|
265
|
+
specify "should support upper case outer functions" do
|
266
|
+
:COUNT['1'].to_s(@ds).should == "COUNT('1')"
|
267
|
+
end
|
268
|
+
|
269
|
+
specify "should inhibit string literalization" do
|
270
|
+
db = Sequel::Database.new
|
271
|
+
ds = db[:t]
|
272
|
+
ds.select(:COUNT['1']).sql.should == "SELECT COUNT('1') FROM t"
|
273
|
+
end
|
274
|
+
|
275
|
+
specify "should support cast function" do
|
276
|
+
:abc.cast_as(:integer).to_s(@ds).should == "cast(abc AS integer)"
|
277
|
+
end
|
278
|
+
|
279
|
+
specify "should raise NoMethodError for non-uppercase invalid methods" do
|
280
|
+
proc {:abc.dfaxs}.should raise_error(NoMethodError)
|
281
|
+
end
|
282
|
+
|
283
|
+
specify "should support subscript access using | operator" do
|
284
|
+
(:abc|1).to_s(@ds).should == 'abc[1]'
|
285
|
+
(:abc|[1]).to_s(@ds).should == 'abc[1]'
|
286
|
+
(:abc|[1, 2]).to_s(@ds).should == 'abc[1, 2]'
|
287
|
+
(:abc|1|2).to_s(@ds).should == 'abc[1, 2]'
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
context "String#to_time" do
|
292
|
+
specify "should convert the string into a Time object" do
|
293
|
+
"2007-07-11".to_time.should == Time.parse("2007-07-11")
|
294
|
+
"06:30".to_time.should == Time.parse("06:30")
|
295
|
+
end
|
296
|
+
|
297
|
+
specify "should raise Error::InvalidValue for an invalid time" do
|
298
|
+
proc {'0000-00-00'.to_time}.should raise_error(Sequel::Error::InvalidValue)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|