sequel_core 1.0.9.1 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -1
- data/Rakefile +1 -1
- data/lib/sequel_core/adapters/mysql.rb +29 -0
- data/lib/sequel_core/dataset/convenience.rb +6 -5
- data/lib/sequel_core/dataset/sql.rb +6 -1
- data/lib/sequel_core/dataset.rb +10 -2
- data/spec/adapters/mysql_spec.rb +98 -1
- data/spec/dataset_spec.rb +51 -11
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
===
|
1
|
+
=== 1.0.10 (2008-02-13)
|
2
|
+
|
3
|
+
* Fixed Datset#group_and_count to work inside a query block (#152).
|
4
|
+
|
5
|
+
* Changed datasets with transforms to automatically transform hash filters (#155).
|
6
|
+
|
7
|
+
* Changed Marshal stock transform to use Base64 encoding with backward-compatibility to support existing marshaled values (#154).
|
8
|
+
|
9
|
+
* Added support for inserting multiple records in a single statement using #multi_insert in MySQL adapter (#153).
|
10
|
+
|
11
|
+
* Added support for :slice option (same as :commit_every) in Dataset#multi_insert.
|
2
12
|
|
3
13
|
* Changed Dataset#all to accept opts and iteration block.
|
4
14
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ include FileUtils
|
|
9
9
|
# Configuration
|
10
10
|
##############################################################################
|
11
11
|
NAME = "sequel_core"
|
12
|
-
VERS = "1.0.
|
12
|
+
VERS = "1.0.10"
|
13
13
|
CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
|
14
14
|
RDOC_OPTS = [
|
15
15
|
"--quiet",
|
@@ -357,6 +357,35 @@ module Sequel
|
|
357
357
|
end
|
358
358
|
self
|
359
359
|
end
|
360
|
+
|
361
|
+
def multi_insert_sql(keys, array)
|
362
|
+
values = array.map {|r| "(#{literal(keys.map {|k| r[k]})})"}.join(COMMA_SEPARATOR)
|
363
|
+
columns = keys.map {|c| literal(c)}.join(COMMA_SEPARATOR)
|
364
|
+
"INSERT INTO #{@opts[:from]} (#{columns}) VALUES #{values}"
|
365
|
+
end
|
366
|
+
|
367
|
+
# Inserts multiple records into the associated table. This method can be
|
368
|
+
# to efficiently insert a large amounts of records into a table. Inserts
|
369
|
+
# are automatically wrapped in a transaction. If the :commit_every
|
370
|
+
# option is specified, the method will generate a separate transaction
|
371
|
+
# for each batch of records, e.g.:
|
372
|
+
#
|
373
|
+
# dataset.multi_insert(list, :commit_every => 1000)
|
374
|
+
def multi_insert(list, opts = {})
|
375
|
+
keys = list.first.keys
|
376
|
+
|
377
|
+
if every = (opts[:commit_every] || opts[:slice])
|
378
|
+
list.each_slice(every) do |s|
|
379
|
+
@db.transaction do
|
380
|
+
@db.execute(multi_insert_sql(keys, s))
|
381
|
+
end
|
382
|
+
end
|
383
|
+
else
|
384
|
+
@db.transaction do
|
385
|
+
@db.execute(multi_insert_sql(keys, list))
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
360
389
|
end
|
361
390
|
end
|
362
391
|
end
|
@@ -221,12 +221,12 @@ module Sequel
|
|
221
221
|
# Inserts multiple records into the associated table. This method can be
|
222
222
|
# to efficiently insert a large amounts of records into a table. Inserts
|
223
223
|
# are automatically wrapped in a transaction. If the :commit_every
|
224
|
-
# option is specified, the method will generate a separate
|
225
|
-
# for each batch of records, e.g.:
|
224
|
+
# or :slice option is specified, the method will generate a separate
|
225
|
+
# transaction for each batch of records, e.g.:
|
226
226
|
#
|
227
227
|
# dataset.multi_insert(list, :commit_every => 1000)
|
228
228
|
def multi_insert(list, opts = {})
|
229
|
-
if every = opts[:commit_every]
|
229
|
+
if every = (opts[:commit_every] || opts[:slice])
|
230
230
|
list.each_slice(every) do |s|
|
231
231
|
@db.transaction do
|
232
232
|
s.each {|r| @db.execute(insert_sql(r))}
|
@@ -246,9 +246,10 @@ module Sequel
|
|
246
246
|
def insert(*args); raise Error, "#insert cannot be invoked inside a query block."; end
|
247
247
|
def update(*args); raise Error, "#update cannot be invoked inside a query block."; end
|
248
248
|
def delete(*args); raise Error, "#delete cannot be invoked inside a query block."; end
|
249
|
-
|
250
|
-
def clone(opts)
|
249
|
+
|
250
|
+
def clone(opts = nil)
|
251
251
|
@opts.merge!(opts)
|
252
|
+
self
|
252
253
|
end
|
253
254
|
end
|
254
255
|
|
@@ -265,8 +265,13 @@ module Sequel
|
|
265
265
|
if cond === true || cond === false
|
266
266
|
raise Error::InvalidFilter, "Invalid filter specified. Did you mean to supply a block?"
|
267
267
|
end
|
268
|
+
|
269
|
+
if cond.is_a?(Hash)
|
270
|
+
cond = transform_save(cond) if @transform
|
271
|
+
filter = cond
|
272
|
+
end
|
268
273
|
parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
|
269
|
-
|
274
|
+
|
270
275
|
if !@opts[clause].nil? and @opts[clause].any?
|
271
276
|
l = expression_list(@opts[clause])
|
272
277
|
r = expression_list(block || cond, parenthesize)
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'time'
|
2
2
|
require 'date'
|
3
3
|
require 'yaml'
|
4
|
+
require 'base64'
|
4
5
|
|
5
6
|
require File.join(File.dirname(__FILE__), 'dataset/sql')
|
6
7
|
require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
|
@@ -298,8 +299,15 @@ module Sequel
|
|
298
299
|
end
|
299
300
|
|
300
301
|
STOCK_TRANSFORMS = {
|
301
|
-
:marshal => [
|
302
|
-
|
302
|
+
:marshal => [
|
303
|
+
# for backwards-compatibility we support also non-base64-encoded values.
|
304
|
+
proc {|v| Marshal.load(Base64.decode64(v)) rescue Marshal.load(v)},
|
305
|
+
proc {|v| Base64.encode64(Marshal.dump(v))}
|
306
|
+
],
|
307
|
+
:yaml => [
|
308
|
+
proc {|v| YAML.load v if v},
|
309
|
+
proc {|v| v.to_yaml}
|
310
|
+
]
|
303
311
|
}
|
304
312
|
|
305
313
|
# Sets a value transform which is used to convert values loaded and saved
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -264,7 +264,7 @@ end
|
|
264
264
|
# end
|
265
265
|
# end
|
266
266
|
#
|
267
|
-
context "
|
267
|
+
context "Joined MySQL dataset" do
|
268
268
|
setup do
|
269
269
|
@ds = MYSQL_DB[:nodes].join(:attributes, :node_id => :id)
|
270
270
|
@ds2 = MYSQL_DB[:nodes]
|
@@ -440,4 +440,101 @@ context "A MySQL database" do
|
|
440
440
|
MYSQL_DB[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).sql.should ==
|
441
441
|
"SELECT * FROM posts WHERE (MATCH (`title`) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"
|
442
442
|
end
|
443
|
+
end
|
444
|
+
|
445
|
+
class Sequel::MySQL::Database
|
446
|
+
alias_method :orig_execute, :execute
|
447
|
+
attr_accessor :sqls
|
448
|
+
def execute(sql, &block)
|
449
|
+
@sqls ||= []; @sqls << sql
|
450
|
+
orig_execute(sql, &block)
|
451
|
+
end
|
452
|
+
|
453
|
+
def transaction
|
454
|
+
@pool.hold do |conn|
|
455
|
+
@transactions ||= []
|
456
|
+
if @transactions.include? Thread.current
|
457
|
+
return yield(conn)
|
458
|
+
end
|
459
|
+
@sqls ||= []; @sqls << SQL_BEGIN
|
460
|
+
conn.query(SQL_BEGIN)
|
461
|
+
begin
|
462
|
+
@transactions << Thread.current
|
463
|
+
result = yield(conn)
|
464
|
+
@sqls ||= []; @sqls << SQL_COMMIT
|
465
|
+
conn.query(SQL_COMMIT)
|
466
|
+
result
|
467
|
+
rescue => e
|
468
|
+
@sqls ||= []; @sqls << SQL_ROLLBACK
|
469
|
+
conn.query(SQL_ROLLBACK)
|
470
|
+
raise e unless Sequel::Error::Rollback === e
|
471
|
+
ensure
|
472
|
+
@transactions.delete(Thread.current)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context "MySQL::Dataset#multi_insert" do
|
479
|
+
setup do
|
480
|
+
@d = MYSQL_DB[:items]
|
481
|
+
@d.delete # remove all records
|
482
|
+
MYSQL_DB.sqls.clear
|
483
|
+
end
|
484
|
+
|
485
|
+
specify "should insert multiple records in a single statement" do
|
486
|
+
@d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
|
487
|
+
|
488
|
+
MYSQL_DB.sqls.should == [
|
489
|
+
'BEGIN',
|
490
|
+
"INSERT INTO items (`name`) VALUES ('abc'), ('def')",
|
491
|
+
'COMMIT'
|
492
|
+
]
|
493
|
+
|
494
|
+
@d.all.should == [
|
495
|
+
{:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
|
496
|
+
]
|
497
|
+
end
|
498
|
+
|
499
|
+
specify "should split the list of records into batches if :commit_every option is given" do
|
500
|
+
@d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
|
501
|
+
:commit_every => 2)
|
502
|
+
|
503
|
+
MYSQL_DB.sqls.should == [
|
504
|
+
'BEGIN',
|
505
|
+
"INSERT INTO items (`value`) VALUES (1), (2)",
|
506
|
+
'COMMIT',
|
507
|
+
'BEGIN',
|
508
|
+
"INSERT INTO items (`value`) VALUES (3), (4)",
|
509
|
+
'COMMIT'
|
510
|
+
]
|
511
|
+
|
512
|
+
@d.all.should == [
|
513
|
+
{:name => nil, :value => 1},
|
514
|
+
{:name => nil, :value => 2},
|
515
|
+
{:name => nil, :value => 3},
|
516
|
+
{:name => nil, :value => 4}
|
517
|
+
]
|
518
|
+
end
|
519
|
+
|
520
|
+
specify "should split the list of records into batches if :slice option is given" do
|
521
|
+
@d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
|
522
|
+
:slice => 2)
|
523
|
+
|
524
|
+
MYSQL_DB.sqls.should == [
|
525
|
+
'BEGIN',
|
526
|
+
"INSERT INTO items (`value`) VALUES (1), (2)",
|
527
|
+
'COMMIT',
|
528
|
+
'BEGIN',
|
529
|
+
"INSERT INTO items (`value`) VALUES (3), (4)",
|
530
|
+
'COMMIT'
|
531
|
+
]
|
532
|
+
|
533
|
+
@d.all.should == [
|
534
|
+
{:name => nil, :value => 1},
|
535
|
+
{:name => nil, :value => 2},
|
536
|
+
{:name => nil, :value => 3},
|
537
|
+
{:name => nil, :value => 4}
|
538
|
+
]
|
539
|
+
end
|
443
540
|
end
|
data/spec/dataset_spec.rb
CHANGED
@@ -1032,11 +1032,18 @@ context "Dataset#group_and_count" do
|
|
1032
1032
|
end
|
1033
1033
|
|
1034
1034
|
specify "should format SQL properly" do
|
1035
|
-
@ds.group_and_count(:name).sql.should ==
|
1035
|
+
@ds.group_and_count(:name).sql.should ==
|
1036
|
+
"SELECT name, count(*) AS count FROM test GROUP BY name ORDER BY count"
|
1036
1037
|
end
|
1037
1038
|
|
1038
1039
|
specify "should accept multiple columns for grouping" do
|
1039
|
-
@ds.group_and_count(:a, :b).sql.should ==
|
1040
|
+
@ds.group_and_count(:a, :b).sql.should ==
|
1041
|
+
"SELECT a, b, count(*) AS count FROM test GROUP BY a, b ORDER BY count"
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
specify "should work within query block" do
|
1045
|
+
@ds.query{group_and_count(:a, :b)}.sql.should ==
|
1046
|
+
"SELECT a, b, count(*) AS count FROM test GROUP BY a, b ORDER BY count"
|
1040
1047
|
end
|
1041
1048
|
end
|
1042
1049
|
|
@@ -2053,7 +2060,7 @@ context "Dataset#multi_insert" do
|
|
2053
2060
|
]
|
2054
2061
|
end
|
2055
2062
|
|
2056
|
-
specify "should accept the commit_every option for committing every x records" do
|
2063
|
+
specify "should accept the :commit_every option for committing every x records" do
|
2057
2064
|
@ds.multi_insert(@list, :commit_every => 2)
|
2058
2065
|
@db.sqls.should == [
|
2059
2066
|
'BEGIN',
|
@@ -2065,6 +2072,19 @@ context "Dataset#multi_insert" do
|
|
2065
2072
|
'COMMIT'
|
2066
2073
|
]
|
2067
2074
|
end
|
2075
|
+
|
2076
|
+
specify "should accept the :slice option for committing every x records" do
|
2077
|
+
@ds.multi_insert(@list, :slice => 2)
|
2078
|
+
@db.sqls.should == [
|
2079
|
+
'BEGIN',
|
2080
|
+
"INSERT INTO items (name) VALUES ('abc')",
|
2081
|
+
"INSERT INTO items (name) VALUES ('def')",
|
2082
|
+
'COMMIT',
|
2083
|
+
'BEGIN',
|
2084
|
+
"INSERT INTO items (name) VALUES ('ghi')",
|
2085
|
+
'COMMIT'
|
2086
|
+
]
|
2087
|
+
end
|
2068
2088
|
end
|
2069
2089
|
|
2070
2090
|
context "Dataset#query" do
|
@@ -2310,39 +2330,59 @@ context "Dataset#transform" do
|
|
2310
2330
|
f.should == {:x => "wow", :y => 'hello'}
|
2311
2331
|
end
|
2312
2332
|
|
2313
|
-
specify "should support stock Marshal transformation" do
|
2333
|
+
specify "should support stock Marshal transformation with Base64 encoding" do
|
2314
2334
|
@ds.transform(:x => :marshal)
|
2315
2335
|
|
2316
|
-
@ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2336
|
+
@ds.raw = {:x => Base64.encode64(Marshal.dump([1, 2, 3])), :y => 'hello'}
|
2317
2337
|
@ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2318
2338
|
|
2319
2339
|
@ds.insert(:x => :toast)
|
2320
|
-
@ds.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2340
|
+
@ds.sql.should == "INSERT INTO items (x) VALUES ('#{Base64.encode64(Marshal.dump(:toast))}')"
|
2321
2341
|
@ds.insert(:y => 'butter')
|
2322
2342
|
@ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
|
2323
2343
|
@ds.update(:x => ['dream'])
|
2324
|
-
@ds.sql.should == "UPDATE items SET x = '#{Marshal.dump(['dream'])}'"
|
2344
|
+
@ds.sql.should == "UPDATE items SET x = '#{Base64.encode64(Marshal.dump(['dream']))}'"
|
2325
2345
|
|
2326
2346
|
@ds2 = @ds.filter(:a => 1)
|
2327
|
-
@ds2.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2347
|
+
@ds2.raw = {:x => Base64.encode64(Marshal.dump([1, 2, 3])), :y => 'hello'}
|
2328
2348
|
@ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2329
2349
|
@ds2.insert(:x => :toast)
|
2330
|
-
@ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2350
|
+
@ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Base64.encode64(Marshal.dump(:toast))}')"
|
2331
2351
|
|
2332
2352
|
@ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
|
2333
|
-
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2353
|
+
@ds.raw = {:x => Base64.encode64(Marshal.dump("wow")), :y => 'hello'}
|
2334
2354
|
@ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
|
2335
2355
|
f = nil
|
2336
|
-
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2356
|
+
@ds.raw = {:x => Base64.encode64(Marshal.dump("wow")), :y => 'hello'}
|
2337
2357
|
@ds.each(:naked => true) {|r| f = r}
|
2338
2358
|
f.should == {:x => "wow", :y => 'hello'}
|
2339
2359
|
end
|
2340
2360
|
|
2361
|
+
specify "should support loading of Marshalled values without Base64 encoding" do
|
2362
|
+
@ds.transform(:x => :marshal)
|
2363
|
+
|
2364
|
+
@ds.raw = {:x => Marshal.dump([1,2,3]), :y => nil}
|
2365
|
+
@ds.first.should == {:x => [1,2,3], :y => nil}
|
2366
|
+
end
|
2367
|
+
|
2341
2368
|
specify "should return self" do
|
2342
2369
|
@ds.transform(:x => :marshal).should be(@ds)
|
2343
2370
|
end
|
2344
2371
|
end
|
2345
2372
|
|
2373
|
+
context "A dataset with a transform" do
|
2374
|
+
setup do
|
2375
|
+
@ds = Sequel::Dataset.new(nil).from(:items)
|
2376
|
+
@ds.transform(:x => :marshal)
|
2377
|
+
end
|
2378
|
+
|
2379
|
+
specify "should automatically transform hash filters" do
|
2380
|
+
@ds.filter(:y => 2).sql.should == 'SELECT * FROM items WHERE (y = 2)'
|
2381
|
+
|
2382
|
+
@ds.filter(:x => 2).sql.should == "SELECT * FROM items WHERE (x = '#{Base64.encode64(Marshal.dump(2))}')"
|
2383
|
+
end
|
2384
|
+
end
|
2385
|
+
|
2346
2386
|
context "Dataset#to_csv" do
|
2347
2387
|
setup do
|
2348
2388
|
@c = Class.new(Sequel::Dataset) do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-02-
|
12
|
+
date: 2008-02-14 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|