sequel 5.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/bin/sequel +5 -6
  4. data/doc/release_notes/5.3.0.txt +121 -0
  5. data/doc/schema_modification.rdoc +15 -4
  6. data/doc/testing.rdoc +1 -0
  7. data/lib/sequel/adapters/jdbc.rb +4 -0
  8. data/lib/sequel/adapters/jdbc/postgresql.rb +15 -0
  9. data/lib/sequel/adapters/oracle.rb +2 -1
  10. data/lib/sequel/adapters/postgres.rb +4 -0
  11. data/lib/sequel/adapters/shared/mysql.rb +38 -3
  12. data/lib/sequel/adapters/shared/postgres.rb +15 -6
  13. data/lib/sequel/adapters/shared/sqlite.rb +10 -0
  14. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
  15. data/lib/sequel/connection_pool.rb +12 -0
  16. data/lib/sequel/database/misc.rb +13 -0
  17. data/lib/sequel/dataset/dataset_module.rb +1 -1
  18. data/lib/sequel/dataset/features.rb +5 -0
  19. data/lib/sequel/dataset/query.rb +20 -6
  20. data/lib/sequel/dataset/sql.rb +3 -0
  21. data/lib/sequel/extensions/pg_extended_date_support.rb +15 -0
  22. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  23. data/lib/sequel/model/associations.rb +1 -0
  24. data/lib/sequel/model/base.rb +4 -11
  25. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  26. data/lib/sequel/version.rb +1 -1
  27. data/spec/adapters/postgres_spec.rb +5 -34
  28. data/spec/core/database_spec.rb +32 -0
  29. data/spec/core/dataset_spec.rb +19 -0
  30. data/spec/core/mock_adapter_spec.rb +65 -0
  31. data/spec/extensions/association_pks_spec.rb +26 -33
  32. data/spec/extensions/class_table_inheritance_spec.rb +18 -32
  33. data/spec/extensions/composition_spec.rb +7 -23
  34. data/spec/extensions/list_spec.rb +4 -5
  35. data/spec/extensions/many_through_many_spec.rb +24 -32
  36. data/spec/extensions/optimistic_locking_spec.rb +1 -1
  37. data/spec/extensions/pg_array_associations_spec.rb +18 -25
  38. data/spec/extensions/pg_extended_date_support_spec.rb +13 -0
  39. data/spec/extensions/pg_hstore_spec.rb +2 -2
  40. data/spec/extensions/prepared_statements_safe_spec.rb +6 -6
  41. data/spec/extensions/pretty_table_spec.rb +39 -8
  42. data/spec/extensions/rcte_tree_spec.rb +22 -33
  43. data/spec/extensions/schema_dumper_spec.rb +42 -31
  44. data/spec/extensions/serialization_spec.rb +3 -3
  45. data/spec/extensions/synchronize_sql_spec.rb +124 -0
  46. data/spec/extensions/timestamps_spec.rb +2 -4
  47. data/spec/extensions/update_or_create_spec.rb +11 -15
  48. data/spec/extensions/uuid_spec.rb +2 -3
  49. data/spec/extensions/xml_serializer_spec.rb +5 -10
  50. data/spec/integration/database_test.rb +1 -1
  51. data/spec/integration/dataset_test.rb +7 -0
  52. data/spec/integration/plugin_test.rb +1 -1
  53. data/spec/integration/schema_test.rb +3 -3
  54. data/spec/integration/spec_helper.rb +4 -0
  55. data/spec/model/base_spec.rb +6 -0
  56. data/spec/model/eager_loading_spec.rb +31 -6
  57. data/spec/model/model_spec.rb +9 -19
  58. data/spec/model/record_spec.rb +4 -8
  59. metadata +6 -2
@@ -102,6 +102,18 @@ describe "A new Database" do
102
102
  end
103
103
  end
104
104
 
105
+ describe "Database :connect_sqls option" do
106
+ it "should issue the each sql query for each new connection" do
107
+ db = Sequel.mock(:connect_sqls=>['SELECT 1', 'SELECT 2'])
108
+ db.sqls.must_equal ['SELECT 1', 'SELECT 2']
109
+ db['SELECT 3'].get
110
+ db.sqls.must_equal ['SELECT 3']
111
+ db.disconnect
112
+ db['SELECT 3'].get
113
+ db.sqls.must_equal ['SELECT 1', 'SELECT 2', 'SELECT 3']
114
+ end
115
+ end
116
+
105
117
  describe "Database#freeze" do
106
118
  before do
