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
@@ -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