sequel 3.30.0 → 3.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGELOG +40 -0
  2. data/Rakefile +12 -2
  3. data/doc/association_basics.rdoc +28 -0
  4. data/doc/dataset_filtering.rdoc +8 -0
  5. data/doc/opening_databases.rdoc +1 -0
  6. data/doc/release_notes/3.31.0.txt +146 -0
  7. data/lib/sequel/adapters/jdbc.rb +7 -6
  8. data/lib/sequel/adapters/jdbc/derby.rb +5 -0
  9. data/lib/sequel/adapters/jdbc/h2.rb +6 -1
  10. data/lib/sequel/adapters/mock.rb +21 -2
  11. data/lib/sequel/adapters/shared/db2.rb +10 -0
  12. data/lib/sequel/adapters/shared/mssql.rb +40 -5
  13. data/lib/sequel/adapters/shared/mysql.rb +19 -2
  14. data/lib/sequel/adapters/shared/oracle.rb +13 -1
  15. data/lib/sequel/adapters/shared/postgres.rb +52 -8
  16. data/lib/sequel/adapters/shared/sqlite.rb +4 -3
  17. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  18. data/lib/sequel/database/schema_generator.rb +9 -2
  19. data/lib/sequel/dataset/actions.rb +37 -19
  20. data/lib/sequel/dataset/features.rb +10 -0
  21. data/lib/sequel/dataset/prepared_statements.rb +0 -10
  22. data/lib/sequel/dataset/query.rb +13 -1
  23. data/lib/sequel/dataset/sql.rb +6 -1
  24. data/lib/sequel/model/associations.rb +14 -4
  25. data/lib/sequel/model/base.rb +10 -0
  26. data/lib/sequel/plugins/serialization.rb +82 -43
  27. data/lib/sequel/version.rb +1 -1
  28. data/spec/adapters/mssql_spec.rb +46 -0
  29. data/spec/adapters/mysql_spec.rb +3 -0
  30. data/spec/adapters/postgres_spec.rb +61 -24
  31. data/spec/core/database_spec.rb +31 -18
  32. data/spec/core/dataset_spec.rb +90 -13
  33. data/spec/core/mock_adapter_spec.rb +37 -0
  34. data/spec/extensions/instance_filters_spec.rb +1 -0
  35. data/spec/extensions/nested_attributes_spec.rb +1 -1
  36. data/spec/extensions/serialization_spec.rb +49 -5
  37. data/spec/extensions/sharding_spec.rb +1 -1
  38. data/spec/integration/associations_test.rb +15 -0
  39. data/spec/integration/dataset_test.rb +71 -0
  40. data/spec/integration/prepared_statement_test.rb +8 -0
  41. data/spec/model/association_reflection_spec.rb +27 -0
  42. data/spec/model/associations_spec.rb +18 -3
  43. data/spec/model/base_spec.rb +20 -0
  44. data/spec/model/eager_loading_spec.rb +21 -0
  45. metadata +4 -2
@@ -39,6 +39,12 @@ describe "Simple Dataset operations" do
39
39
  @ds.filter(:id=>2).first[:number].should == 20
40
40
  end
41
41
 
42
+ specify "should have insert_multiple return primary key values" do
43
+ @ds.insert_multiple([{:number=>20}, {:number=>30}]).should == [2, 3]
44
+ @ds.filter(:id=>2).get(:number).should == 20
45
+ @ds.filter(:id=>3).get(:number).should == 30
46
+ end
47
+
42
48
  specify "should join correctly" do
43
49
  @ds.join(:items___b, :id=>:id).select_all(:items).all.should == [{:id=>1, :number=>10}]
44
50
  end
@@ -713,6 +719,55 @@ describe "Sequel::Dataset#import and #multi_insert" do
713
719
  end
714
720
  end
715
721
 