107
119
  @db = Sequel.mock.freeze
@@ -510,6 +522,12 @@ describe "Database#extend_datasets custom methods" do
510
522
  ds.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY baz, bar'
511
523
  end
512
524
 
525
+ it "should have dataset_module support a reverse method" do
526
+ @db.extend_datasets{reverse(:foo){:baz}}
527
+ ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz DESC'
528
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar ORDER BY baz DESC'
529
+ end
530
+
513
531
  it "should have dataset_module support a select method" do
514
532
  @db.extend_datasets{select :foo, :baz}
515
533
  ds.foo.sql.must_equal 'SELECT baz FROM items'
@@ -2547,6 +2565,20 @@ describe "Database extensions" do
2547
2565
  Sequel.mock.a.must_equal 1
2548
2566
  Sequel.mock.b.must_equal 2
2549
2567
  end
2568
+
2569
+ it "should be loadable via the :extensions Database option" do
2570
+ Sequel::Database.register_extension(:a, Module.new{def a; 1; end})
2571
+ Sequel::Database.register_extension(:b, Module.new{def b; 2; end})
2572
+ Sequel.mock(:extensions=>:a).a.must_equal 1
2573
+ db = Sequel.mock(:extensions=>'a,b')
2574
+ db.a.must_equal 1
2575
+ db.b.must_equal 2
2576
+ db = Sequel.mock(:extensions=>[:a, :b])
2577
+ db.a.must_equal 1
2578
+ db.b.must_equal 2
2579
+ proc{Sequel.mock(:extensions=>nil).a}.must_raise NoMethodError
2580
+ proc{Sequel.mock(:extensions=>Object.new)}.must_raise Sequel::Error
2581
+ end
2550
2582
  end
2551
2583
 
2552
2584
  describe "Database specific exception classes" do
@@ -378,6 +378,15 @@ describe "Dataset#where" do
378
378
  @dataset.where(nil).sql.must_equal "SELECT * FROM test WHERE NULL"
379
379
  end
380
380
 
381
+ deprecated "should handle nil block result has no existing filter" do
382
+ @dataset.where{nil}.sql.must_equal "SELECT * FROM test"
383
+ end
384
+
385
+ # SEQUEL54
386
+ it "should handle nil block result has no existing filter" do
387
+ @dataset.where{nil}.sql.must_equal "SELECT * FROM test WHERE NULL"
388
+ end if false
389
+
381
390
  it "should just clone if given an empty array or hash argument" do
382
391
  @dataset.where({}).sql.must_equal @dataset.sql
383
392
  @dataset.where([]).sql.must_equal @dataset.sql
@@ -3568,6 +3577,16 @@ describe "Dataset#insert_sql" do
3568
3577
  it "should use unaliased table name" do
3569
3578
  @ds.from(Sequel.as(:items, :i)).insert_sql(1).must_equal "INSERT INTO items VALUES (1)"
3570
3579
  end
3580
+
3581
+ it "should hoist WITH clauses from query if the dataset doesn't support CTEs in subselects" do
3582
+ @ds = @ds.with_extend do
3583
+ Sequel::Dataset.def_sql_method(self, :insert, %w'with insert into columns values')
3584
+ def supports_cte?(type=:select); true end
3585
+ def supports_cte_in_subselect?; false end
3586
+ end
3587
+ @ds.insert_sql(@ds.from(:foo).with(:foo, @ds.select(:bar))).must_equal 'WITH foo AS (SELECT bar FROM items) INSERT INTO items SELECT * FROM foo'
3588
+ @ds.insert_sql([:a], @ds.from(:foo).with(:foo, @ds.select(:bar))).must_equal 'WITH foo AS (SELECT bar FROM items) INSERT INTO items (a) SELECT * FROM foo'
3589
+ end
3571
3590
  end
3572
3591
 
3573
3592
  describe "Dataset#inspect" do
@@ -577,6 +577,71 @@ describe "PostgreSQL support" do
577
577
  inf = -1.0/0.0
578
578
  @db[:test5].insert_sql(:value => inf).must_equal %q{INSERT INTO "test5" ("value") VALUES ('-Infinity')}
579
579
  end
