sequel 3.29.0 → 3.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGELOG +35 -3
  2. data/Rakefile +2 -1
  3. data/doc/association_basics.rdoc +11 -0
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.30.0.txt +135 -0
  6. data/doc/testing.rdoc +17 -3
  7. data/lib/sequel/adapters/amalgalite.rb +2 -2
  8. data/lib/sequel/adapters/do/mysql.rb +5 -2
  9. data/lib/sequel/adapters/ibmdb.rb +2 -2
  10. data/lib/sequel/adapters/jdbc.rb +126 -43
  11. data/lib/sequel/adapters/jdbc/as400.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/db2.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/derby.rb +44 -19
  14. data/lib/sequel/adapters/jdbc/h2.rb +32 -19
  15. data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
  16. data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
  17. data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
  18. data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
  19. data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
  20. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
  21. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
  22. data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
  23. data/lib/sequel/adapters/mock.rb +2 -1
  24. data/lib/sequel/adapters/mysql.rb +4 -2
  25. data/lib/sequel/adapters/mysql2.rb +2 -2
  26. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  27. data/lib/sequel/adapters/openbase.rb +1 -1
  28. data/lib/sequel/adapters/oracle.rb +6 -6
  29. data/lib/sequel/adapters/postgres.rb +25 -12
  30. data/lib/sequel/adapters/shared/access.rb +14 -6
  31. data/lib/sequel/adapters/shared/db2.rb +36 -13
  32. data/lib/sequel/adapters/shared/firebird.rb +12 -5
  33. data/lib/sequel/adapters/shared/informix.rb +11 -3
  34. data/lib/sequel/adapters/shared/mssql.rb +94 -47
  35. data/lib/sequel/adapters/shared/mysql.rb +107 -49
  36. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  37. data/lib/sequel/adapters/shared/oracle.rb +54 -27
  38. data/lib/sequel/adapters/shared/postgres.rb +65 -26
  39. data/lib/sequel/adapters/shared/progress.rb +4 -1
  40. data/lib/sequel/adapters/shared/sqlite.rb +36 -20
  41. data/lib/sequel/adapters/sqlite.rb +2 -3
  42. data/lib/sequel/adapters/swift/mysql.rb +3 -2
  43. data/lib/sequel/adapters/swift/sqlite.rb +2 -2
  44. data/lib/sequel/adapters/tinytds.rb +14 -8
  45. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
  46. data/lib/sequel/database/misc.rb +6 -2
  47. data/lib/sequel/dataset/graph.rb +33 -7
  48. data/lib/sequel/dataset/prepared_statements.rb +19 -5
  49. data/lib/sequel/dataset/sql.rb +611 -201
  50. data/lib/sequel/model/associations.rb +12 -5
  51. data/lib/sequel/model/base.rb +20 -5
  52. data/lib/sequel/plugins/sharding.rb +9 -29
  53. data/lib/sequel/sql.rb +2 -1
  54. data/lib/sequel/timezones.rb +14 -4
  55. data/lib/sequel/version.rb +1 -1
  56. data/spec/adapters/mysql_spec.rb +10 -0
  57. data/spec/adapters/oracle_spec.rb +1 -1
  58. data/spec/core/core_sql_spec.rb +3 -1
  59. data/spec/core/database_spec.rb +42 -0
  60. data/spec/core/dataset_spec.rb +10 -3
  61. data/spec/core/mock_adapter_spec.rb +4 -0
  62. data/spec/core/object_graph_spec.rb +38 -0
  63. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  64. data/spec/extensions/association_dependencies_spec.rb +2 -12
  65. data/spec/extensions/association_pks_spec.rb +35 -39
  66. data/spec/extensions/caching_spec.rb +23 -50
  67. data/spec/extensions/class_table_inheritance_spec.rb +30 -82
  68. data/spec/extensions/composition_spec.rb +18 -13
  69. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  70. data/spec/extensions/identity_map_spec.rb +33 -103
  71. data/spec/extensions/instance_filters_spec.rb +10 -21
  72. data/spec/extensions/instance_hooks_spec.rb +6 -24
  73. data/spec/extensions/json_serializer_spec.rb +4 -5
  74. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  75. data/spec/extensions/list_spec.rb +17 -39
  76. data/spec/extensions/many_through_many_spec.rb +135 -277
  77. data/spec/extensions/migration_spec.rb +18 -15
  78. data/spec/extensions/named_timezones_spec.rb +1 -1
  79. data/spec/extensions/nested_attributes_spec.rb +97 -92
  80. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  81. data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
  82. data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
  83. data/spec/extensions/prepared_statements_spec.rb +11 -30
  84. data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
  85. data/spec/extensions/pretty_table_spec.rb +1 -6
  86. data/spec/extensions/rcte_tree_spec.rb +41 -43
  87. data/spec/extensions/schema_dumper_spec.rb +3 -6
  88. data/spec/extensions/serialization_spec.rb +20 -32
  89. data/spec/extensions/sharding_spec.rb +66 -140
  90. data/spec/extensions/single_table_inheritance_spec.rb +14 -36
  91. data/spec/extensions/spec_helper.rb +10 -64
  92. data/spec/extensions/sql_expr_spec.rb +20 -60
  93. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  94. data/spec/extensions/timestamps_spec.rb +6 -6
  95. data/spec/extensions/to_dot_spec.rb +1 -2
  96. data/spec/extensions/touch_spec.rb +13 -14
  97. data/spec/extensions/tree_spec.rb +11 -26
  98. data/spec/extensions/update_primary_key_spec.rb +30 -24
  99. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  100. data/spec/extensions/validation_helpers_spec.rb +16 -35
  101. data/spec/integration/dataset_test.rb +16 -4
  102. data/spec/integration/prepared_statement_test.rb +4 -2
  103. data/spec/model/eager_loading_spec.rb +16 -0
  104. data/spec/model/model_spec.rb +15 -1
  105. data/spec/model/record_spec.rb +60 -0
  106. metadata +23 -40
