sequel_core 1.0.9.1 → 1.0.10
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 +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
|