580
+
581
+ it "Dataset#insert_conflict should respect expressions in the target argument" do
582
+ @ds = @db[:ic_test]
583
+ @ds.insert_conflict(:target=>:a).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"a\") DO NOTHING"
584
+ @ds.insert_conflict(:target=>:c, :conflict_where=>{:c_is_unique=>true}).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"c\") WHERE (\"c_is_unique\" IS TRUE) DO NOTHING"
585
+ @ds.insert_conflict(:target=>[:b, :c]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", \"c\") DO NOTHING"
586
+ @ds.insert_conflict(:target=>[:b, Sequel.function(:round, :c)]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
587
+ @ds.insert_conflict(:target=>[:b, Sequel.virtual_row{|o| o.round(:c)}]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
588
+ end
589
+
590
+ it "should support creating and dropping foreign tables" do
591
+ @db.create_table(:t, :foreign=>:f, :options=>{:o=>1}){Integer :a}
592
+ @db.drop_table(:t, :foreign=>true)
593
+ @db.sqls.must_equal ['CREATE FOREIGN TABLE "t" ("a" integer) SERVER "f" OPTIONS (o \'1\')',
594
+ 'DROP FOREIGN TABLE "t"']
595
+ end
596
+
597
+ it "#create_function and #drop_function should create and drop functions" do
598
+ @db.create_function('tf', 'SELECT 1', :returns=>:integer)
599
+ @db.drop_function('tf')
600
+ @db.sqls.map{|s| s.gsub(/\s+/, ' ').strip}.must_equal ["CREATE FUNCTION tf() RETURNS integer LANGUAGE SQL AS 'SELECT 1'",
601
+ 'DROP FUNCTION tf()']
602
+ end
603
+
604
+ it "#create_function and #drop_function should support options" do
605
+ @db.create_function('tf', 'SELECT $1 + $2', :args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'})
606
+ @db.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>[[:integer, :a], :integer])
607
+ @db.sqls.map{|s| s.gsub(/\s+/, ' ').strip}.must_equal ["CREATE OR REPLACE FUNCTION tf(a integer, integer) RETURNS integer LANGUAGE SQL IMMUTABLE STRICT SECURITY DEFINER COST 2 SET search_path = public AS 'SELECT $1 + $2'",
608
+ 'DROP FUNCTION IF EXISTS tf(a integer, integer) CASCADE']
609
+ end
610
+
611
+ it "#create_language and #drop_language should create and drop languages" do
612
+ @db.create_language(:plpgsql)
613
+ @db.create_language(:plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b)
614
+ @db.drop_language(:plpgsql)
615
+ @db.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
616
+ @db.sqls.map{|s| s.gsub(/\s+/, ' ').strip}.must_equal ['CREATE LANGUAGE plpgsql',
617
+ 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b',
618
+ 'DROP LANGUAGE plpgsql',
619
+ 'DROP LANGUAGE IF EXISTS plpgsql CASCADE']
620
+ end
621
+
622
+ it "#create_schema and #drop_schema should create and drop schemas" do
623
+ @db.create_schema(:sequel)
624
+ @db.create_schema(:sequel, :if_not_exists=>true, :owner=>:foo)
625
+ @db.drop_schema(:sequel)
626
+ @db.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
627
+ @db.sqls.must_equal ['CREATE SCHEMA "sequel"',
628
+ 'CREATE SCHEMA IF NOT EXISTS "sequel" AUTHORIZATION "foo"',
629
+ 'DROP SCHEMA "sequel"',
630
+ 'DROP SCHEMA IF EXISTS "sequel" CASCADE']
631
+ end
632
+
633
+ it "#create_trigger and #drop_trigger should create and drop triggers" do
634
+ @db.create_trigger(:test, :identity, :tf, :each_row=>true)
635
+ @db.create_trigger(:test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a'])
636
+ @db.create_trigger(:test, :identity, :tf, :each_row=>true, :when=> {Sequel[:new][:name] => 'b'})
637
+ @db.drop_trigger(:test, :identity)
638
+ @db.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
639
+ @db.sqls.map{|s| s.gsub(/\s+/, ' ').strip}.must_equal ['CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW EXECUTE PROCEDURE tf()',
640
+ 'CREATE TRIGGER identity AFTER INSERT ON "test" EXECUTE PROCEDURE tf(1, \'a\')',
641
+ %q{CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW WHEN ("new"."name" = 'b') EXECUTE PROCEDURE tf()},
642
+ 'DROP TRIGGER identity ON "test"',
643
+ 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE']
644
+ end
580
645
  end
581
646
 
582
647
  describe "MySQL support" do
@@ -105,13 +105,11 @@ describe "Sequel::Plugins::AssociationPks" do
105
105
 
106
106
  it "should set associated pks correctly for a many_to_many association" do
107
107
  @Album.load(:id=>2).tag_pks = [1, 3]
108
- sqls = @db.sqls
109
- sqls[0].must_equal "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
110
- sqls[1].must_equal 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
111
- sqls[2].must_equal 'BEGIN'
112
- sqls[3].must_match(/INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/)
113
- sqls[4].must_equal 'COMMIT'
114
- sqls.length.must_equal 5
108
+ @db.sqls.must_equal ["DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))",
109
+ 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)',
110
+ 'BEGIN',
111
+ 'INSERT INTO albums_tags (album_id, tag_id) VALUES (2, 1)',
112
+ 'COMMIT']
115
113
  end
116
114
 
117
115
  it "should return correct right-side associated cpks for one_to_many associations" do
@@ -161,10 +159,8 @@ describe "Sequel::Plugins::AssociationPks" do
161
159
  it "should set associated pks correctly for left-side cpks for a one_to_many association" do
162
160
  @Vocalist.one_to_many :instruments, :class=>@Instrument, :key=>[:first, :last], :delay_pks=>false
163
161
  @Vocalist.load(:first=>'F1', :last=>'L1').instrument_pks = [1, 2]
164
- sqls = @db.sqls
165
- sqls[0].must_match(/UPDATE instruments SET (first = 'F1', last = 'L1'|last = 'L1', first = 'F1') WHERE \(id IN \(1, 2\)\)/)
166
- sqls[1].must_match(/UPDATE instruments SET (first = NULL, last = NULL|last = NULL, first = NULL) WHERE \(\(instruments.first = 'F1'\) AND \(instruments.last = 'L1'\) AND \(id NOT IN \(1, 2\)\)\)/)
167
- sqls.length.must_equal 2
162
+ @db.sqls.must_equal ["UPDATE instruments SET first = 'F1', last = 'L1' WHERE (id IN (1, 2))",
163
+ "UPDATE instruments SET first = NULL, last = NULL WHERE ((instruments.first = 'F1') AND (instruments.last = 'L1') AND (id NOT IN (1, 2)))"]
168
164
  end
169
165
 
170
166
  it "should set associated pks correctly for left-side cpks for a many_to_many association" do
@@ -209,10 +205,8 @@ describe "Sequel::Plugins::AssociationPks" do
209
205
  it "should set associated right-side cpks correctly for left-side cpks for a one_to_many association" do
210
206
  @Vocalist.one_to_many :hits, :class=>@Hit, :key=>[:first, :last], :order=>:week, :delay_pks=>false
211
207
  @Vocalist.load(:first=>'F1', :last=>'L1').hit_pks = [[1997, 1], [1997, 2]]
212
- sqls = @db.sqls
213
- sqls[0].must_match(/UPDATE hits SET (first = 'F1', last = 'L1'|last = 'L1', first = 'F1') WHERE \(\(year, week\) IN \(\(1997, 1\), \(1997, 2\)\)\)/)
214
- sqls[1].must_match(/UPDATE hits SET (first = NULL, last = NULL|last = NULL, first = NULL) WHERE \(\(hits.first = 'F1'\) AND \(hits.last = 'L1'\) AND \(\(year, week\) NOT IN \(\(1997, 1\), \(1997, 2\)\)\)\)/)
215
- sqls.length.must_equal 2
208
+ @db.sqls.must_equal ["UPDATE hits SET first = 'F1', last = 'L1' WHERE ((year, week) IN ((1997, 1), (1997, 2)))",
209
+ "UPDATE hits SET first = NULL, last = NULL WHERE ((hits.first = 'F1') AND (hits.last = 'L1') AND ((year, week) NOT IN ((1997, 1), (1997, 2))))"]
216
210
  end
217
211
 
218
212
  it "should set associated right-side cpks correctly for left-side cpks for a many_to_many association" do
@@ -238,13 +232,11 @@ describe "Sequel::Plugins::AssociationPks" do
238
232
  album = @Album.load(:id=>2)
239
233
  album.use_transactions = true
240
234
  album.tag_pks = [1, 3]
241
- sqls = @db.sqls
242
- sqls[0].must_equal "BEGIN"
243
- sqls[1].must_equal "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
244
- sqls[2].must_equal 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
245
- sqls[3].must_match(/INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/)
246
- sqls[4].must_equal "COMMIT"
247
- sqls.length.must_equal 5
235
+ @db.sqls.must_equal ["BEGIN",
236
+ "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))",
237
+ 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)',
238
+ 'INSERT INTO albums_tags (album_id, tag_id) VALUES (2, 1)',
239
+ "COMMIT"]
248
240
  end
249
241
 
250
242
  it "should automatically convert keys to numbers if the primary key is an integer for one_to_many associations" do
@@ -264,22 +256,23 @@ describe "Sequel::Plugins::AssociationPks" do
264
256
  it "should automatically convert keys to numbers if the primary key is an integer for many_to_many associations" do
265
257
  @Tag.db_schema[:id][:type] = :integer
266
258
  @Album.load(:id=>2).tag_pks = %w'1 3'
267
- sqls = @db.sqls
268
- sqls[0].must_equal "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
269
- sqls[1].must_equal 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
270
- sqls[3].must_match(/INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/)
271
- sqls.length.must_equal 5
259
+ @db.sqls.must_equal ["DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))",
260
+ 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)',
261
+ 'BEGIN',
262
+ 'INSERT INTO albums_tags (album_id, tag_id) VALUES (2, 1)',
263
+ 'COMMIT']
272
264
  end
273
265
 
274
266
  it "should not automatically convert keys to numbers if the primary key is an integer for many_to_many associations" do
275
267
  @Tag.db_schema[:id][:type] = :string
276
268
  @Album.load(:id=>2).tag_pks = %w'1 3'
277
- sqls = @db.sqls
278
- sqls[0].must_equal "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN ('1', '3')))"
279
- sqls[1].must_equal 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
280
- sqls[3].must_match(/INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '1'|'1', 2)\)/)
281
- sqls[4].must_match(/INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '3'|'3', 2)\)/)
282
- sqls.length.must_equal 6
269
+ sqls = @db.sqls.must_equal [
270
+ "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN ('1', '3')))",
271
+ 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)',
272
+ 'BEGIN',
273
+ "INSERT INTO albums_tags (album_id, tag_id) VALUES (2, '1')",
274
+ "INSERT INTO albums_tags (album_id, tag_id) VALUES (2, '3')",
275
+ 'COMMIT']
283
276
  end