722
+ describe "Sequel::Dataset#import and #multi_insert :return=>:primary_key " do
723
+ before do
724
+ @db = INTEGRATION_DB
725
+ @db.create_table!(:imp){primary_key :id; Integer :i}
726
+ @ds = @db[:imp]
727
+ end
728
+ after do
729
+ @db.drop_table(:imp)
730
+ end
731
+
732
+ specify "should return primary key values " do
733
+ @ds.multi_insert([{:i=>10}, {:i=>20}, {:i=>30}], :return=>:primary_key).should == [1, 2, 3]
734
+ @ds.import([:i], [[40], [50], [60]], :return=>:primary_key).should == [4, 5, 6]
735
+ @ds.order(:id).map([:id, :i]).should == [[1, 10], [2, 20], [3, 30], [4, 40], [5, 50], [6, 60]]
736
+ end
737
+
738
+ specify "should return primary key values when :slice is used" do
739
+ @ds.multi_insert([{:i=>10}, {:i=>20}, {:i=>30}], :return=>:primary_key, :slice=>2).should == [1, 2, 3]
740
+ @ds.import([:i], [[40], [50], [60]], :return=>:primary_key, :slice=>2).should == [4, 5, 6]
741
+ @ds.order(:id).map([:id, :i]).should == [[1, 10], [2, 20], [3, 30], [4, 40], [5, 50], [6, 60]]
742
+ end
743
+ end
744
+
745
+ describe "Sequel::Dataset convenience methods" do
746
+ before(:all) do
747
+ @db = INTEGRATION_DB
748
+ @db.create_table!(:a){Integer :a; Integer :b; Integer :c}
749
+ @ds = @db[:a]
750
+ @ds.insert(1, 3, 5)
751
+ @ds.insert(1, 3, 6)
752
+ @ds.insert(1, 4, 5)
753
+ @ds.insert(2, 3, 5)
754
+ @ds.insert(2, 4, 6)
755
+ end
756
+ after(:all) do
757
+ @db.drop_table(:a)
758
+ end
759
+
760
+ it "#group_rollup should include hierarchy of groupings" do
761
+ @ds.group_by(:a).group_rollup.select_map([:a, :sum.sql_function(:b).cast(Integer).as(:b), :sum.sql_function(:c).cast(Integer).as(:c)]).sort_by{|x| x.inspect}.should == [[1, 10, 16], [2, 7, 11], [nil, 17, 27]]
762
+ @ds.group_by(:a, :b).group_rollup.select_map([:a, :b, :sum.sql_function(:c).cast(Integer).as(:c)]).sort_by{|x| x.inspect}.should == [[1, 3, 11], [1, 4, 5], [1, nil, 16], [2, 3, 5], [2, 4, 6], [2, nil, 11], [nil, nil, 27]]
763
+ end if INTEGRATION_DB.dataset.supports_group_rollup?
764
+
765
+ it "#group_cube should include all combinations of groupings" do
766
+ @ds.group_by(:a).group_cube.select_map([:a, :sum.sql_function(:b).cast(Integer).as(:b), :sum.sql_function(:c).cast(Integer).as(:c)]).sort_by{|x| x.inspect}.should == [[1, 10, 16], [2, 7, 11], [nil, 17, 27]]
767
+ @ds.group_by(:a, :b).group_cube.select_map([:a, :b, :sum.sql_function(:c).cast(Integer).as(:c)]).sort_by{|x| x.inspect}.should == [[1, 3, 11], [1, 4, 5], [1, nil, 16], [2, 3, 5], [2, 4, 6], [2, nil, 11], [nil, 3, 16], [nil, 4, 11], [nil, nil, 27]]
768
+ end if INTEGRATION_DB.dataset.supports_group_cube?
769
+ end
770
+
716
771
  describe "Sequel::Dataset convenience methods" do
717
772
  before(:all) do
718
773
  @db = INTEGRATION_DB
@@ -738,6 +793,22 @@ describe "Sequel::Dataset convenience methods" do
738
793
  @ds.empty?.should == false
739
794
  end
740
795
 
796
+ it "#empty? should work correctly for datasets with limits" do
797
+ ds = @ds.limit(1)
798
+ ds.empty?.should == true
799
+ ds.insert(20, 10)
800
+ ds.empty?.should == false
801
+ end
802
+
803
+ it "#empty? should work correctly for datasets with limits and offsets" do
804
+ ds = @ds.limit(1, 1)
805
+ ds.empty?.should == true
806
+ ds.insert(20, 10)
807
+ ds.empty?.should == true
808
+ ds.insert(20, 10)
809
+ ds.empty?.should == false
810
+ end
811
+
741
812
  it "#group_and_count should return a grouping by count" do
742
813
  @ds.group_and_count(:a).order(:count).all.should == []
743
814
  @ds.insert(20, 10)
@@ -66,6 +66,10 @@ describe "Prepared Statements and Bound Arguments" do
66
66
  @ds.filter(:id=>:$i).filter(:numb=>@ds.select(:numb).filter(:numb=>:$n)).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :numb=>10}]
67
67
  end
68
68
 
