sequel 5.2.0 → 5.3.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.
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
@@ -15,15 +15,15 @@ describe "Serialization plugin" do
15
15
  it "should allow setting additional serializable attributes via plugin :serialization call" do
16
16
  @c.plugin :serialization, :yaml, :abc
17
17
  @c.create(:abc => 1, :def=> 2)
18
- DB.sqls.last.must_match(/INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n(\.\.\.\n)?', 2|2, '--- 1\n(\.\.\.\n)?')\)/)
18
+ DB.sqls.map{|s| s.sub("1\n...", '1')}.must_equal ["INSERT INTO items (def, abc) VALUES (2, '--- 1\n')"]
19
19
 
20
20
  @c.plugin :serialization, :marshal, :def
21
21
  @c.create(:abc => 1, :def=> 1)
22
- DB.sqls.last.must_match(/INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n(\.\.\.\n)?', 'BAhpBg==\n'|'BAhpBg==\n', '--- 1\n(\.\.\.\n)?')\)/)
22
+ DB.sqls.map{|s| s.sub("1\n...", '1')}.must_equal ["INSERT INTO items (abc, def) VALUES ('--- 1\n', 'BAhpBg==\n')"]
23
23
 
24
24
  @c.plugin :serialization, :json, :ghi
25
25
  @c.create(:ghi => [123])
26
- DB.sqls.last.must_match(/INSERT INTO items \((ghi)\) VALUES \('\[123\]'\)/)
26
+ DB.sqls.must_equal ["INSERT INTO items (ghi) VALUES ('[123]')"]
27
27
  end
28
28
 
29
29
  it "should handle validations of underlying column" do
@@ -0,0 +1,124 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "synchronize_sql extension" do
4
+ module Sync
5
+ def literal_string_append(sql, v)
6
+ db.synchronize{super}
7
+ end
8
+ end
9
+
10
+ before do
11
+ @db = Sequel.mock
12
+ @db.pool.extend(Module.new do
13
+ def assign_connection(*args)
14
+ r = super
15
+ @times_connection_acquired ||= 0
16
+ @times_connection_acquired += 1 if r
17
+ return r
18
+ end
19
+
20
+ def times_connection_acquired
21
+ v = @times_connection_acquired
22
+ @times_connection_acquired = 0
23
+ v || 0
24
+ end
25
+ end)
26
+ @db.extend_datasets(Sync)
27
+ @ds = @db[:tab1]
28
+ end
29
+
30
+ it 'does not checkout a connection if SQL is given as a string' do
31
+ extds = @ds.extension(:synchronize_sql).with_sql('SELECT 1').sql
32
+ @db.pool.times_connection_acquired.must_equal 0
33
+ end
34
+
35
+ it 'checks out an extra connection on insert_sql if there are no strings' do
36
+ @ds.insert_sql(:numeric_foo => 8)
37
+ @db.pool.times_connection_acquired.must_equal 0
38
+
39
+ extds = @ds.extension(:synchronize_sql)
40
+ extds.insert_sql(:numeric_foo => 8)
41
+ @db.pool.times_connection_acquired.must_equal 1
42
+ end
43
+
44
+ it 'checks out just one connection on insert_sql if there are multiple strings' do
45
+ @ds.insert_sql(:string_foo1 => 'eight', :string_foo2 => 'nine', :string_foo3 => 'ten')
46
+ @db.pool.times_connection_acquired.must_equal 3
47
+
48
+ extds = @ds.extension(:synchronize_sql)
49
+ extds.insert_sql(:string_foo1 => 'eight', :string_foo2 => 'nine', :string_foo3 => 'ten')
50
+ @db.pool.times_connection_acquired.must_equal 1
51
+ end
52
+
53
+ it 'cheks out an extra connectrion on update_sql if there are no strings' do
54
+ @ds.where(:numeric_foo => [1, 2, 3, 4, 5]).update_sql(:numeric_foo => 99)
55
+ @db.pool.times_connection_acquired.must_equal 0
56
+
57
+ extds = @ds.extension(:synchronize_sql)
58
+ extds.where(:numeric_foo => [1, 2, 3, 4, 5]).update_sql(:numeric_foo => 99)
59
+ @db.pool.times_connection_acquired.must_equal 1
60
+ end
61
+
62
+ it 'checks out just one connection on update_sql if there are multiple strings' do
63
+ @ds.where(:numeric_foo => [1, 2, 3, 4, 5]).update_sql(:string_foo1 => 'eight', :string_foo2 => 'nine', :string_foo3 => 'ten')
64
+ @db.pool.times_connection_acquired.must_equal 3
65
+
66
+ extds = @ds.extension(:synchronize_sql)
67
+ extds.where(:numeric_foo => [1, 2, 3, 4, 5]).update_sql(:string_foo1 => 'eight', :string_foo2 => 'nine', :string_foo3 => 'ten')
68
+ @db.pool.times_connection_acquired.must_equal 1
69
+ end
70
+
71
+ it 'checks out an extra connection on delete_sql if there are no strings' do
72
+ @ds.where(:numeric_foo => [1, 2, 3]).delete_sql
73
+ @db.pool.times_connection_acquired.must_equal 0
74
+
75
+ extds = @ds.extension(:synchronize_sql)
76
+ extds.where(:numeric_foo => [1, 2, 3]).delete_sql
77
+ @db.pool.times_connection_acquired.must_equal 1
78
+ end
79
+
80
+ it 'checks out just one connection on delete_sql if there are multiple strings' do
81
+ @ds.where(:string_foo => ['one', 'two', 'three', 'four']).delete_sql
82
+ @db.pool.times_connection_acquired.must_equal 4
83
+
84
+ extds = @ds.extension(:synchronize_sql)
85
+ extds.where(:string_foo => ['one', 'two', 'three', 'four']).delete_sql
86
+ @db.pool.times_connection_acquired.must_equal 1
87
+ end
88
+
89
+ it 'checks out an extra connection on select_sql if there are no strings' do
90
+ @ds.where(:numeric_foo => [1, 2, 3]).select_sql
91
+ @db.pool.times_connection_acquired.must_equal 0
92
+
93
+ extds = @ds.extension(:synchronize_sql)
94
+ extds.where(:numeric_foo => [1, 2, 3]).select_sql
95
+ @db.pool.times_connection_acquired.must_equal 1
96
+ end
97
+
98
+ it 'checks out just one connection on select_sql if there are multiple strings' do
99
+ @ds.where(:string_foo => ['one', 'two', 'three', 'four']).select_sql
100
+ @db.pool.times_connection_acquired.must_equal 4
101
+
102
+ extds = @ds.extension(:synchronize_sql)
103
+ extds.where(:string_foo => ['one', 'two', 'three', 'four']).select_sql
104
+ @db.pool.times_connection_acquired.must_equal 1
105
+ end
106
+
107
+ it 'checks out an extra connection on fetch if there are no strings' do
108
+ @db.fetch('SELECT * FROM tab1 WHERE numeric_foo IN (?, ?, ?, ?)', 1, 2, 3, 4).select_sql
109
+ @db.pool.times_connection_acquired.must_equal 0
110
+
111
+ @db.extension(:synchronize_sql)
112
+ @db.fetch('SELECT * FROM tab1 WHERE numeric_foo IN (?, ?, ?, ?)', 1, 2, 3, 4).select_sql
113
+ @db.pool.times_connection_acquired.must_equal 1
114
+ end
115
+
116
+ it 'checks out just one connection on fetch if there are multiple strings' do
117
+ @db.fetch('SELECT * FROM tab1 WHERE string_foo IN (?, ?, ?, ?)', 'one', 'two', 'three', 'four').select_sql
118
+ @db.pool.times_connection_acquired.must_equal 4
119
+
120
+ @db.extension(:synchronize_sql)
121
+ @db.fetch('SELECT * FROM tab1 WHERE string_foo IN (?, ?, ?, ?)', 'one', 'two', 'three', 'four').select_sql
122
+ @db.pool.times_connection_acquired.must_equal 1
123
+ end
124
+ end
@@ -76,9 +76,7 @@ describe "Sequel::Plugins::Timestamps" do
76
76
  it "should use the same value for the creation and update timestamps when creating if the :update_on_create option is given" do
77
77
  @c.plugin :timestamps, :update_on_create=>true
78
78
  o = @c.create
79
- sqls = @c.db.sqls
80
- sqls.shift.must_match(/INSERT INTO t \((creat|updat)ed_at, (creat|updat)ed_at\) VALUES \('2009-08-01', '2009-08-01'\)/)
81
- sqls.must_equal []
79
+ @c.db.sqls.must_equal ["INSERT INTO t (created_at, updated_at) VALUES ('2009-08-01', '2009-08-01')"]
82
80
  o.created_at.must_be :===, o.updated_at
83
81
  end
84
82
 
@@ -201,7 +199,7 @@ describe "Sequel::Plugins::Timestamps" do
201
199
  o = c2.create
202
200
  o.c.must_equal '2009-08-01'
203
201
  o.u.must_be :===, o.c
204
- c2.db.sqls.first.must_match(/INSERT INTO t \([cu], [cu]\) VALUES \('2009-08-01', '2009-08-01'\)/)
202
+ c2.db.sqls.must_equal ["INSERT INTO t (c, u) VALUES ('2009-08-01', '2009-08-01')"]
205
203
  c2.db.reset
206
204
  o = c2.load(:id=>1).save
207
205
  o.u.must_equal '2009-08-01'
@@ -20,32 +20,28 @@ describe "Sequel::Plugins::UpdateOrCreate" do
20
20
 
21
21
  @db.fetch = [[{:id=>1, :a=>2, :b=>3}]]
22
22
  @c.update_or_create({:a=>2}, :a=>3){|t| t.b = 4}.must_equal @c.load(:id=>1, :a=>3, :b=>4)
23
- sqls = @db.sqls
24
- sqls.shift.must_equal "SELECT * FROM test WHERE (a = 2) LIMIT 1"
25
- sqls.shift.must_match(/UPDATE test SET [ab] = [34], [ab] = [34] WHERE \(id = 1\)/)
23
+ @db.sqls.must_equal ["SELECT * FROM test WHERE (a = 2) LIMIT 1",
24
+ 'UPDATE test SET a = 3, b = 4 WHERE (id = 1)']
26
25
  end
27
26
 
28
27
  it ".update_or_create should create a record if an existing record does not exist" do
29
28
  @db.fetch = [[], [{:id=>1, :a=>1, :b=>4}]]
30
29
  @c.update_or_create(:a=>1){|t| t.b = 4}.must_equal @c.load(:id=>1, :a=>1, :b=>4)
31
- sqls = @db.sqls
32
- sqls.shift.must_equal "SELECT * FROM test WHERE (a = 1) LIMIT 1"
33
- sqls.shift.must_match(/INSERT INTO test \([ab], [ab]\) VALUES \([14], [14]\)/)
34
- sqls.shift.must_equal "SELECT * FROM test WHERE (id = 1) LIMIT 1"
30
+ @db.sqls.must_equal ["SELECT * FROM test WHERE (a = 1) LIMIT 1",
31
+ "INSERT INTO test (a, b) VALUES (1, 4)",
32
+ "SELECT * FROM test WHERE (id = 1) LIMIT 1"]
35
33
 
36
34
  @db.fetch = [[], [{:id=>1, :a=>1, :b=>4}]]
37
35
  @c.update_or_create({:a=>1}, :b=>4).must_equal @c.load(:id=>1, :a=>1, :b=>4)
38
- sqls = @db.sqls
39
- sqls.shift.must_equal "SELECT * FROM test WHERE (a = 1) LIMIT 1"
40
- sqls.shift.must_match(/INSERT INTO test \([ab], [ab]\) VALUES \([14], [14]\)/)
41
- sqls.shift.must_equal "SELECT * FROM test WHERE (id = 1) LIMIT 1"
36
+ @db.sqls.must_equal ["SELECT * FROM test WHERE (a = 1) LIMIT 1",
37
+ "INSERT INTO test (a, b) VALUES (1, 4)",
38
+ "SELECT * FROM test WHERE (id = 1) LIMIT 1"]
42
39
 
43
40
  @db.fetch = [[], [{:id=>1, :a=>3, :b=>4}]]
44
41
  @c.update_or_create({:a=>1}, :a=>3){|t| t.b = 4}.must_equal @c.load(:id=>1, :a=>3, :b=>4)
45
- sqls = @db.sqls
46
- sqls.shift.must_equal "SELECT * FROM test WHERE (a = 1) LIMIT 1"
47
- sqls.shift.must_match(/INSERT INTO test \([ab], [ab]\) VALUES \([34], [34]\)/)
48
- sqls.shift.must_equal "SELECT * FROM test WHERE (id = 1) LIMIT 1"
42
+ @db.sqls.must_equal ["SELECT * FROM test WHERE (a = 1) LIMIT 1",
43
+ "INSERT INTO test (a, b) VALUES (3, 4)",
44
+ "SELECT * FROM test WHERE (id = 1) LIMIT 1"]
49
45
  end
50
46
 
51
47
  it ".update_or_create should return an existing record even if no changes necessary" do
@@ -45,8 +45,7 @@ describe "Sequel::Plugins::Uuid" do
45
45
  def _save_refresh(*) end
46
46
  end
47
47
  o = c.create
48
- c.db.sqls.first.must_match(/INSERT INTO t \(u\) VALUES \('[-0-9a-f]+'\)/)
49
- o.u.must_match(/[-0-9a-f]+/)
48
+ c.db.sqls.must_equal ["INSERT INTO t (u) VALUES ('#{o.u}')"]
50
49
  end
51
50
 
52
51
  it "should not raise an error if the model doesn't have the uuid column" do
@@ -97,6 +96,6 @@ describe "Sequel::Plugins::Uuid" do
97
96
  c2.db.reset
98
97
  o = c2.create
99
98
  o.u.must_equal @uuid
100
- c2.db.sqls.first.must_match(/INSERT INTO t \([u]\) VALUES \('#{@uuid}'\)/)
99
+ c2.db.sqls.must_equal ["INSERT INTO t (u) VALUES ('#{@uuid}')"]
101
100
  end
102
101
  end
@@ -125,30 +125,25 @@ describe "Sequel::Plugins::XmlSerializer" do
125
125
  end
126
126
 
127
127
  it "should support an :encoding option when serializing" do
128
- ["<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><id>2</id><name>YJM</name></artist>",
129
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><name>YJM</name><id>2</id></artist>"].must_include(@artist.to_xml(:encoding=>'UTF-8').gsub(/\n */m, ''))
128
+ @artist.to_xml(:encoding=>'UTF-8').gsub(/\n */m, '').must_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><id>2</id><name>YJM</name></artist>"
130
129
  end
131
130
 
132
131
  it "should support a :builder_opts option when serializing" do
133
- ["<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><id>2</id><name>YJM</name></artist>",
134
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><name>YJM</name><id>2</id></artist>"].must_include(@artist.to_xml(:builder_opts=>{:encoding=>'UTF-8'}).gsub(/\n */m, ''))
132
+ @artist.to_xml(:builder_opts=>{:encoding=>'UTF-8'}).gsub(/\n */m, '').must_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?><artist><id>2</id><name>YJM</name></artist>"
135
133
  end
136
134
 
137
135
  it "should support an :types option when serializing" do
138
- ["<?xml version=\"1.0\"?><artist><id type=\"integer\">2</id><name type=\"string\">YJM</name></artist>",
139
- "<?xml version=\"1.0\"?><artist><name type=\"string\">YJM</name><id type=\"integer\">2</id></artist>"].must_include(@artist.to_xml(:types=>true).gsub(/\n */m, ''))
136
+ @artist.to_xml(:types=>true).gsub(/\n */m, '').must_equal "<?xml version=\"1.0\"?><artist><id type=\"integer\">2</id><name type=\"string\">YJM</name></artist>"
140
137
  end
141
138
 
142
139
  it "should support an :root_name option when serializing" do
143
- ["<?xml version=\"1.0\"?><ar><id>2</id><name>YJM</name></ar>",
144
- "<?xml version=\"1.0\"?><ar><name>YJM</name><id>2</id></ar>"].must_include(@artist.to_xml(:root_name=>'ar').gsub(/\n */m, ''))
140
+ @artist.to_xml(:root_name=>'ar').gsub(/\n */m, '').must_equal "<?xml version=\"1.0\"?><ar><id>2</id><name>YJM</name></ar>"
145
141
  end
146
142
 
147
143
  it "should support an :array_root_name option when serializing arrays" do
148
144
  artist = @artist
149
145
  Artist.dataset = Artist.dataset.with_extend{define_method(:all){[artist]}}
150
- ["<?xml version=\"1.0\"?><ars><ar><id>2</id><name>YJM</name></ar></ars>",
151
- "<?xml version=\"1.0\"?><ars><ar><name>YJM</name><id>2</id></ar></ars>"].must_include(Artist.to_xml(:array_root_name=>'ars', :root_name=>'ar').gsub(/\n */m, ''))
146
+ Artist.to_xml(:array_root_name=>'ars', :root_name=>'ar').gsub(/\n */m, '').must_equal "<?xml version=\"1.0\"?><ars><ar><id>2</id><name>YJM</name></ar></ars>"
152
147
  end
153
148
 
154
149
  it "should raise an exception for xml tags that aren't associations, columns, or setter methods" do
@@ -53,7 +53,7 @@ describe Sequel::Database do
53
53
  proc{@db[:test].update(:a=>'1', :b=>'2')}.must_raise(Sequel::UniqueConstraintViolation)
54
54
  end
55
55
 
56
- cspecify "should raise Sequel::CheckConstraintViolation when a check constraint is violated", :mysql, [proc{|db| db.sqlite_version < 30802}, :sqlite] do
56
+ cspecify "should raise Sequel::CheckConstraintViolation when a check constraint is violated", [proc{|db| !db.mariadb? || db.server_version <= 100200}, :mysql], [proc{|db| db.sqlite_version < 30802}, :sqlite] do
57
57
  @db.create_table!(:test){String :a; check Sequel.~(:a=>'1')}
58
58
  proc{@db[:test].insert('1')}.must_raise(Sequel::CheckConstraintViolation)
59
59
  @db[:test].insert('2')
@@ -771,6 +771,13 @@ if DB.dataset.supports_cte?(:update) # Assume INSERT and DELETE support as well
771
771
  @ds2.filter(:id=>@db[:t].select{max(id)}).delete
772
772
  @ds.select_order_map(:id).must_equal [1, 1]
773
773
  end
774
+
775
+ it "should support a subselect in an subquery used for INSERT" do
776
+ @db.transaction(:rollback=>:always) do
777
+ @ds.insert([:id], @db[:foo].with(:foo, @ds.select{(id + 10).as(:id)}))
778
+ @ds.select_order_map(:id).must_equal [1,2,11,12]
779
+ end
780
+ end
774
781
  end
775
782
  end
776
783
 
@@ -2043,7 +2043,7 @@ describe "Sequel::Plugins::ConstraintValidations" do
2043
2043
  end
2044
2044
 
2045
2045
  ConstraintValidationsSpecs = shared_description do
2046
- cspecify "should set up constraints that work even outside the model", :mysql do
2046
+ cspecify "should set up constraints that work even outside the model", [proc{|db| !db.mariadb? || db.server_version <= 100200}, :mysql] do
2047
2047
  @ds.insert(@valid_row)
2048
2048
 
2049
2049
  # Test for unique constraint
@@ -129,7 +129,7 @@ describe "Database schema parser" do
129
129
  DB.schema(:items).first.last[:ruby_default].must_equal Sequel::CURRENT_TIMESTAMP
130
130
  end
131
131
 
132
- cspecify "should parse current date defaults from the schema properly", :mysql, :oracle do
132
+ cspecify "should parse current date defaults from the schema properly", [proc{|db| !db.mariadb? || db.server_version <= 100200}, :mysql], :oracle do
133
133
  DB.create_table!(:items){Date :a, :default=>Sequel::CURRENT_DATE}
134
134
  DB.schema(:items).first.last[:ruby_default].must_equal Sequel::CURRENT_DATE
135
135
  end
@@ -765,14 +765,14 @@ describe "Database schema modifiers" do
765
765
  primary_key :id
766
766
  String :name2
767
767
  String :number2
768
- constraint :bar, Sequel.~(:id=>nil)
768
+ constraint :bar, Sequel.~(:number2=>nil, :name2=>nil)
769
769
  end
770
770
  @ds.insert(:name2=>'A12')
771
771
  @db.alter_table(:items) do
772
772
  add_column :number, Integer
773
+ drop_constraint :bar
773
774
  drop_column :number2
774
775
  rename_column :name2, :name
775
- drop_constraint :bar
776
776
  set_column_not_null :name
777
777
  set_column_default :name, 'A13'
778
778
  add_constraint :foo, Sequel.like(:name, 'A%')
@@ -42,6 +42,10 @@ if ENV['SEQUEL_ERROR_SQL']
42
42
  DB.extension :error_sql
43
43
  end
44
44
 
45
+ if ENV['SEQUEL_SYNCHRONIZE_SQL']
46
+ DB.extension :synchronize_sql
47
+ end
48
+
45
49
  if ENV['SEQUEL_CONNECTION_VALIDATOR']
46
50
  ENV['SEQUEL_NO_CHECK_SQLS'] = '1'
47
51
  DB.extension(:connection_validator)
@@ -316,6 +316,12 @@ describe Sequel::Model, ".dataset_module" do
316
316
  @c.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY baz, bar'
317
317
  end
318
318
 
319
+ it "should have dataset_module support a reverse method" do
320
+ @c.dataset_module{reverse(:foo){:baz}}
321
+ @c.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz DESC'
322
+ @c.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar ORDER BY baz DESC'
323
+ end
324
+
319
325
  it "should have dataset_module support a select method" do
320
326
  @c.dataset_module{select :foo, :baz}
321
327
  @c.foo.sql.must_equal 'SELECT baz FROM items'
@@ -222,6 +222,31 @@ describe Sequel::Model, "#eager" do
222
222
  DB.sqls.must_equal []
223
223
  end
224
224
 
225
+ it "should eagerly load a single one_to_one association using the :window_function strategy on MySQL" do
226
+ odb = DB
227
+ db = Class.new do
228
+ def database_type; :mysql; end
229
+ define_method(:method_missing) do |*args, &block|
230
+ odb.send(*args, &block)
231
+ end
232
+ end.new
233
+
234
+ begin
235
+ EagerTrack.dataset = EagerTrack.dataset.with_extend do
236
+ def supports_window_functions?; true end
237
+ define_method(:db){db}
238
+ end
239
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :order=>:name, :eager_limit_strategy=>:window_function
240
+ a = EagerAlbum.eager(:track).all
241
+ a.must_equal [EagerAlbum.load(:id => 1, :band_id => 2)]
242
+ DB.sqls.must_equal ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x = 1) ORDER BY x_sequel_row_number_x']
243
+ a.first.track.must_equal EagerTrack.load(:id => 3, :album_id=>1)
244
+ DB.sqls.must_equal []
245
+ ensure
246
+ db = DB
247
+ end
248
+ end
249
+
225
250
  it "should automatically use an eager limit stategy if the association has an offset" do
226
251
  EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :limit=>[1,1]
227
252
  EagerTrack.dataset = EagerTrack.dataset.with_fetch([{:id => 4, :album_id=>1}])
@@ -675,9 +700,9 @@ describe Sequel::Model, "#eager" do
675
700
  it "should cache the negative lookup when eagerly loading a *_to_many associations" do
676
701
  a = EagerBand.eager(:albums).where{id > 100}.all
677
702
  a.must_equal [EagerBand.load(:id => 101), EagerBand.load(:id =>102)]
678
- sqls = DB.sqls
679
- ['SELECT * FROM albums WHERE (albums.band_id IN (101, 102))', 'SELECT * FROM albums WHERE (albums.band_id IN (102, 101))'].must_include(sqls.delete_at(1))
680
- sqls.must_equal ['SELECT * FROM bands WHERE (id > 100)', "SELECT * FROM tracks WHERE (tracks.album_id IN (101))"]
703
+ DB.sqls.must_equal ['SELECT * FROM bands WHERE (id > 100)',
704
+ 'SELECT * FROM albums WHERE (albums.band_id IN (101, 102))',
705
+ "SELECT * FROM tracks WHERE (tracks.album_id IN (101))"]
681
706
  a.map{|b| b.associations[:albums]}.must_equal [[EagerAlbum.load({:band_id=>101, :id=>101})], []]
682
707
  DB.sqls.must_equal []
683
708
  end
@@ -1100,9 +1125,9 @@ describe Sequel::Model, "#eager" do
1100
1125
  EagerTrack.dataset = EagerTrack.dataset.with_fetch([{:id=>3, :album_id=>1}])
1101
1126
  a = EagerBand.eager(:top_10_albums=>{proc{|ds| ds.select(:id, :name)}=>:tracks}).all
1102
1127
  a.must_equal [EagerBand.load(:id => 2)]
1103
- sqls = DB.sqls
1104
- sqls.pop.must_match(/SELECT \* FROM tracks WHERE \(tracks.album_id IN \((\d+, ){10}\d+\)\)/)
1105
- sqls.must_equal ['SELECT * FROM bands', 'SELECT id, name FROM albums WHERE (albums.band_id IN (2))']
1128
+ DB.sqls.must_equal ['SELECT * FROM bands',
1129
+ 'SELECT id, name FROM albums WHERE (albums.band_id IN (2))',
1130
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))']
1106
1131
  a = a.first
1107
1132
  a.top_10_albums.must_equal((1..10).map{|i| EagerAlbum.load(:band_id=>2, :id=>i)})
1108
1133
  a.top_10_albums.map{|x| x.tracks}.must_equal [[EagerTrack.load(:id => 3, :album_id=>1)]] + ([[]] * 9)
@@ -231,10 +231,8 @@ end
231
231
 
232
232
  describe Sequel::Model do
233
233
  it "should have class method aliased as model" do
234
- Sequel::Model.instance_methods.collect{|x| x.to_s}.must_include("model")
235
-
236
234
  model_a = Class.new(Sequel::Model(:items))
237
- model_a.new.model.wont_be_nil
235
+ model_a.new.model.must_be_same_as model_a
238
236
  end
239
237
 
240
238
  it "should be associated with a dataset" do
@@ -626,10 +624,9 @@ describe Sequel::Model, ".find_or_create" do
626
624
  @db.fetch = [[], {:x=>1, :id=>1}]
627
625
  @db.autoid = 1
628
626
  @c.find_or_create(:x => 1){|x| x[:y] = 2}.must_equal @c.load(:x=>1, :id=>1)
629
- sqls = @db.sqls
630
- sqls.first.must_equal "SELECT * FROM items WHERE (x = 1) LIMIT 1"
631
- ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].must_include(sqls[1])
632
- sqls.last.must_equal "SELECT * FROM items WHERE id = 1"
627
+ @db.sqls.must_equal ["SELECT * FROM items WHERE (x = 1) LIMIT 1",
628
+ "INSERT INTO items (x, y) VALUES (1, 2)",
629
+ "SELECT * FROM items WHERE id = 1"]
633
630
  end
634
631
  end
635
632
 
@@ -687,17 +684,12 @@ describe Sequel::Model, "attribute accessors" do
687
684
  end
688
685
 
689
686
  it "should be created on set_dataset" do
690
- %w'x z x= z='.each do |x|
691
- @c.instance_methods.collect{|z| z.to_s}.wont_include(x)
692
- end
687
+ a = [:x, :z, :x= ,:z=]
688
+ (a - @c.instance_methods).must_equal a
693
689
  @c.set_dataset(@dataset)
694
- %w'x z x= z='.each do |x|
695
- @c.instance_methods.collect{|z| z.to_s}.must_include(x)
696
- end
690
+ (a - @c.instance_methods).must_equal []
697
691
  o = @c.new
698
- %w'x z x= z='.each do |x|
699
- o.methods.collect{|z| z.to_s}.must_include(x)
700
- end
692
+ (a - o.methods).must_equal []
701
693
 
702
694
  o.x.must_be_nil
703
695
  o.x = 34
@@ -769,9 +761,7 @@ describe Sequel::Model, ".[]" do
769
761
  it "should work correctly for composite primary key specified as array" do
770
762
  @c.set_primary_key [:node_id, :kind]
771
763
  @c[3921, 201].must_be_kind_of(@c)
772
- sqls = DB.sqls
773
- sqls.length.must_equal 1
774
- sqls.first.must_match(/^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/)
764
+ DB.sqls.must_equal ['SELECT * FROM items WHERE ((node_id = 3921) AND (kind = 201)) LIMIT 1']
775
765
  end
776
766
  end
777
767