284
277
 
285
278
  it "should automatically convert keys to numbers for appropriate integer primary key for composite key associations" do
@@ -274,25 +274,19 @@ describe "class_table_inheritance plugin" do
274
274
 
275
275
  it "should insert the correct rows into all tables when inserting into parent class" do
276
276
  Employee.create(:name=>'E')
277
- sqls = @db.sqls
278
- sqls.length.must_equal 1
279
- sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Employee)', '(E|Employee)'\)/)
277
+ @db.sqls.must_equal ["INSERT INTO employees (name, kind) VALUES ('E', 'Employee')"]
280
278
  end
281
279
 
282
280
  it "should insert the correct rows into all tables when inserting into subclass without separate table" do
283
281
  Intern.create(:name=>'E')
284
- sqls = @db.sqls
285
- sqls.length.must_equal 1
286
- sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Intern)', '(E|Intern)'\)/)
282
+ @db.sqls.must_equal ["INSERT INTO employees (name, kind) VALUES ('E', 'Intern')"]
287
283
  end
288
284
 
289
285
  it "should insert the correct rows into all tables when inserting" do
290
286
  Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
291
- sqls = @db.sqls
292
- sqls.length.must_equal 3
293
- sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\)/)
294
- sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
295
- sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
287
+ @db.sqls.must_equal ["INSERT INTO employees (name, kind) VALUES ('E', 'Ceo')",
288
+ "INSERT INTO managers (id, num_staff) VALUES (1, 2)",
289
+ "INSERT INTO executives (id, num_managers) VALUES (1, 3)"]
296
290
  end