@@ -43,19 +43,17 @@ describe "Sequel::Schema::Generator dump methods" do
43
43
  index [:b, :c], :unique=>true
44
44
  end
45
45
 
46
- t = <<END_CODE
46
+ g.dump_indexes(:add_index=>:t).should == (<<END_CODE).strip
47
47
  add_index :t, [:a]
48
48
  add_index :t, [:c, :e], :name=>:blah
49
49
  add_index :t, [:b, :c], :unique=>true
50
50
  END_CODE
51
- g.dump_indexes(:add_index=>:t).should == t.strip
52
51
 
53
- t = <<END_CODE
52
+ g.dump_indexes(:drop_index=>:t).should == (<<END_CODE).strip
54
53
  drop_index :t, [:a]
55
54
  drop_index :t, [:c, :e], :name=>:blah
56
55
  drop_index :t, [:b, :c], :unique=>true
57
56
  END_CODE
58
- g.dump_indexes(:drop_index=>:t).should == t.strip
59
57
  end
60
58
 
61
59
  it "should raise an error if you try to dump a Generator that uses a constraint with a proc" do
@@ -296,7 +294,7 @@ END_MIG
296
294
  i = 0
297
295
  types.map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
298
296
  end
299
- table = <<END_MIG
297
+ @d.dump_table_schema(:x).should == (<<END_MIG).chomp
300
298
  create_table(:x) do
301
299
  Integer :c1
302
300
  Integer :c2
@@ -365,6 +363,5 @@ create_table(:x) do
365
363
  Bignum :c65
366
364
  end
367
365
  END_MIG
368
- @d.dump_table_schema(:x).should == table.chomp
369
366
  end
370
367
  end
@@ -9,10 +9,6 @@ else
9
9
  describe "Serialization plugin" do
10
10
  before do
11
11
  @c = Class.new(Sequel::Model(:items)) do
12
- include(Module.new do
13
- def before_save
14
- end
15
- end)
16
12
  no_primary_key
17
13
  columns :id, :abc, :def, :ghi
18
14
  end