69
+ specify "should support subselects with exists with call" do
70
+ @ds.filter(:id=>:$i).filter(@ds.select(:numb).filter(:numb=>:$n).exists).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :numb=>10}]
71
+ end
72
+
69
73
  specify "should support subselects with literal strings with call" do
70
74
  @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter("numb = ?", :$n)).call(:select, :n=>10, :i=>1).should == [{:id=>1, :numb=>10}]
71
75
  end
@@ -151,6 +155,10 @@ describe "Prepared Statements and Bound Arguments" do
151
155
  @ds.filter(:id=>:$i).filter(:numb=>@ds.select(:numb).filter(:numb=>:$n)).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :numb=>10}]
152
156
  end
153
157
 
158
+ specify "should support subselects with exists with prepare" do
159
+ @ds.filter(:id=>:$i).filter(@ds.select(:numb).filter(:numb=>:$n).exists).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :numb=>10}]
160
+ end
161
+
154
162
  specify "should support subselects with literal strings with prepare" do
155
163
  @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter("numb = ?", :$n)).prepare(:select, :seq_select).call(:n=>10, :i=>1).should == [{:id=>1, :numb=>10}]
156
164
  end
@@ -187,6 +187,33 @@ describe Sequel::Model::Associations::AssociationReflection, "#associated_object
187
187
  end
188
188
  end
189
189
 
190
+ describe Sequel::Model::Associations::AssociationReflection do
191
+ before do
192
+ @c = Class.new(Sequel::Model(:foo))
193
+ @c.meta_def(:name){"C"}
194
+ end
195
+
196
+ it "one_to_many #qualified_primary_key should be a qualified version of the primary key" do
197
+ @c.one_to_many :cs, :class=>@c
198
+ @c.dataset.literal(@c.association_reflection(:cs).qualified_primary_key).should == 'foo.id'
199
+ end
200
+
201
+ it "many_to_many #associated_key_column should be the left key" do
202
+ @c.many_to_many :cs, :class=>@c
203
+ @c.association_reflection(:cs).associated_key_column.should == :c_id
204
+ end
205
+
206
+ it "many_to_many #qualified_right_key should be a qualified version of the primary key" do
207
+ @c.many_to_many :cs, :class=>@c, :right_key=>:c2_id
208
+ @c.dataset.literal(@c.association_reflection(:cs).qualified_right_key).should == 'cs_cs.c2_id'
209
+ end
210
+
211
+ it "many_to_many #qualified_right_primary_key should be a qualified version of the primary key" do
212
+ @c.many_to_many :cs, :class=>@c
213
+ @c.dataset.literal(@c.association_reflection(:cs).qualified_right_primary_key).should == 'foo.id'
214
+ end
215
+ end
216
+
190
217
  describe Sequel::Model::Associations::AssociationReflection, "#remove_before_destroy?" do
191
218
  before do
192
219
  @c = Class.new(Sequel::Model(:foo))
@@ -148,6 +148,21 @@ describe Sequel::Model, "many_to_one" do
148
148
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
149
149
  end
150
150
 
151
+ it "should allow association with the same name as the key if :key_alias is given" do
152
+ @c2.def_column_alias(:parent_id_id, :parent_id)
153
+ @c2.many_to_one :parent_id, :key_column=>:parent_id, :class => @c2
154
+ d = @c2.load(:id => 1, :parent_id => 234)
155
+ d.parent_id_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"
156
+ d.parent_id.should == @c2.load(:x => 1, :id => 1)
157
+ d.parent_id_id.should == 234
158
+ d[:parent_id].should == 234
159
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
160
+
161
+ d.parent_id_id = 3
162
+ d.parent_id_id.should == 3
163
+ d[:parent_id].should == 3
164
+ end
165
+
151
166
  it "should use implicit class if omitted" do
152
167
  begin
153
168
  class ::ParParent < Sequel::Model; end
@@ -1181,7 +1196,7 @@ describe Sequel::Model, "one_to_many" do
1181
1196
  a = @c1.load(:id => 2345, :node_id => 1234)
1182
1197
  a.should == n.remove_attribute(a)
1183
1198
  a.values.should == {:node_id => nil, :id => 2345}
1184
- MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1199
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1185
1200
  end
1186
1201
 
1187
1202
  it "should have the remove_ method raise an error if the passed object is not already associated" do
@@ -1191,7 +1206,7 @@ describe Sequel::Model, "one_to_many" do
1191
1206
  a = @c1.load(:id => 2345, :node_id => 1234)