297
291
 
298
292
  it "should insert the correct rows into all tables when inserting when insert_select is supported" do
@@ -306,22 +300,18 @@ describe "class_table_inheritance plugin" do
306
300
  end)
307
301
  end
308
302
  Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
309
- sqls = @db.sqls
310
- sqls.length.must_equal 3
311
- sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\) RETURNING \*/)
312
- sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\) RETURNING \*/)
313
- sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\) RETURNING \*/)
303
+ @db.sqls.must_equal ["INSERT INTO employees (name, kind) VALUES ('E', 'Ceo') RETURNING *",
304
+ "INSERT INTO managers (id, num_staff) VALUES (1, 2) RETURNING *",
305
+ "INSERT INTO executives (id, num_managers) VALUES (1, 3) RETURNING *"]
314
306
  end
315
307
 
316
308
  it "should insert the correct rows into all tables with a given primary key" do
317
309
  e = Ceo.new(:num_managers=>3, :num_staff=>2, :name=>'E')
318
310
  e.id = 2
319
311
  e.save
320
- sqls = @db.sqls
321
- sqls.length.must_equal 3
322
- sqls[0].must_match(/INSERT INTO employees \((name|kind|id), (name|kind|id), (name|kind|id)\) VALUES \(('E'|'Ceo'|2), ('E'|'Ceo'|2), ('E'|'Ceo'|2)\)/)
323
- sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
324
- sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
312
+ @db.sqls.must_equal ["INSERT INTO employees (id, name, kind) VALUES (2, 'E', 'Ceo')",
313
+ "INSERT INTO managers (id, num_staff) VALUES (2, 2)",
314
+ "INSERT INTO executives (id, num_managers) VALUES (2, 3)"]
325
315
  end