@@ -79,11 +75,7 @@ describe "Serialization plugin" do
79
75
  @c.set_primary_key :id
80
76
  @c.plugin :serialization, :yaml, :abc, :def
81
77
  vals = nil
82
-
83
- ds = @c.dataset
84
- def ds.fetch_rows(sql, &block)
85
- block.call(:id => 1, :abc => "--- 1\n", :def => "--- hello\n")
86
- end
78
+ @c.dataset._fetch = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
87
79
 
88
80
  o = @c.first
89
81
  o.id.should == 1
@@ -94,18 +86,16 @@ describe "Serialization plugin" do
94
86
 
95
87
  o.update(:abc => 23)
96
88
  @c.create(:abc => [1, 2, 3])
97
- MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
98
- "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')"]
89
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
90
+ "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
91
+ "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')",
92
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
99
93
  end
100
94
 
101
95
  it "should translate values to and from marshal serialization format using accessor methods" do
102
96
  @c.set_primary_key :id
103
97
  @c.plugin :serialization, :marshal, :abc, :def
104
-
105
- ds = @c.dataset
106
- def ds.fetch_rows(sql, &block)
107
- block.call(:id => 1, :abc =>[Marshal.dump(1)].pack('m'), :def =>[Marshal.dump('hello')].pack('m'))
108
- end
98
+ @c.dataset._fetch = [:id => 1, :abc =>[Marshal.dump(1)].pack('m'), :def =>[Marshal.dump('hello')].pack('m')]
109
99
 
110
100
  o = @c.first
111
101
  o.id.should == 1
@@ -116,18 +106,16 @@ describe "Serialization plugin" do
116
106
 
117
107
  o.update(:abc => 23)
118
108
  @c.create(:abc => [1, 2, 3])
119
- MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{[Marshal.dump(23)].pack('m')}' WHERE (id = 1)",
120
- "INSERT INTO items (abc) VALUES ('#{[Marshal.dump([1, 2, 3])].pack('m')}')"]
109
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
110
+ "UPDATE items SET abc = '#{[Marshal.dump(23)].pack('m')}' WHERE (id = 1)",
111
+ "INSERT INTO items (abc) VALUES ('#{[Marshal.dump([1, 2, 3])].pack('m')}')",
112
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
121
113
  end
122
114
 
123
115
  it "should translate values to and from json serialization format using accessor methods" do
124
116
  @c.set_primary_key :id
125
117
  @c.plugin :serialization, :json, :abc, :def
126
-
127
- ds = @c.dataset
128
- def ds.fetch_rows(sql, &block)
129
- block.call(:id => 1, :abc => [1].to_json, :def => ["hello"].to_json)
130
- end
118
+ @c.dataset._fetch = {:id => 1, :abc => [1].to_json, :def => ["hello"].to_json}
131
119
 
132
120
  o = @c.first
133
121
  o.id.should == 1
@@ -139,18 +127,16 @@ describe "Serialization plugin" do
139
127
  o.update(:abc => [23])
140
128
  @c.create(:abc => [1,2,3])
141
129
 
142
- MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{[23].to_json}' WHERE (id = 1)",
143
- "INSERT INTO items (abc) VALUES ('#{[1,2,3].to_json}')"]
130
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
131
+ "UPDATE items SET abc = '#{[23].to_json}' WHERE (id = 1)",
132
+ "INSERT INTO items (abc) VALUES ('#{[1,2,3].to_json}')",
133
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
144
134
  end
145
135
 
146
136
  it "should copy serialization formats and columns to subclasses" do
147
137
  @c.set_primary_key :id
148
138
  @c.plugin :serialization, :yaml, :abc, :def
149
-
150
- ds = @c.dataset
151
- def ds.fetch_rows(sql, &block)
152
- block.call(:id => 1, :abc => "--- 1\n", :def => "--- hello\n")
153
- end
139
+ @c.dataset._fetch = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
154
140
 
155
141
  o = Class.new(@c).first
156
142
  o.id.should == 1
@@ -161,8 +147,10 @@ describe "Serialization plugin" do
161
147
 
