sequel_core 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +116 -0
- data/COPYING +19 -19
- data/README +83 -32
- data/Rakefile +9 -20
- data/bin/sequel +43 -112
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +257 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
- data/lib/sequel_core/adapters/ado.rb +3 -1
- data/lib/sequel_core/adapters/db2.rb +4 -2
- data/lib/sequel_core/adapters/dbi.rb +127 -113
- data/lib/sequel_core/adapters/informix.rb +4 -2
- data/lib/sequel_core/adapters/jdbc.rb +5 -3
- data/lib/sequel_core/adapters/mysql.rb +112 -46
- data/lib/sequel_core/adapters/odbc.rb +5 -7
- data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
- data/lib/sequel_core/adapters/openbase.rb +3 -1
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +261 -262
- data/lib/sequel_core/adapters/sqlite.rb +72 -22
- data/lib/sequel_core/connection_pool.rb +140 -73
- data/lib/sequel_core/core_ext.rb +201 -66
- data/lib/sequel_core/core_sql.rb +123 -153
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/database.rb +321 -338
- data/lib/sequel_core/dataset/callback.rb +11 -12
- data/lib/sequel_core/dataset/convenience.rb +213 -240
- data/lib/sequel_core/dataset/pagination.rb +58 -43
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sequelizer.rb +41 -373
- data/lib/sequel_core/dataset/sql.rb +741 -632
- data/lib/sequel_core/dataset.rb +183 -168
- data/lib/sequel_core/deprecated.rb +1 -169
- data/lib/sequel_core/exceptions.rb +24 -19
- data/lib/sequel_core/migration.rb +44 -52
- data/lib/sequel_core/object_graph.rb +43 -42
- data/lib/sequel_core/pretty_table.rb +71 -76
- data/lib/sequel_core/schema/generator.rb +163 -105
- data/lib/sequel_core/schema/sql.rb +250 -93
- data/lib/sequel_core/schema.rb +2 -8
- data/lib/sequel_core/sql.rb +394 -0
- data/lib/sequel_core/worker.rb +37 -27
- data/lib/sequel_core.rb +99 -45
- data/spec/adapters/informix_spec.rb +0 -1
- data/spec/adapters/mysql_spec.rb +177 -124
- data/spec/adapters/oracle_spec.rb +0 -1
- data/spec/adapters/postgres_spec.rb +98 -58
- data/spec/adapters/sqlite_spec.rb +45 -4
- data/spec/blockless_filters_spec.rb +269 -0
- data/spec/connection_pool_spec.rb +21 -18
- data/spec/core_ext_spec.rb +169 -19
- data/spec/core_sql_spec.rb +56 -49
- data/spec/database_spec.rb +78 -17
- data/spec/dataset_spec.rb +300 -428
- data/spec/migration_spec.rb +1 -1
- data/spec/object_graph_spec.rb +5 -11
- data/spec/rcov.opts +1 -1
- data/spec/schema_generator_spec.rb +16 -4
- data/spec/schema_spec.rb +89 -10
- data/spec/sequelizer_spec.rb +56 -56
- data/spec/spec.opts +0 -5
- data/spec/spec_config.rb +7 -0
- data/spec/spec_config.rb.example +5 -5
- data/spec/spec_helper.rb +6 -0
- data/spec/worker_spec.rb +1 -1
- metadata +78 -63
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
|
2
1
|
require File.join(File.dirname(__FILE__), '../spec_helper.rb')
|
3
|
-
require 'logger'
|
4
2
|
|
5
3
|
unless defined?(MYSQL_DB)
|
6
4
|
MYSQL_URL = (ENV['SEQUEL_MY_SPEC_DB']||'mysql://root@localhost/sandbox') unless defined? MYSQL_URL
|
@@ -11,18 +9,28 @@ unless defined?(MYSQL_SOCKET_FILE)
|
|
11
9
|
end
|
12
10
|
|
13
11
|
MYSQL_URI = URI.parse(MYSQL_DB.uri)
|
14
|
-
MYSQL_DB_NAME =
|
12
|
+
MYSQL_DB_NAME = (m = /\/(.*)/.match(MYSQL_URI.path)) && m[1]
|
15
13
|
|
16
|
-
MYSQL_DB.
|
17
|
-
MYSQL_DB.drop_table(:test2) if MYSQL_DB.table_exists?(:test2)
|
18
|
-
MYSQL_DB.create_table :items do
|
14
|
+
MYSQL_DB.create_table! :items do
|
19
15
|
text :name
|
20
16
|
integer :value, :index => true
|
21
17
|
end
|
22
|
-
MYSQL_DB.create_table :test2 do
|
18
|
+
MYSQL_DB.create_table! :test2 do
|
23
19
|
text :name
|
24
20
|
integer :value
|
25
21
|
end
|
22
|
+
MYSQL_DB.create_table! :booltest do
|
23
|
+
tinyint :value
|
24
|
+
end
|
25
|
+
class Sequel::MySQL::Database
|
26
|
+
attr_accessor :sqls
|
27
|
+
end
|
28
|
+
logger = Object.new
|
29
|
+
def logger.method_missing(m, msg)
|
30
|
+
MYSQL_DB.sqls << msg
|
31
|
+
end
|
32
|
+
MYSQL_DB.logger = logger
|
33
|
+
MYSQL_DB.sqls = []
|
26
34
|
|
27
35
|
context "A MySQL database" do
|
28
36
|
setup do
|
@@ -51,12 +59,25 @@ context "A MySQL database" do
|
|
51
59
|
{:id => 3, :name => 'ghi'}
|
52
60
|
]
|
53
61
|
end
|
62
|
+
|
63
|
+
specify "should convert Mysql::Errors to Sequel::Errors" do
|
64
|
+
proc{@db << "SELECT 1 + blah;"}.should raise_error(Sequel::Error)
|
65
|
+
end
|
66
|
+
|
67
|
+
specify "should correctly parse the schema" do
|
68
|
+
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"tinyint", :numeric_precision=>3}]]
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "should get the schema all database tables if no table name is used" do
|
72
|
+
@db.schema(:booltest, :reload=>true).should == @db.schema(nil, :reload=>true)[:booltest]
|
73
|
+
end
|
54
74
|
end
|
55
75
|
|
56
76
|
context "A MySQL dataset" do
|
57
77
|
setup do
|
58
78
|
@d = MYSQL_DB[:items]
|
59
79
|
@d.delete # remove all records
|
80
|
+
MYSQL_DB.sqls.clear
|
60
81
|
end
|
61
82
|
|
62
83
|
specify "should return the correct record count" do
|
@@ -107,72 +128,74 @@ context "A MySQL dataset" do
|
|
107
128
|
proc {@d.literal(false)}.should_not raise_error
|
108
129
|
end
|
109
130
|
|
110
|
-
specify "should quote columns using back-ticks" do
|
131
|
+
specify "should quote columns and tables using back-ticks if quoting identifiers" do
|
132
|
+
@d.quote_identifiers = true
|
111
133
|
@d.select(:name).sql.should == \
|
112
|
-
'SELECT `name` FROM items'
|
134
|
+
'SELECT `name` FROM `items`'
|
113
135
|
|
114
136
|
@d.select('COUNT(*)'.lit).sql.should == \
|
115
|
-
'SELECT COUNT(*) FROM items'
|
137
|
+
'SELECT COUNT(*) FROM `items`'
|
116
138
|
|
117
139
|
@d.select(:max[:value]).sql.should == \
|
118
|
-
'SELECT max(`value`) FROM items'
|
140
|
+
'SELECT max(`value`) FROM `items`'
|
119
141
|
|
120
142
|
@d.select(:NOW[]).sql.should == \
|
121
|
-
'SELECT NOW() FROM items'
|
143
|
+
'SELECT NOW() FROM `items`'
|
122
144
|
|
123
145
|
@d.select(:max[:items__value]).sql.should == \
|
124
|
-
'SELECT max(items
|
146
|
+
'SELECT max(`items`.`value`) FROM `items`'
|
125
147
|
|
126
148
|
@d.order(:name.desc).sql.should == \
|
127
|
-
'SELECT * FROM items ORDER BY `name` DESC'
|
149
|
+
'SELECT * FROM `items` ORDER BY `name` DESC'
|
128
150
|
|
129
151
|
@d.select('items.name AS item_name'.lit).sql.should == \
|
130
|
-
'SELECT items.name AS item_name FROM items'
|
152
|
+
'SELECT items.name AS item_name FROM `items`'
|
131
153
|
|
132
154
|
@d.select('`name`'.lit).sql.should == \
|
133
|
-
'SELECT `name` FROM items'
|
155
|
+
'SELECT `name` FROM `items`'
|
134
156
|
|
135
157
|
@d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
|
136
|
-
'SELECT max(items.`name`) AS `max_name` FROM items'
|
158
|
+
'SELECT max(items.`name`) AS `max_name` FROM `items`'
|
137
159
|
|
138
160
|
@d.select(:test[:abc, 'hello']).sql.should == \
|
139
|
-
"SELECT test(`abc`, 'hello') FROM items"
|
161
|
+
"SELECT test(`abc`, 'hello') FROM `items`"
|
140
162
|
|
141
163
|
@d.select(:test[:abc__def, 'hello']).sql.should == \
|
142
|
-
"SELECT test(abc
|
164
|
+
"SELECT test(`abc`.`def`, 'hello') FROM `items`"
|
143
165
|
|
144
166
|
@d.select(:test[:abc__def, 'hello'].as(:x2)).sql.should == \
|
145
|
-
"SELECT test(abc
|
167
|
+
"SELECT test(`abc`.`def`, 'hello') AS `x2` FROM `items`"
|
146
168
|
|
147
169
|
@d.insert_sql(:value => 333).should == \
|
148
|
-
'INSERT INTO items (`value`) VALUES (333)'
|
170
|
+
'INSERT INTO `items` (`value`) VALUES (333)'
|
149
171
|
|
150
172
|
@d.insert_sql(:x => :y).should == \
|
151
|
-
'INSERT INTO items (`x`) VALUES (`y`)'
|
173
|
+
'INSERT INTO `items` (`x`) VALUES (`y`)'
|
152
174
|
end
|
153
175
|
|
154
176
|
specify "should quote fields correctly when reversing the order" do
|
177
|
+
@d.quote_identifiers = true
|
155
178
|
@d.reverse_order(:name).sql.should == \
|
156
|
-
'SELECT * FROM items ORDER BY `name` DESC'
|
179
|
+
'SELECT * FROM `items` ORDER BY `name` DESC'
|
157
180
|
|
158
181
|
@d.reverse_order(:name.desc).sql.should == \
|
159
|
-
'SELECT * FROM items ORDER BY `name`'
|
182
|
+
'SELECT * FROM `items` ORDER BY `name`'
|
160
183
|
|
161
184
|
@d.reverse_order(:name, :test.desc).sql.should == \
|
162
|
-
'SELECT * FROM items ORDER BY `name` DESC, `test`'
|
185
|
+
'SELECT * FROM `items` ORDER BY `name` DESC, `test`'
|
163
186
|
|
164
187
|
@d.reverse_order(:name.desc, :test).sql.should == \
|
165
|
-
'SELECT * FROM items ORDER BY `name`, `test` DESC'
|
188
|
+
'SELECT * FROM `items` ORDER BY `name`, `test` DESC'
|
166
189
|
end
|
167
190
|
|
168
191
|
specify "should support ORDER clause in UPDATE statements" do
|
169
192
|
@d.order(:name).update_sql(:value => 1).should == \
|
170
|
-
'UPDATE items SET
|
193
|
+
'UPDATE items SET value = 1 ORDER BY name'
|
171
194
|
end
|
172
195
|
|
173
196
|
specify "should support LIMIT clause in UPDATE statements" do
|
174
197
|
@d.limit(10).update_sql(:value => 1).should == \
|
175
|
-
'UPDATE items SET
|
198
|
+
'UPDATE items SET value = 1 LIMIT 10'
|
176
199
|
end
|
177
200
|
|
178
201
|
specify "should support transactions" do
|
@@ -183,6 +206,29 @@ context "A MySQL dataset" do
|
|
183
206
|
@d.count.should == 1
|
184
207
|
end
|
185
208
|
|
209
|
+
specify "should correctly rollback transactions" do
|
210
|
+
proc do
|
211
|
+
MYSQL_DB.transaction do
|
212
|
+
@d << {:name => 'abc'}
|
213
|
+
raise Interrupt, 'asdf'
|
214
|
+
end
|
215
|
+
end.should raise_error(Interrupt)
|
216
|
+
|
217
|
+
MYSQL_DB.sqls.should == ['BEGIN', "INSERT INTO items (name) VALUES ('abc')", 'ROLLBACK']
|
218
|
+
end
|
219
|
+
|
220
|
+
specify "should handle returning inside of the block by committing" do
|
221
|
+
def MYSQL_DB.ret_commit
|
222
|
+
transaction do
|
223
|
+
self[:items] << {:name => 'abc'}
|
224
|
+
return
|
225
|
+
self[:items] << {:name => 'd'}
|
226
|
+
end
|
227
|
+
end
|
228
|
+
MYSQL_DB.ret_commit
|
229
|
+
MYSQL_DB.sqls.should == ['BEGIN', "INSERT INTO items (name) VALUES ('abc')", 'COMMIT']
|
230
|
+
end
|
231
|
+
|
186
232
|
specify "should support regexps" do
|
187
233
|
@d << {:name => 'abc', :value => 1}
|
188
234
|
@d << {:name => 'bcd', :value => 2}
|
@@ -204,6 +250,7 @@ context "MySQL datasets" do
|
|
204
250
|
end
|
205
251
|
|
206
252
|
specify "should correctly quote column references" do
|
253
|
+
@d.quote_identifiers = true
|
207
254
|
market = 'ICE'
|
208
255
|
ack_stamp = Time.now - 15 * 60 # 15 minutes ago
|
209
256
|
@d.query do
|
@@ -214,7 +261,16 @@ context "MySQL datasets" do
|
|
214
261
|
end
|
215
262
|
group_by :minute[:from_unixtime[:ack]]
|
216
263
|
end.sql.should == \
|
217
|
-
"SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM orders WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
|
264
|
+
"SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM `orders` WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
|
265
|
+
end
|
266
|
+
|
267
|
+
specify "should accept and return tinyints as bools" do
|
268
|
+
MYSQL_DB[:booltest].delete
|
269
|
+
MYSQL_DB[:booltest] << {:value=>true}
|
270
|
+
MYSQL_DB[:booltest].all.should == [{:value=>true}]
|
271
|
+
MYSQL_DB[:booltest].delete
|
272
|
+
MYSQL_DB[:booltest] << {:value=>false}
|
273
|
+
MYSQL_DB[:booltest].all.should == [{:value=>false}]
|
218
274
|
end
|
219
275
|
end
|
220
276
|
|
@@ -240,67 +296,67 @@ context "MySQL join expressions" do
|
|
240
296
|
end
|
241
297
|
|
242
298
|
specify "should raise error for :full_outer join requests." do
|
243
|
-
lambda{@ds.
|
299
|
+
lambda{@ds.join_table(:full_outer, :nodes)}.should raise_error(Sequel::Error::InvalidJoinType)
|
244
300
|
end
|
245
301
|
specify "should support natural left joins" do
|
246
|
-
@ds.
|
247
|
-
'NATURAL LEFT JOIN nodes'
|
302
|
+
@ds.join_table(:natural_left, :nodes).sql.should == \
|
303
|
+
'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
|
248
304
|
end
|
249
305
|
specify "should support natural right joins" do
|
250
|
-
@ds.
|
251
|
-
'NATURAL RIGHT JOIN nodes'
|
306
|
+
@ds.join_table(:natural_right, :nodes).sql.should == \
|
307
|
+
'SELECT * FROM nodes NATURAL RIGHT JOIN nodes'
|
252
308
|
end
|
253
309
|
specify "should support natural left outer joins" do
|
254
|
-
@ds.
|
255
|
-
'NATURAL LEFT OUTER JOIN nodes'
|
310
|
+
@ds.join_table(:natural_left_outer, :nodes).sql.should == \
|
311
|
+
'SELECT * FROM nodes NATURAL LEFT OUTER JOIN nodes'
|
256
312
|
end
|
257
313
|
specify "should support natural right outer joins" do
|
258
|
-
@ds.
|
259
|
-
'NATURAL RIGHT OUTER JOIN nodes'
|
314
|
+
@ds.join_table(:natural_right_outer, :nodes).sql.should == \
|
315
|
+
'SELECT * FROM nodes NATURAL RIGHT OUTER JOIN nodes'
|
260
316
|
end
|
261
317
|
specify "should support natural inner joins" do
|
262
|
-
@ds.
|
263
|
-
'NATURAL LEFT JOIN nodes'
|
318
|
+
@ds.join_table(:natural_inner, :nodes).sql.should == \
|
319
|
+
'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
|
264
320
|
end
|
265
321
|
specify "should support cross joins (equivalent to inner join in MySQL, not in std SQL)" do
|
266
|
-
@ds.
|
267
|
-
'INNER JOIN nodes'
|
322
|
+
@ds.join_table(:cross, :nodes).sql.should == \
|
323
|
+
'SELECT * FROM nodes INNER JOIN nodes'
|
268
324
|
end
|
269
325
|
specify "should support straight joins (force left table to be read before right)" do
|
270
|
-
@ds.
|
271
|
-
'STRAIGHT_JOIN nodes'
|
326
|
+
@ds.join_table(:straight, :nodes).sql.should == \
|
327
|
+
'SELECT * FROM nodes STRAIGHT_JOIN nodes'
|
272
328
|
end
|
273
329
|
specify "should support natural joins on multiple tables." do
|
274
|
-
@ds.
|
275
|
-
'NATURAL LEFT OUTER JOIN (
|
330
|
+
@ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
|
331
|
+
'SELECT * FROM nodes NATURAL LEFT OUTER JOIN ( nodes, branches )'
|
276
332
|
end
|
277
333
|
specify "should support straight joins on multiple tables." do
|
278
|
-
@ds.
|
279
|
-
'STRAIGHT_JOIN (
|
334
|
+
@ds.join_table(:straight, [:nodes,:branches]).sql.should == \
|
335
|
+
'SELECT * FROM nodes STRAIGHT_JOIN ( nodes, branches )'
|
280
336
|
end
|
281
337
|
end
|
282
338
|
|
283
339
|
context "Joined MySQL dataset" do
|
284
340
|
setup do
|
285
|
-
@ds = MYSQL_DB[:nodes]
|
286
|
-
@ds2 = MYSQL_DB[:nodes]
|
341
|
+
@ds = MYSQL_DB[:nodes]
|
287
342
|
end
|
288
343
|
|
289
344
|
specify "should quote fields correctly" do
|
290
|
-
@ds.
|
291
|
-
|
345
|
+
@ds.quote_identifiers = true
|
346
|
+
@ds.join(:attributes, :node_id => :id).sql.should == \
|
347
|
+
"SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
|
292
348
|
end
|
293
349
|
|
294
350
|
specify "should allow a having clause on ungrouped datasets" do
|
295
|
-
proc {@
|
351
|
+
proc {@ds.having('blah')}.should_not raise_error
|
296
352
|
|
297
|
-
@
|
298
|
-
"SELECT * FROM nodes HAVING blah"
|
353
|
+
@ds.having('blah').sql.should == \
|
354
|
+
"SELECT * FROM nodes HAVING (blah)"
|
299
355
|
end
|
300
356
|
|
301
357
|
specify "should put a having clause before an order by clause" do
|
302
|
-
@
|
303
|
-
"SELECT * FROM nodes HAVING (
|
358
|
+
@ds.order(:aaa).having(:bbb => :ccc).sql.should == \
|
359
|
+
"SELECT * FROM nodes HAVING (bbb = ccc) ORDER BY aaa"
|
304
360
|
end
|
305
361
|
end
|
306
362
|
|
@@ -376,7 +432,7 @@ context "A MySQL database" do
|
|
376
432
|
end
|
377
433
|
statements = @db.create_table_sql_list(:items, *g.create_info)
|
378
434
|
statements.should == [
|
379
|
-
"CREATE TABLE items (
|
435
|
+
"CREATE TABLE items (active1 boolean DEFAULT 1, active2 boolean DEFAULT 0)"
|
380
436
|
]
|
381
437
|
end
|
382
438
|
|
@@ -386,7 +442,7 @@ context "A MySQL database" do
|
|
386
442
|
:null => false, :on_delete => :cascade
|
387
443
|
end
|
388
444
|
@db.create_table_sql_list(:items, *g.create_info).should == [
|
389
|
-
"CREATE TABLE items (
|
445
|
+
"CREATE TABLE items (p_id integer NOT NULL, FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE)"
|
390
446
|
]
|
391
447
|
end
|
392
448
|
|
@@ -402,20 +458,23 @@ context "A MySQL database" do
|
|
402
458
|
end
|
403
459
|
end
|
404
460
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
461
|
+
# Socket tests should only be run if the MySQL server is on localhost
|
462
|
+
if %w'localhost 127.0.0.1 ::1'.include? MYSQL_URI.host
|
463
|
+
context "A MySQL database" do
|
464
|
+
specify "should accept a socket option" do
|
465
|
+
db = Sequel.mysql(MYSQL_DB_NAME, :host => 'localhost', :user => 'root', :socket => MYSQL_SOCKET_FILE)
|
466
|
+
proc {db.test_connection}.should_not raise_error
|
467
|
+
end
|
468
|
+
|
469
|
+
specify "should accept a socket option without host option" do
|
470
|
+
db = Sequel.mysql(MYSQL_DB_NAME, :user => 'root', :socket => MYSQL_SOCKET_FILE)
|
471
|
+
proc {db.test_connection}.should_not raise_error
|
472
|
+
end
|
473
|
+
|
474
|
+
specify "should fail to connect with invalid socket" do
|
475
|
+
db = Sequel.mysql(MYSQL_DB_NAME, :host => 'localhost', :user => 'root', :socket => 'blah')
|
476
|
+
proc {db.test_connection}.should raise_error
|
477
|
+
end
|
419
478
|
end
|
420
479
|
end
|
421
480
|
|
@@ -452,20 +511,20 @@ context "A MySQL database" do
|
|
452
511
|
full_text_index [:title, :body]
|
453
512
|
end
|
454
513
|
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
455
|
-
"CREATE TABLE posts (
|
456
|
-
"CREATE FULLTEXT INDEX posts_title_body_index ON posts (
|
514
|
+
"CREATE TABLE posts (title text, body text)",
|
515
|
+
"CREATE FULLTEXT INDEX posts_title_body_index ON posts (title, body)"
|
457
516
|
]
|
458
517
|
end
|
459
518
|
|
460
519
|
specify "should support full_text_search" do
|
461
520
|
MYSQL_DB[:posts].full_text_search(:title, 'ruby').sql.should ==
|
462
|
-
"SELECT * FROM posts WHERE (MATCH (
|
521
|
+
"SELECT * FROM posts WHERE (MATCH (title) AGAINST ('ruby'))"
|
463
522
|
|
464
523
|
MYSQL_DB[:posts].full_text_search([:title, :body], ['ruby', 'sequel']).sql.should ==
|
465
|
-
"SELECT * FROM posts WHERE (MATCH (
|
524
|
+
"SELECT * FROM posts WHERE (MATCH (title, body) AGAINST ('ruby', 'sequel'))"
|
466
525
|
|
467
526
|
MYSQL_DB[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).sql.should ==
|
468
|
-
"SELECT * FROM posts WHERE (MATCH (
|
527
|
+
"SELECT * FROM posts WHERE (MATCH (title) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"
|
469
528
|
end
|
470
529
|
|
471
530
|
specify "should support spatial indexes" do
|
@@ -474,8 +533,8 @@ context "A MySQL database" do
|
|
474
533
|
spatial_index [:geom]
|
475
534
|
end
|
476
535
|
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
477
|
-
"CREATE TABLE posts (
|
478
|
-
"CREATE SPATIAL INDEX posts_geom_index ON posts (
|
536
|
+
"CREATE TABLE posts (geom point)",
|
537
|
+
"CREATE SPATIAL INDEX posts_geom_index ON posts (geom)"
|
479
538
|
]
|
480
539
|
end
|
481
540
|
|
@@ -485,8 +544,8 @@ context "A MySQL database" do
|
|
485
544
|
index :title, :type => :hash
|
486
545
|
end
|
487
546
|
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
488
|
-
"CREATE TABLE posts (
|
489
|
-
"CREATE INDEX posts_title_index ON posts (
|
547
|
+
"CREATE TABLE posts (title text)",
|
548
|
+
"CREATE INDEX posts_title_index ON posts (title) USING hash"
|
490
549
|
]
|
491
550
|
end
|
492
551
|
|
@@ -496,45 +555,12 @@ context "A MySQL database" do
|
|
496
555
|
index :title, :type => :hash, :unique => true
|
497
556
|
end
|
498
557
|
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
499
|
-
"CREATE TABLE posts (
|
500
|
-
"CREATE UNIQUE INDEX posts_title_index ON posts (
|
558
|
+
"CREATE TABLE posts (title text)",
|
559
|
+
"CREATE UNIQUE INDEX posts_title_index ON posts (title) USING hash"
|
501
560
|
]
|
502
561
|
end
|
503
562
|
end
|
504
563
|
|
505
|
-
class Sequel::MySQL::Database
|
506
|
-
alias_method :orig_execute, :execute
|
507
|
-
attr_accessor :sqls
|
508
|
-
def execute(sql, &block)
|
509
|
-
@sqls ||= []; @sqls << sql
|
510
|
-
orig_execute(sql, &block)
|
511
|
-
end
|
512
|
-
|
513
|
-
def transaction
|
514
|
-
@pool.hold do |conn|
|
515
|
-
@transactions ||= []
|
516
|
-
if @transactions.include? Thread.current
|
517
|
-
return yield(conn)
|
518
|
-
end
|
519
|
-
@sqls ||= []; @sqls << SQL_BEGIN
|
520
|
-
conn.query(SQL_BEGIN)
|
521
|
-
begin
|
522
|
-
@transactions << Thread.current
|
523
|
-
result = yield(conn)
|
524
|
-
@sqls ||= []; @sqls << SQL_COMMIT
|
525
|
-
conn.query(SQL_COMMIT)
|
526
|
-
result
|
527
|
-
rescue => e
|
528
|
-
@sqls ||= []; @sqls << SQL_ROLLBACK
|
529
|
-
conn.query(SQL_ROLLBACK)
|
530
|
-
raise e unless Sequel::Error::Rollback === e
|
531
|
-
ensure
|
532
|
-
@transactions.delete(Thread.current)
|
533
|
-
end
|
534
|
-
end
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
564
|
context "MySQL::Dataset#insert" do
|
539
565
|
setup do
|
540
566
|
@d = MYSQL_DB[:items]
|
@@ -555,7 +581,7 @@ context "MySQL::Dataset#insert" do
|
|
555
581
|
end
|
556
582
|
|
557
583
|
specify "should insert record with default values when empty hash given" do
|
558
|
-
@d.insert
|
584
|
+
@d.insert({})
|
559
585
|
|
560
586
|
MYSQL_DB.sqls.should == [
|
561
587
|
"INSERT INTO items () VALUES ()"
|
@@ -591,7 +617,7 @@ context "MySQL::Dataset#multi_insert" do
|
|
591
617
|
|
592
618
|
MYSQL_DB.sqls.should == [
|
593
619
|
'BEGIN',
|
594
|
-
"INSERT INTO items (
|
620
|
+
"INSERT INTO items (name) VALUES ('abc'), ('def')",
|
595
621
|
'COMMIT'
|
596
622
|
]
|
597
623
|
|
@@ -606,10 +632,10 @@ context "MySQL::Dataset#multi_insert" do
|
|
606
632
|
|
607
633
|
MYSQL_DB.sqls.should == [
|
608
634
|
'BEGIN',
|
609
|
-
"INSERT INTO items (
|
635
|
+
"INSERT INTO items (value) VALUES (1), (2)",
|
610
636
|
'COMMIT',
|
611
637
|
'BEGIN',
|
612
|
-
"INSERT INTO items (
|
638
|
+
"INSERT INTO items (value) VALUES (3), (4)",
|
613
639
|
'COMMIT'
|
614
640
|
]
|
615
641
|
|
@@ -627,10 +653,10 @@ context "MySQL::Dataset#multi_insert" do
|
|
627
653
|
|
628
654
|
MYSQL_DB.sqls.should == [
|
629
655
|
'BEGIN',
|
630
|
-
"INSERT INTO items (
|
656
|
+
"INSERT INTO items (value) VALUES (1), (2)",
|
631
657
|
'COMMIT',
|
632
658
|
'BEGIN',
|
633
|
-
"INSERT INTO items (
|
659
|
+
"INSERT INTO items (value) VALUES (3), (4)",
|
634
660
|
'COMMIT'
|
635
661
|
]
|
636
662
|
|
@@ -647,7 +673,7 @@ context "MySQL::Dataset#multi_insert" do
|
|
647
673
|
|
648
674
|
MYSQL_DB.sqls.should == [
|
649
675
|
'BEGIN',
|
650
|
-
"INSERT INTO items (
|
676
|
+
"INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2)",
|
651
677
|
'COMMIT'
|
652
678
|
]
|
653
679
|
|
@@ -681,3 +707,30 @@ context "MySQL::Dataset#replace" do
|
|
681
707
|
@d.all.should == [{:id => 111, :value => 333}]
|
682
708
|
end
|
683
709
|
end
|
710
|
+
|
711
|
+
context "MySQL::Dataset#complex_expression_sql" do
|
712
|
+
setup do
|
713
|
+
@d = MYSQL_DB.dataset
|
714
|
+
end
|
715
|
+
|
716
|
+
specify "should handle case insensitive regular expressions with REGEXP" do
|
717
|
+
@d.literal(:x.like(/a/i)).should == "(x REGEXP 'a')"
|
718
|
+
@d.literal(~:x.like(/a/i)).should == "NOT (x REGEXP 'a')"
|
719
|
+
end
|
720
|
+
|
721
|
+
specify "should handle case sensitive regular expressions with REGEXP BINARY" do
|
722
|
+
@d.literal(:x.like(/a/)).should == "(x REGEXP BINARY 'a')"
|
723
|
+
@d.literal(~:x.like(/a/)).should == "NOT (x REGEXP BINARY 'a')"
|
724
|
+
end
|
725
|
+
|
726
|
+
specify "should handle string concatenation with CONCAT if more than one record" do
|
727
|
+
@d.literal([:x, :y].sql_string_join).should == "CONCAT(x, y)"
|
728
|
+
@d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(x, ' ', y)"
|
729
|
+
@d.literal([:x[:y], 1, 'z'.lit].sql_string_join(:y|1)).should == "CONCAT(x(y), y[1], '1', y[1], z)"
|
730
|
+
end
|
731
|
+
|
732
|
+
specify "should handle string concatenation as simple string if just one record" do
|
733
|
+
@d.literal([:x].sql_string_join).should == "x"
|
734
|
+
@d.literal([:x].sql_string_join(' ')).should == "x"
|
735
|
+
end
|
736
|
+
end
|