326
316
 
327
317
  it "should update the correct rows in all tables when updating parent class" do
@@ -463,22 +453,18 @@ describe "class_table_inheritance plugin without sti_key with :alias option" do
463
453
 
464
454
  it "should insert the correct rows into all tables when inserting" do
465
455
  Executive.create(:num_managers=>3, :num_staff=>2, :name=>'E')
466
- sqls = @db.sqls
467
- sqls.length.must_equal 3
468
- sqls[0].must_match(/INSERT INTO employees \(name\) VALUES \('E'\)/)
469
- sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
470
- sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
471
- end
456
+ @db.sqls.must_equal ["INSERT INTO employees (name) VALUES ('E')",
457
+ "INSERT INTO managers (id, num_staff) VALUES (1, 2)",
458
+ "INSERT INTO executives (id, num_managers) VALUES (1, 3)"]
459
+ end
472
460
 
473
461
  it "should insert the correct rows into all tables with a given primary key" do
474
462
  e = Executive.new(:num_managers=>3, :num_staff=>2, :name=>'E')
475
463
  e.id = 2
476
464
  e.save
477
- sqls = @db.sqls
478
- sqls.length.must_equal 3
479
- sqls[0].must_match(/INSERT INTO employees \((name|id), (name|id)\) VALUES \(('E'|2), ('E'|2)\)/)
480
- sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
481
- sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
465
+ @db.sqls.must_equal ["INSERT INTO employees (id, name) VALUES (2, 'E')",
466
+ "INSERT INTO managers (id, num_staff) VALUES (2, 2)",
467
+ "INSERT INTO executives (id, num_managers) VALUES (2, 3)"]
482
468
  end
483
469
 
484
470
  it "should update the correct rows in all tables when updating" do
@@ -53,11 +53,8 @@ describe "Composition plugin" do
53
53
 
54
54
  it "should set column values even when not validating" do
55
55
  @c.composition :date, :mapping=>[:year, :month, :day]
56
- @c.load({}).set(:date=>Date.new(4, 8, 12)).save(:validate=>false)
57
- sql = DB.sqls.last
58
- sql.must_include("year = 4")
59
- sql.must_include("month = 8")
60
- sql.must_include("day = 12")
56
+ @c.load(id: 1).set(:date=>Date.new(4, 8, 12)).save(:validate=>false)
57
+ DB.sqls.must_equal ['UPDATE items SET year = 4, month = 8, day = 12 WHERE (id = 1)']
61
58
  end
62
59
 
63
60
  it ".compositions should return the reflection hash of compositions" do
@@ -81,10 +78,7 @@ describe "Composition plugin" do
81
78
  @c.composition :date, :composer=>proc{Date.new(year+1, month+2, day+3)}, :decomposer=>proc{[:year, :month, :day].each{|s| self.send("#{s}=", date.send(s) * 2)}}
82
79
  @o.date.must_equal Date.new(2, 4, 6)
83
80
  @o.save
84
- sql = DB.sqls.last
85
- sql.must_include("year = 4")
86
- sql.must_include("month = 8")
87
- sql.must_include("day = 12")
81
+ DB.sqls.must_equal ['UPDATE items SET year = 4, month = 8, day = 12 WHERE (id = 1)']
88
82
  end
89
83
 
90
84
  it "should allow call super in composition getter and setter method definition in class" do
@@ -187,9 +181,7 @@ describe "Composition plugin" do
187
181
  @o.date.month.must_equal 6
188
182
  @o.date = c.new(3, 4)
189
183
  @o.save
190
- sql = DB.sqls.last
191
- sql.must_include("year = 6")
192
- sql.must_include("month = 12")
184
+ DB.sqls.must_equal ['UPDATE items SET year = 6, month = 12, day = 3 WHERE (id = 1)']
193
185
  end