162
148
  o.update(:abc => 23)
163
149
  Class.new(@c).create(:abc => [1, 2, 3])
164
- MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
165
- "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')"]
150
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
151
+ "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
152
+ "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')",
153
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
166
154
  end
167
155
 
168
156
  it "should clear the deserialized columns when refreshing" do
@@ -2,271 +2,197 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "sharding plugin" do
4
4
  before do
5
- @db = Sequel::Model.db.clone
5
+ @db = Sequel.mock(:numrows=>1, :autoid=>proc{1}, :servers=>{:s1=>{}, :s2=>{}, :s3=>{}, :s4=>{}})
6
6
  @Artist = Class.new(Sequel::Model(@db[:artists]))
7
7
  @Artist.class_eval do
8
+ dataset._fetch = {:id=>2, :name=>'YJM'}
8
9
  columns :id, :name
9
-
10
- def self.y
11
- {:id=>2, :name=>'YJM'}
12
- end
10
+ plugin :sharding
13
11
  end
14
12
  @Album = Class.new(Sequel::Model(@db[:albums]))
15
13
  @Album.class_eval do
14
+ dataset._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
16
15
  columns :id, :artist_id, :name
17
-
18
- def self.ds_ext(m=nil)
19
- @ds_ext = m if m
20
- @ds_ext
21
- end
22
-
23
- def self.y
24
- {:id=>1, :name=>'RF', :artist_id=>2}
25
- end
26
-
27
- private
28
-
29
- def _join_table_dataset(opts)
30
- ds = super
31
- m = model
32
- ds.meta_def(:model){m}
33
- ds.extend model.ds_ext
34
- ds
35
- end
16
+ plugin :sharding
36
17
  end
37
18
  @Tag = Class.new(Sequel::Model(@db[:tags]))
38
19
  @Tag.class_eval do
20
+ dataset._fetch = {:id=>3, :name=>'M'}
39
21
  columns :id, :name
40
-
41
- def self.y
42
- {:id=>3, :name=>'M'}
43
- end
22
+ plugin :sharding
44
23
  end
45
- models = [@Artist, @Album, @Tag]
46
24
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
47
25
  @Album.many_to_one :artist, :class=>@Artist
48
26
  @Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:albums_tags
49
- m = Module.new do
50
- def actions
51
- @actions ||= []
52
- end
53
- end
54
- models.each do |model|
55
- model.extend m
56
- model.plugin :sharding
57
- model.dataset.extend(ds_ext = Module.new do
58
- def insert(h={})
59
- model.actions << [:insert, h.dup, opts[:server]]
60
- 1
61
- end
62
- def delete
63
- model.actions << [:delete,(literal(opts[:where]) if opts[:where]), opts[:server]]
64
- 1
65
- end
66
- def update(h={})
67
- model.actions << [:update, h.dup, (literal(opts[:where]) if opts[:where]), opts[:server]]
68
- 1
69
- end
70
- def fetch_rows(sql)
71
- model.actions << [:fetch, (literal(opts[:where] || opts[:join]) if opts[:where] || opts[:join]), opts[:server]]
72
- yield(model.y)
73
- end
74
- end)
75
- @Album.ds_ext(ds_ext)
76
- end
77
- def @db.actions; @actions ||= []; end
78
- def @db.transaction(opts)
79
- actions << [:transaction, opts[:server]]
80
- super
81
- end
27
+ @db.sqls
82
28
  end
83
29
 
84
30
  specify "should allow you to instantiate a new object for a specified shard" do
85
31
  @Album.new_using_server(:s1, :name=>'RF').save