1192
1207
  @c1.dataset._fetch = []
1193
1208
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1194
- MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1209
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1195
1210
  end
1196
1211
 
1197
1212
  it "should accept a hash for the add_ method and create a new record" do
@@ -1287,7 +1302,7 @@ describe Sequel::Model, "one_to_many" do
1287
1302
  n.remove_attribute(a).should == a
1288
1303
  sqls = MODEL_DB.sqls
1289
1304
  sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1290
- sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1"]
1305
+ sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1"]
1291
1306
  end
1292
1307
 
1293
1308
  it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
@@ -35,6 +35,26 @@ describe "Model attribute setters" do
35
35
  end
36
36
  end
37
37
 
38
+ describe "Model.def_column_alias" do
39
+ before do
40
+ @o = Class.new(Sequel::Model(:items)) do
41
+ columns :id
42
+ def_column_alias(:id2, :id)
43
+ end.load(:id=>1)
44
+ MODEL_DB.reset
45
+ end
46
+
47
+ it "should create an getter alias for the column" do
48
+ @o.id2.should == 1
49
+ end
50
+
51
+ it "should create an setter alias for the column" do
52
+ @o.id2 = 2
53
+ @o.id2.should == 2
54
+ @o.values.should == {:id => 2}
55
+ end
56
+ end
57
+
38
58
  describe Sequel::Model, "dataset" do
39
59
  before do
40
60
  @a = Class.new(Sequel::Model(:items))
@@ -102,6 +102,16 @@ describe Sequel::Model, "#eager" do
102
102
  MODEL_DB.sqls.should == []
103
103
  end
104
104
 
105
+ it "should eagerly load a single many_to_one association with the same name as the column" do
106
+ EagerAlbum.def_column_alias(:band_id_id, :band_id)
107
+ EagerAlbum.many_to_one :band_id, :key_column=>:band_id, :class=>EagerBand
108
+ a = EagerAlbum.eager(:band_id).all
109
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (bands.id IN (2))']
110
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
111
+ a.first.band_id.should == EagerBand.load(:id=>2)
112
+ MODEL_DB.sqls.should == []
113
+ end
114
+
105
115
  it "should eagerly load a single one_to_one association" do
106
116
  EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id
107
117
  a = EagerAlbum.eager(:track).all
@@ -757,6 +767,17 @@ describe Sequel::Model, "#eager_graph" do
757
767
  a.first.band.should == GraphBand.load(:id => 2, :vocalist_id=>3)
758
768
  end
759
769
 
770
+ it "should eagerly load a single many_to_one association with the same name as a column" do
771
+ GraphAlbum.def_column_alias(:band_id_id, :band_id)
772
+ GraphAlbum.many_to_one :band_id, :key_column=>:band_id, :class=>GraphBand
773
+ ds = GraphAlbum.eager_graph(:band_id)
774
+ ds.sql.should == 'SELECT albums.id, albums.band_id, band_id.id AS band_id_id, band_id.vocalist_id FROM albums LEFT OUTER JOIN bands AS band_id ON (band_id.id = albums.band_id)'
775
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_id=>2, :vocalist_id=>3}
776
+ a = ds.all
777
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
778
+ a.first.band_id.should == GraphBand.load(:id => 2, :vocalist_id=>3)
779
+ end
780
+
760
781
  it "should eagerly load a single one_to_one association" do
761
782
  GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
762
783
  ds = GraphAlbum.eager_graph(:track)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.30.0
4
+ version: 3.31.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-01 00:00:00.000000000 Z
12
+ date: 2012-01-03 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: The Database Toolkit for Ruby
15
15
  email: code@jeremyevans.net
@@ -89,6 +89,7 @@ extra_rdoc_files:
89
89
  - doc/release_notes/3.28.0.txt
90
90
  - doc/release_notes/3.29.0.txt
91
91
  - doc/release_notes/3.30.0.txt
92
+ - doc/release_notes/3.31.0.txt
92
93
  files:
93
94
  - MIT-LICENSE
94
95
  - CHANGELOG
@@ -156,6 +157,7 @@ files:
156
157
  - doc/release_notes/3.28.0.txt
157
158
  - doc/release_notes/3.29.0.txt
158
159
  - doc/release_notes/3.30.0.txt
160
+ - doc/release_notes/3.31.0.txt
159
161
  - doc/sharding.rdoc
160
162
  - doc/sql.rdoc
161
163
  - doc/validations.rdoc