194
186
 
195
187
  it ":mapping option should work with an array of two pairs of symbols" do
@@ -209,9 +201,7 @@ describe "Composition plugin" do
209
201
  @o.date.m.must_equal 6
210
202
  @o.date = c.new(3, 4)
211
203
  @o.save
212
- sql = DB.sqls.last
213
- sql.must_include("year = 6")
214
- sql.must_include("month = 12")
204
+ DB.sqls.must_equal ['UPDATE items SET year = 6, month = 12, day = 3 WHERE (id = 1)']
215
205
  end
216
206
 
217
207
  it ":mapping option :composer should return nil if all values are nil" do
@@ -223,10 +213,7 @@ describe "Composition plugin" do
223
213
  @c.composition :date, :mapping=>[:year, :month, :day]
224
214
  @o.date = nil
225
215
  @o.save
226
- sql = DB.sqls.last
227
- sql.must_include("year = NULL")
228
- sql.must_include("month = NULL")
229
- sql.must_include("day = NULL")
216
+ DB.sqls.must_equal ['UPDATE items SET year = NULL, month = NULL, day = NULL WHERE (id = 1)']
230
217
  end
231
218
 
232
219
  it "should work with frozen instances" do
@@ -249,10 +236,7 @@ describe "Composition plugin" do
249
236
  o = c.load(:id=>1, :year=>1, :month=>2, :day=>3)
250
237
  o.date.must_equal Date.new(1, 2, 3)
251
238
  o.save
252
- sql = DB.sqls.last
253
- sql.must_include("year = 1")
254
- sql.must_include("month = 2")
255
- sql.must_include("day = 3")
239
+ DB.sqls.must_equal ['UPDATE items SET year = 1, month = 2, day = 3 WHERE (id = 1)']
256
240
  end
257
241
 
258
242
  it "should freeze composition metadata when freezing model class" do
@@ -92,15 +92,14 @@ describe "List plugin" do
92
92
  @sc.create(:scope_id=>1).values.must_equal(:id=>1, :scope_id=>1, :position=>1)
93
93
  @sc.create(:scope_id=>1).values.must_equal(:id=>2, :scope_id=>1, :position=>2)
94
94
  @sc.create(:scope_id=>2).values.must_equal(:id=>3, :scope_id=>2, :position=>1)
95
- sqls = @db.sqls
96
- sqls.slice!(7).must_match(/INSERT INTO items \((scope_id|position), (scope_id|position)\) VALUES \([12], [12]\)/)
97
- sqls.slice!(4).must_match(/INSERT INTO items \((scope_id|position), (scope_id|position)\) VALUES \([12], [12]\)/)
98
- sqls.slice!(1).must_match(/INSERT INTO items \((scope_id|position), (scope_id|position)\) VALUES \(1, 1\)/)
99
- sqls.must_equal ["SELECT max(position) AS max FROM items WHERE (scope_id = 1) LIMIT 1",
95
+ @db.sqls.must_equal ["SELECT max(position) AS max FROM items WHERE (scope_id = 1) LIMIT 1",
96
+ 'INSERT INTO items (scope_id, position) VALUES (1, 1)',
100
97
  "SELECT * FROM items WHERE (id = 1) ORDER BY scope_id, position LIMIT 1",
101
98
  "SELECT max(position) AS max FROM items WHERE (scope_id = 1) LIMIT 1",
99
+ 'INSERT INTO items (scope_id, position) VALUES (1, 2)',
102
100
  "SELECT * FROM items WHERE (id = 2) ORDER BY scope_id, position LIMIT 1",
103
101
  "SELECT max(position) AS max FROM items WHERE (scope_id = 2) LIMIT 1",
102
+ 'INSERT INTO items (scope_id, position) VALUES (2, 1)',
104
103
  "SELECT * FROM items WHERE (id = 3) ORDER BY scope_id, position LIMIT 1"]
105
104
  end
106
105
 
@@ -462,12 +462,12 @@ describe Sequel::Model, "many_through_many" do
462
462
 
463
463
  it "should not add associations methods directly to class" do
464
464
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
465
- im = @c1.instance_methods.collect{|x| x.to_s}
466
- im.must_include('tags')
467
- im.must_include('tags_dataset')
468
- im2 = @c1.instance_methods(false).collect{|x| x.to_s}
469
- im2.wont_include('tags')
470
- im2.wont_include('tags_dataset')
465
+ im = @c1.instance_methods
466
+ im.must_include(:tags)
467
+ im.must_include(:tags_dataset)
468
+ im2 = @c1.instance_methods(false)
469
+ im2.wont_include(:tags)
470
+ im2.wont_include(:tags_dataset)
471
471
  end
472
472
 
473
473
  it "should support after_load association callback" do
@@ -603,11 +603,9 @@ describe "many_through_many eager loading methods" do
603
603
  it "should eagerly load multiple associations in a single call" do
604
604
  a = @c1.eager(:tags, :albums).all
605
605
  a.must_equal [@c1.load(:id=>1)]
606
- sqls = DB.sqls
607
- sqls.length.must_equal 3
608
- sqls[0].must_equal 'SELECT * FROM artists'
609
- sqls[1..-1].must_include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
610
- sqls[1..-1].must_include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
606
+ DB.sqls.must_equal ['SELECT * FROM artists',
607
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))',
608
+ 'SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))']
611
609
  a = a.first