86
- @Album.actions.should == [[:insert, {:name=>"RF"}, :s1], [:fetch, "(id = 1)", :s1]]
32
+ @db.sqls.should == ["INSERT INTO albums (name) VALUES ('RF') -- s1", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s1"]
87
33
 
88
- @Album.actions.clear
89
34
  @Album.new_using_server(:s2){|o| o.name = 'MO'}.save
90
- @Album.actions.should == [[:insert, {:name=>"MO"}, :s2], [:fetch, "(id = 1)", :s2]]
35
+ @db.sqls.should == ["INSERT INTO albums (name) VALUES ('MO') -- s2", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s2"]
91
36
  end
92
37
 
93
38
  specify "should allow you to create and save a new object for a specified shard" do
94
39
  @Album.create_using_server(:s1, :name=>'RF')
95
- @Album.actions.should == [[:insert, {:name=>"RF"}, :s1], [:fetch, "(id = 1)", :s1]]
96
-
97
- @Album.actions.clear
40
+ @db.sqls.should == ["INSERT INTO albums (name) VALUES ('RF') -- s1", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s1"]
41
+
98
42
  @Album.create_using_server(:s2){|o| o.name = 'MO'}
99
- @Album.actions.should == [[:insert, {:name=>"MO"}, :s2], [:fetch, "(id = 1)", :s2]]
43
+ @db.sqls.should == ["INSERT INTO albums (name) VALUES ('MO') -- s2", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s2"]
100
44
  end
101
45
 
102
46
  specify "should have objects retrieved from a specific shard update that shard" do
103
47
  @Album.server(:s1).first.update(:name=>'MO')
104
- @Album.actions.should == [[:fetch, nil, :s1], [:update, {:name=>"MO"}, "(id = 1)", :s1]]
48
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "UPDATE albums SET name = 'MO' WHERE (id = 1) -- s1"]
105
49
  end
106
50
 
107
51
  specify "should have objects retrieved from a specific shard delete from that shard" do
108
52
  @Album.server(:s1).first.delete
109
- @Album.actions.should == [[:fetch, nil, :s1], [:delete, "(id = 1)", :s1]]
53
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "DELETE FROM albums WHERE (id = 1) -- s1"]
110
54
  end
111
55
 
112
56
  specify "should have objects retrieved from a specific shard reload from that shard" do
113
57
  @Album.server(:s1).first.reload
114
- @Album.actions.should == [[:fetch, nil, :s1], [:fetch, "(id = 1)", :s1]]
58
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s1"]
115
59
  end
116
60
 
117
61
  specify "should use current dataset's shard when eager loading if eagerly loaded dataset doesn't have its own shard" do
118
62
  albums = @Album.server(:s1).eager(:artist).all
119
- @Album.actions.should == [[:fetch, nil, :s1]]
120
- @Artist.actions.should == [[:fetch, "(artists.id IN (2))", :s1]]
121
- @Artist.actions.clear
122
- albums.length == 1
63
+ @db.sqls.should == ["SELECT * FROM albums -- s1", "SELECT * FROM artists WHERE (artists.id IN (2)) -- s1"]
64
+ albums.length.should == 1
123
65
  albums.first.artist.save
124
- @Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s1]]
66
+ @db.sqls.should == ["UPDATE artists SET name = 'YJM' WHERE (id = 2) -- s1"]
125
67
  end
126
68
 
127
69
  specify "should not use current dataset's shard when eager loading if eagerly loaded dataset has its own shard" do
128
70
  @Artist.dataset.opts[:server] = :s2
129
71
  albums = @Album.server(:s1).eager(:artist).all
130
- @Album.actions.should == [[:fetch, nil, :s1]]
131
- @Artist.actions.should == [[:fetch, "(artists.id IN (2))", :s2]]
132
- @Artist.actions.clear
133
- albums.length == 1
72
+ @db.sqls.should == ["SELECT * FROM albums -- s1", "SELECT * FROM artists WHERE (artists.id IN (2)) -- s2"]
73
+ albums.length.should == 1
134
74
  albums.first.artist.save
135
- @Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
75
+ @db.sqls.should == ["UPDATE artists SET name = 'YJM' WHERE (id = 2) -- s2"]
136
76
  end
137
77
 
138
78
  specify "should use current dataset's shard when eager graphing if eagerly graphed dataset doesn't have its own shard" do
139
79
  ds = @Album.server(:s1).eager_graph(:artist)