612
610
  a.tags.must_equal [Tag.load(:id=>2)]
613
611
  a.albums.must_equal [Album.load(:id=>3)]
@@ -617,11 +615,9 @@ describe "many_through_many eager loading methods" do
617
615
  it "should eagerly load multiple associations in separate" do
618
616
  a = @c1.eager(:tags).eager(:albums).all
619
617
  a.must_equal [@c1.load(:id=>1)]
620
- sqls = DB.sqls
621
- sqls.length.must_equal 3
622
- sqls[0].must_equal 'SELECT * FROM artists'
623
- sqls[1..-1].must_include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
624
- sqls[1..-1].must_include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
618
+ DB.sqls.must_equal ['SELECT * FROM artists',
619
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))',
620
+ 'SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))']
625
621
  a = a.first
626
622
  a.tags.must_equal [Tag.load(:id=>2)]
627
623
  a.albums.must_equal [Album.load(:id=>3)]
@@ -1585,12 +1581,12 @@ describe Sequel::Model, "one_through_many" do
1585
1581
 
1586
1582
  it "should not add associations methods directly to class" do
1587
1583
  @c1.one_through_many :tag, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
1588
- im = @c1.instance_methods.collect{|x| x.to_s}
1589
- im.must_include('tag')
1590
- im.must_include('tag_dataset')
1591
- im2 = @c1.instance_methods(false).collect{|x| x.to_s}
1592
- im2.wont_include('tag')
1593
- im2.wont_include('tag_dataset')
1584
+ im = @c1.instance_methods
1585
+ im.must_include(:tag)
1586
+ im.must_include(:tag_dataset)
1587
+ im2 = @c1.instance_methods(false)
1588
+ im2.wont_include(:tag)
1589
+ im2.wont_include(:tag_dataset)
1594
1590
  end
1595
1591
 
1596
1592
  it "should support after_load association callback" do
@@ -1681,11 +1677,9 @@ describe "one_through_many eager loading methods" do
1681
1677
  it "should eagerly load multiple associations in a single call" do
1682
1678
  a = @c1.eager(:tag, :album).all
1683
1679
  a.must_equal [@c1.load(:id=>1)]
1684
- sqls = DB.sqls
1685
- sqls.length.must_equal 3
1686
- sqls[0].must_equal 'SELECT * FROM artists'
1687
- sqls[1..-1].must_include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
1688
- sqls[1..-1].must_include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
1680
+ DB.sqls.must_equal ['SELECT * FROM artists',
1681
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))',
1682
+ 'SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))']
1689
1683
  a = a.first
1690
1684
  a.tag.must_equal Tag.load(:id=>2)
1691
1685
  a.album.must_equal Album.load(:id=>3)
@@ -1695,11 +1689,9 @@ describe "one_through_many eager loading methods" do
1695
1689
  it "should eagerly load multiple associations in separate" do
1696
1690
  a = @c1.eager(:tag).eager(:album).all
1697
1691
  a.must_equal [@c1.load(:id=>1)]
1698
- sqls = DB.sqls
1699
- sqls.length.must_equal 3
1700
- sqls[0].must_equal 'SELECT * FROM artists'
1701
- sqls[1..-1].must_include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
1702
- sqls[1..-1].must_include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))')
1692
+ DB.sqls.must_equal ['SELECT * FROM artists',
1693
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))',
1694
+ 'SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE (albums_artists.artist_id IN (1))']
1703
1695
  a = a.first
1704
1696
  a.tag.must_equal Tag.load(:id=>2)
1705
1697
  a.album.must_equal Album.load(:id=>3)