140
- def ds.fetch_rows(sql)
141
- super(sql)
142
- yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
143
- end
80
+ ds._fetch = {:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'}
144
81
  albums = ds.all
145
- @Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", :s1]]
146
- albums.length == 1
82
+ @db.sqls.should == ["SELECT albums.id, albums.artist_id, albums.name, artist.id AS artist_id_0, artist.name AS artist_name FROM albums LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id) -- s1"]
83
+ albums.length.should == 1
147
84
  albums.first.artist.save
148
- @Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s1]]
85
+ @db.sqls.should == ["UPDATE artists SET name = 'YJM' WHERE (id = 2) -- s1"]
149
86
  end
150
87
 
151
88
  specify "should not use current dataset's shard when eager graphing if eagerly graphed dataset has its own shard" do
152
89
  @Artist.dataset.opts[:server] = :s2
153
90
  ds = @Album.server(:s1).eager_graph(:artist)
154
- def ds.fetch_rows(sql)
155
- super(sql)
156
- yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
157
- end
91
+ ds._fetch = {:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'}
158
92
  albums = ds.all
159
- @Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", :s1]]
160
- albums.length == 1
93
+ @db.sqls.should == ["SELECT albums.id, albums.artist_id, albums.name, artist.id AS artist_id_0, artist.name AS artist_name FROM albums LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id) -- s1"]
94
+ albums.length.should == 1
161
95
  albums.first.artist.save
162
- @Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
96
+ @db.sqls.should == ["UPDATE artists SET name = 'YJM' WHERE (id = 2) -- s2"]
163
97
  end
164
98
 
165
99
  specify "should use eagerly graphed dataset shard for eagerly graphed objects even if current dataset does not have a shard" do
166
100
  @Artist.dataset.opts[:server] = :s2
167
101
  ds = @Album.eager_graph(:artist)
168
- def ds.fetch_rows(sql)
169
- super(sql)
170
- yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
171
- end
102
+ ds._fetch = {:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'}
172
103
  albums = ds.all
173
- @Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", nil]]
174
- albums.length == 1
104
+ @db.sqls.should == ["SELECT albums.id, albums.artist_id, albums.name, artist.id AS artist_id_0, artist.name AS artist_name FROM albums LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id)"]
105
+ albums.length.should == 1
175
106
  albums.first.artist.save
176
- @Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
107
+ @db.sqls.should == ["UPDATE artists SET name = 'YJM' WHERE (id = 2) -- s2"]
177
108
  end
178
109
 
179
110
  specify "should have objects retrieved from a specific shard use associated objects from that shard, with modifications to the associated objects using that shard" do
180
111
  album = @Album.server(:s1).first
181
- @Album.actions.should == [[:fetch, nil, :s1]]
112
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1"]
182
113
  album.artist.update(:name=>'AS')
183
- @Artist.actions.should == [[:fetch, "(artists.id = 2)", :s1], [:update, {:name=>"AS"}, "(id = 2)", :s1]]
114
+ @db.sqls.should == ["SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1 -- s1", "UPDATE artists SET name = 'AS' WHERE (id = 2) -- s1"]
184
115
  album.tags.map{|a| a.update(:name=>'SR')}
185
- @Tag.actions.should == [[:fetch, "( INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.album_id = 1)))", :s1], [:update, {:name=>"SR"}, "(id = 3)", :s1]]
186
-
187
- @Album.actions.clear
188
- @Artist.actions.clear
116
+ @db.sqls.should == ["SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.album_id = 1)) -- s1", "UPDATE tags SET name = 'SR' WHERE (id = 3) -- s1"]
189
117
  @Artist.server(:s2).first.albums.map{|a| a.update(:name=>'MO')}
190
- @Artist.actions.should == [[:fetch, nil, :s2]]
191
- @Album.actions.should == [[:fetch, "(albums.artist_id = 2)", :s2], [:update, {:name=>"MO"}, "(id = 1)", :s2]]
118
+ @db.sqls.should == ["SELECT * FROM artists LIMIT 1 -- s2", "SELECT * FROM albums WHERE (albums.artist_id = 2) -- s2", "UPDATE albums SET name = 'MO' WHERE (id = 1) -- s2"]
192
119
  end
193
120
 
194
121
  specify "should have objects retrieved from a specific shard add associated objects to that shard" do
195
122
  album = @Album.server(:s1).first
196
123
  artist = @Artist.server(:s2).first
197
- @Album.actions.clear
198
- @Artist.actions.clear
124
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM artists LIMIT 1 -- s2"]
199
125
 
200
126
  artist.add_album(:name=>'MO')
201
- @Album.actions.should == [[:insert, {:name=>"MO", :artist_id=>2}, :s2], [:fetch, "(id = 1)", :s2]]
202
- @Album.actions.clear
127
+ sqls = @db.sqls
128
+ ["INSERT INTO albums (artist_id, name) VALUES (2, 'MO') -- s2", "INSERT INTO albums (name, artist_id) VALUES ('MO', 2) -- s2"].should include(sqls.shift)
129
+ sqls.should == ["SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s2"]
203
130
 
204
131
  album.add_tag(:name=>'SR')
205
- @Tag.actions.should == [[:insert, {:name=>"SR"}, :s1], [:fetch, "(id = 1)", :s1]]
206
- @Album.actions.should == [[:insert, {:album_id=>1, :tag_id=>3}, :s1]]
132
+ sqls = @db.sqls
133
+ ["INSERT INTO albums_tags (album_id, tag_id) VALUES (1, 3) -- s1", "INSERT INTO albums_tags (tag_id, album_id) VALUES (3, 1) -- s1"].should include(sqls.pop)
134
+ sqls.should == ["INSERT INTO tags (name) VALUES ('SR') -- s1", "SELECT * FROM tags WHERE (id = 1) LIMIT 1 -- s1", ]
207
135
  end
208
136
 
209
137
  specify "should have objects retrieved from a specific shard remove associated objects from that shard" do
210
138
  album = @Album.server(:s1).first
211
139
  artist = @Artist.server(:s2).first
212
- @Album.actions.clear
213
- @Artist.actions.clear
140
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM artists LIMIT 1 -- s2"]
214
141
 
215
142
  artist.remove_album(1)
216
- @Album.actions.should == [[:fetch, "((albums.artist_id = 2) AND (albums.id = 1))", :s2], [:update, {:name=>"RF", :artist_id=>nil}, "(id = 1)", :s2]]
217
- @Album.actions.clear
143
+ sqls = @db.sqls
144
+ ["UPDATE albums SET artist_id = NULL, name = 'RF' WHERE (id = 1) -- s2", "UPDATE albums SET name = 'RF', artist_id = NULL WHERE (id = 1) -- s2"].should include(sqls.pop)
145
+ sqls.should == ["SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (albums.id = 1)) LIMIT 1 -- s2"]
218
146
 
219
147
  album.remove_tag(3)
220
- @Tag.actions.should == [[:fetch, "(tags.id = 3)", :s1]]
221
- @Album.actions.should == [[:delete, "((album_id = 1) AND (tag_id = 3))", :s1]]
148
+ @db.sqls.should == ["SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.album_id = 1)) WHERE (tags.id = 3) LIMIT 1 -- s1", "DELETE FROM albums_tags WHERE ((album_id = 1) AND (tag_id = 3)) -- s1"]
222
149
  end
223
150
 
224
151
  specify "should have objects retrieved from a specific shard remove all associated objects from that shard" do
225
152
  album = @Album.server(:s1).first
226
153
  artist = @Artist.server(:s2).first
227
- @Album.actions.clear
228
- @Artist.actions.clear
154
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM artists LIMIT 1 -- s2"]
229
155
 
230
156
  artist.remove_all_albums
231
- @Album.actions.should == [[:update, {:artist_id=>nil}, "(artist_id = 2)", :s2]]
232
- @Album.actions.clear
157
+ @db.sqls.should == ["UPDATE albums SET artist_id = NULL WHERE (artist_id = 2) -- s2"]
233
158
 
234
159
  album.remove_all_tags
235
- @Album.actions.should == [[:delete, "(album_id = 1)", :s1]]
160
+ @db.sqls.should == ["DELETE FROM albums_tags WHERE (album_id = 1) -- s1"]
236
161
  end
237
162
 
238
163
  specify "should not override a server already set on an associated object" do
239
164
  album = @Album.server(:s1).first
240
165
  artist = @Artist.server(:s2).first
241
- @Album.actions.clear
242
- @Artist.actions.clear
166
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM artists LIMIT 1 -- s2"]
243
167
 
244
168
  artist.add_album(@Album.load(:id=>4, :name=>'MO').set_server(:s3))
245
- @Album.actions.should == [[:update, {:name=>"MO", :artist_id=>2}, "(id = 4)", :s3]]
246
- @Album.actions.clear
169
+ ["UPDATE albums SET artist_id = 2, name = 'MO' WHERE (id = 4) -- s3", "UPDATE albums SET name = 'MO', artist_id = 2 WHERE (id = 4) -- s3"].should include(@db.sqls.pop)
247
170
 
248
171
  artist.remove_album(@Album.load(:id=>5, :name=>'T', :artist_id=>2).set_server(:s4))
249
172
  # Should select from current object's shard to check existing association, but update associated object's shard
250
- @Album.actions.should == [[:fetch, "((albums.artist_id = 2) AND (id = 5))", :s2], [:update, {:name=>"T", :artist_id=>nil}, "(id = 5)", :s4]]
251
- @Album.actions.clear
173
+ sqls = @db.sqls
174
+ ["UPDATE albums SET artist_id = NULL, name = 'T' WHERE (id = 5) -- s4", "UPDATE albums SET name = 'T', artist_id = NULL WHERE (id = 5) -- s4"].should include(sqls.pop)
175
+ sqls.should == ["SELECT 1 FROM albums WHERE ((albums.artist_id = 2) AND (id = 5)) LIMIT 1 -- s2"]
252
176
  end
253
177
 
254
178
  specify "should be able to set a shard to use for any object using set_server" do
255
179
  @Album.server(:s1).first.set_server(:s2).reload
256
- @Album.actions.should == [[:fetch, nil, :s1], [:fetch, "(id = 1)", :s2]]
180
+ @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM albums WHERE (id = 1) LIMIT 1 -- s2"]
257
181
  end
258
182
 
259
183
  specify "should use transactions on the correct shard" do
260
184
  @Album.use_transactions = true
261
185
  @Album.server(:s2).first.save
262
- @Album.actions.should == [[:fetch, nil, :s2], [:update, {:name=>"RF", :artist_id=>2}, "(id = 1)", :s2]]
263
- @db.actions.should == [[:transaction, :s2]]
186
+ sqls = @db.sqls
187
+ ["UPDATE albums SET artist_id = 2, name = 'RF' WHERE (id = 1) -- s2", "UPDATE albums SET name = 'RF', artist_id = 2 WHERE (id = 1) -- s2"].should include(sqls.slice!(2))
188
+ sqls.should == ["SELECT * FROM albums LIMIT 1 -- s2", "BEGIN -- s2", "COMMIT -- s2"]
264
189
  end
265
190
 
266
- specify "should use not override shard given when saving" do
191
+ specify "should use override current shard when saving with given :server option" do
267
192
  @Album.use_transactions = true
268
193
  @Album.server(:s2).first.save(:server=>:s1)
269
- @Album.actions.should == [[:fetch, nil, :s2], [:update, {:name=>"RF", :artist_id=>2}, "(id = 1)", :s2]]
270
- @db.actions.should == [[:transaction, :s1]]
194
+ sqls = @db.sqls
195
+ ["UPDATE albums SET artist_id = 2, name = 'RF' WHERE (id = 1) -- s1", "UPDATE albums SET name = 'RF', artist_id = 2 WHERE (id = 1) -- s1"].should include(sqls.slice!(2))
196
+ sqls.should == ["SELECT * FROM albums LIMIT 1 -- s2", "BEGIN -- s1", "COMMIT -- s1"]
271
197
  end
272
198
  end