sequel 4.0.0 → 4.1.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/doc/active_record.rdoc +2 -2
  4. data/doc/cheat_sheet.rdoc +0 -5
  5. data/doc/opening_databases.rdoc +3 -2
  6. data/doc/prepared_statements.rdoc +6 -0
  7. data/doc/release_notes/4.1.0.txt +85 -0
  8. data/doc/schema_modification.rdoc +9 -2
  9. data/lib/sequel/adapters/jdbc.rb +5 -0
  10. data/lib/sequel/adapters/mysql2.rb +24 -3
  11. data/lib/sequel/adapters/odbc.rb +6 -4
  12. data/lib/sequel/adapters/postgres.rb +25 -0
  13. data/lib/sequel/adapters/shared/mysql.rb +4 -29
  14. data/lib/sequel/adapters/shared/postgres.rb +14 -3
  15. data/lib/sequel/adapters/shared/sqlite.rb +4 -0
  16. data/lib/sequel/adapters/utils/replace.rb +36 -0
  17. data/lib/sequel/database/query.rb +1 -0
  18. data/lib/sequel/database/schema_generator.rb +12 -5
  19. data/lib/sequel/database/schema_methods.rb +2 -0
  20. data/lib/sequel/dataset/features.rb +5 -0
  21. data/lib/sequel/extensions/pg_json_ops.rb +0 -6
  22. data/lib/sequel/model/associations.rb +1 -1
  23. data/lib/sequel/plugins/instance_filters.rb +11 -1
  24. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -2
  25. data/lib/sequel/plugins/prepared_statements.rb +38 -9
  26. data/lib/sequel/plugins/update_primary_key.rb +10 -0
  27. data/lib/sequel/sql.rb +1 -1
  28. data/lib/sequel/version.rb +1 -1
  29. data/spec/adapters/mysql_spec.rb +1 -22
  30. data/spec/adapters/postgres_spec.rb +79 -2
  31. data/spec/core/database_spec.rb +10 -0
  32. data/spec/core/dataset_spec.rb +8 -3
  33. data/spec/core/expression_filters_spec.rb +1 -1
  34. data/spec/core/schema_spec.rb +17 -2
  35. data/spec/extensions/caching_spec.rb +2 -2
  36. data/spec/extensions/hook_class_methods_spec.rb +0 -4
  37. data/spec/extensions/instance_filters_spec.rb +22 -0
  38. data/spec/extensions/migration_spec.rb +5 -5
  39. data/spec/extensions/nested_attributes_spec.rb +4 -4
  40. data/spec/extensions/prepared_statements_spec.rb +37 -26
  41. data/spec/extensions/update_primary_key_spec.rb +13 -0
  42. data/spec/integration/dataset_test.rb +36 -0
  43. data/spec/model/associations_spec.rb +20 -2
  44. data/spec/model/hooks_spec.rb +1 -7
  45. metadata +5 -2
@@ -508,6 +508,11 @@ describe "Database#run" do
508
508
  @db.sqls.should == ["DELETE FROM items"]
509
509
  end
510
510
 
511
+ specify "should handle placeholder literal strings" do
512
+ @db.run(Sequel.lit("DELETE FROM ?", :items))
513
+ @db.sqls.should == ["DELETE FROM items"]
514
+ end
515
+
511
516
  specify "should return nil" do
512
517
  @db.run("DELETE FROM items").should be_nil
513
518
  end
@@ -528,6 +533,11 @@ describe "Database#<<" do
528
533
  @db.sqls.should == ["DELETE FROM items"]
529
534
  end
530
535
 
536
+ specify "should handle placeholder literal strings" do
537
+ @db << Sequel.lit("DELETE FROM ?", :items)
538
+ @db.sqls.should == ["DELETE FROM items"]
539
+ end
540
+
531
541
  specify "should be chainable" do
532
542
  @db << "DELETE FROM items" << "DELETE FROM items2"
533
543
  @db.sqls.should == ["DELETE FROM items", "DELETE FROM items2"]
@@ -1609,10 +1609,10 @@ describe "Dataset#limit" do
1609
1609
  specify "should raise an error if an invalid limit or offset is used" do
1610
1610
  proc{@dataset.limit(-1)}.should raise_error(Sequel::Error)
1611
1611
  proc{@dataset.limit(0)}.should raise_error(Sequel::Error)
1612
- proc{@dataset.limit(1)}.should_not raise_error(Sequel::Error)
1612
+ proc{@dataset.limit(1)}.should_not raise_error
1613
1613
  proc{@dataset.limit(1, -1)}.should raise_error(Sequel::Error)
1614
- proc{@dataset.limit(1, 0)}.should_not raise_error(Sequel::Error)
1615
- proc{@dataset.limit(1, 1)}.should_not raise_error(Sequel::Error)
1614
+ proc{@dataset.limit(1, 0)}.should_not raise_error
1615
+ proc{@dataset.limit(1, 1)}.should_not raise_error
1616
1616
  end
1617
1617
  end
1618
1618
 
@@ -4508,3 +4508,8 @@ describe "Dataset#escape_like" do
4508
4508
  end
4509
4509
  end
4510
4510
 
4511
+ describe "Dataset#supports_replace?" do
4512
+ it "should be false by default" do
4513
+ Sequel::Dataset.new(nil).supports_replace?.should be_false
4514
+ end
4515
+ end
@@ -429,7 +429,7 @@ describe Sequel::SQL::VirtualRow do
429
429
  end
430
430
 
431
431
  it "should raise an error if an unsupported argument is used with a block" do
432
- proc{@d.l{count(:blah){}}}.should raise_error(Sequel::Error)
432
+ proc{@d.where{count(:blah){}}}.should raise_error(Sequel::Error)
433
433
  end
434
434
 
435
435
  it "should treat methods with a block and a leading argument :over as a window function call" do
@@ -446,7 +446,7 @@ describe "DB#create_table" do
446
446
  meta_def(@db, :execute_ddl){|*a| raise Sequel::DatabaseError if /blah/.match(a.first); super(*a)}
447
447
  lambda{@db.create_table(:cats){Integer :id; index :blah; index :id}}.should raise_error(Sequel::DatabaseError)
448
448
  @db.sqls.should == ['CREATE TABLE cats (id integer)']
449
- lambda{@db.create_table(:cats, :ignore_index_errors=>true){Integer :id; index :blah; index :id}}.should_not raise_error(Sequel::DatabaseError)
449
+ lambda{@db.create_table(:cats, :ignore_index_errors=>true){Integer :id; index :blah; index :id}}.should_not raise_error
450
450
  @db.sqls.should == ['CREATE TABLE cats (id integer)', 'CREATE INDEX cats_id_index ON cats (id)']
451
451
  end
452
452
 
@@ -551,6 +551,14 @@ describe "DB#create_table" do
551
551
  @db.sqls.should == ["CREATE TABLE cats (score integer, CONSTRAINT valid_score CHECK (score <= 100))"]
552
552
  end
553
553
 
554
+ specify "should accept named constraint definitions with options" do
555
+ @db.create_table(:cats) do
556
+ integer :score
557
+ constraint({:name=>:valid_score, :deferrable=>true}, 'score <= 100')
558
+ end
559
+ @db.sqls.should == ["CREATE TABLE cats (score integer, CONSTRAINT valid_score CHECK (score <= 100) DEFERRABLE INITIALLY DEFERRED)"]
560
+ end
561
+
554
562
  specify "should accept named constraint definitions with block" do
555
563
  @db.create_table(:cats) do
556
564
  constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
@@ -892,6 +900,13 @@ describe "DB#alter_table" do
892
900
  @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT valid_score CHECK (score <= 100)"]
893
901
  end
894
902
 
903
+ specify "should support add_constraint with options" do
904
+ @db.alter_table(:cats) do
905
+ add_constraint({:name=>:valid_score, :deferrable=>true}, 'score <= 100')
906
+ end
907
+ @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT valid_score CHECK (score <= 100) DEFERRABLE INITIALLY DEFERRED"]
908
+ end
909
+
895
910
  specify "should support add_constraint with block" do
896
911
  @db.alter_table(:cats) do
897
912
  add_constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
@@ -950,7 +965,7 @@ describe "DB#alter_table" do
950
965
  specify "should ignore errors if the database raises an error on an add_index call and the :ignore_errors option is used" do
951
966
  meta_def(@db, :execute_ddl){|*a| raise Sequel::DatabaseError}
952
967
  lambda{@db.add_index(:cats, :id)}.should raise_error(Sequel::DatabaseError)
953
- lambda{@db.add_index(:cats, :id, :ignore_errors=>true)}.should_not raise_error(Sequel::DatabaseError)
968
+ lambda{@db.add_index(:cats, :id, :ignore_errors=>true)}.should_not raise_error
954
969
  @db.sqls.should == []
955
970
  end
956
971
 
@@ -125,12 +125,12 @@ describe Sequel::Model, "caching" do
125
125
  m = @c.new
126
126
  proc {m.cache_key}.should raise_error(Sequel::Error)
127
127
  m.values[:id] = 1
128
- proc {m.cache_key}.should_not raise_error(Sequel::Error)
128
+ proc {m.cache_key}.should_not raise_error
129
129
 
130
130
  m = @c2.new
131
131
  proc {m.cache_key}.should raise_error(Sequel::Error)
132
132
  m.values[:id] = 1
133
- proc {m.cache_key}.should_not raise_error(Sequel::Error)
133
+ proc {m.cache_key}.should_not raise_error
134
134
  end
135
135
 
136
136
  it "should not raise error if trying to save a new record" do
@@ -184,7 +184,6 @@ describe "Model#before_create && Model#after_create" do
184
184
 
185
185
  specify ".create should cancel the save and raise an error if before_create returns false and raise_on_save_failure is true" do
186
186
  @c.before_create{false}
187
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
188
187
  proc{@c.create(:x => 2)}.should raise_error(Sequel::BeforeHookFailed)
189
188
  DB.sqls.should == []
190
189
  end
@@ -215,7 +214,6 @@ describe "Model#before_update && Model#after_update" do
215
214
 
216
215
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_save_failure is true" do
217
216
  @c.before_update{false}
218
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
219
217
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
220
218
  DB.sqls.should == []
221
219
  end
@@ -254,7 +252,6 @@ describe "Model#before_save && Model#after_save" do
254
252
 
255
253
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_save_failure is true" do
256
254
  @c.before_save{false}
257
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
258
255
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
259
256
  DB.sqls.should == []
260
257
  end
@@ -339,7 +336,6 @@ describe "Model#before_validation && Model#after_validation" do
339
336
 
340
337
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_save_failure is true" do
341
338
  @c.before_validation{false}
342
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
343
339
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
344
340
  DB.sqls.should == []
345
341
  end
@@ -28,6 +28,28 @@ describe "instance_filters plugin" do
28
28
  DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
29
29
  end
30
30
 
31
+ specify "should work when using the prepared_statements plugin" do
32
+ @c.plugin :prepared_statements
33
+
34
+ @p.update(:name=>'Bob')
35
+ DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
36
+ @p.instance_filter(:name=>'Jim')
37
+ @p.this.numrows = 0
38
+ proc{@p.update(:name=>'Joe')}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
39
+ DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"]
40
+
41
+ @p = @c.load(:id=>1, :name=>'John', :num=>1)
42
+ @p.this.numrows = 1
43
+ @p.destroy
44
+ DB.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
45
+ @p.instance_filter(:name=>'Jim')
46
+ @p.this.numrows = 0
47
+ proc{@p.destroy}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
48
+ DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
49
+
50
+ @c.create.should be_a_kind_of(@c)
51
+ end
52
+
31
53
  specify "should apply all instance filters" do
32
54
  @p.instance_filter(:name=>'Jim')
33
55
  @p.instance_filter{num > 2}
@@ -194,19 +194,19 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
194
194
 
195
195
  specify "should raise in the down direction if migration uses unsupported method" do
196
196
  m = Sequel.migration{change{run 'SQL'}}
197
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
197
+ proc{m.apply(@db, :up)}.should_not raise_error
198
198
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
199
199
  end
200
200
 
201
201
  specify "should raise in the down direction if migration uses add_primary_key with an array" do
202
202
  m = Sequel.migration{change{alter_table(:a){add_primary_key [:b]}}}
203
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
203
+ proc{m.apply(@db, :up)}.should_not raise_error
204
204
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
205
205
  end
206
206
 
207
207
  specify "should raise in the down direction if migration uses add_foreign_key with an array" do
208
208
  m = Sequel.migration{change{alter_table(:a){add_foreign_key [:b]}}}
209
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
209
+ proc{m.apply(@db, :up)}.should_not raise_error
210
210
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
211
211
  end
212
212
  end
@@ -263,7 +263,7 @@ describe "Sequel::IntegerMigrator" do
263
263
  end
264
264
 
265
265
  specify "should not raise and error if there is a missing integer migration version and allow_missing_migration_files is true" do
266
- Sequel::Migrator.run(@db, "spec/files/missing_integer_migrations", :allow_missing_migration_files => true).should_not raise_error(Sequel::Migrator::Error)
266
+ proc{Sequel::Migrator.run(@db, "spec/files/missing_integer_migrations", :allow_missing_migration_files => true)}.should_not raise_error
267
267
  end
268
268
 
269
269
  specify "should raise and error if there is a duplicate integer migration version" do
@@ -622,7 +622,7 @@ describe "Sequel::TimestampMigrator" do
622
622
  @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
623
623
 
624
624
  @dir = 'spec/files/missing_timestamped_migrations'
625
- proc{@m.run(@db, @dir, :allow_missing_migration_files => true)}.should_not raise_error(Sequel::Migrator::Error)
625
+ proc{@m.run(@db, @dir, :allow_missing_migration_files => true)}.should_not raise_error
626
626
  [:schema_migrations, :sm1111, :sm2222, :sm3333].each{|n| @db.table_exists?(n).should be_true}
627
627
  @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
628
628
  end
@@ -340,7 +340,7 @@ describe "NestedAttributes plugin" do
340
340
  @Album.nested_attributes :artist
341
341
  proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should raise_error(Sequel::Error)
342
342
  @Album.nested_attributes :artist, :destroy=>true
343
- proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error(Sequel::Error)
343
+ proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error
344
344
  end
345
345
 
346
346
  it "should only allow removing associated objects if :remove option is used in the nested_attributes call" do
@@ -350,7 +350,7 @@ describe "NestedAttributes plugin" do
350
350
  @Album.nested_attributes :artist
351
351
  proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should raise_error(Sequel::Error)
352
352
  @Album.nested_attributes :artist, :remove=>true
353
- proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error(Sequel::Error)
353
+ proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error
354
354
  end
355
355
 
356
356
  it "should raise an Error if a primary key is given in a nested attribute hash, but no matching associated object exists" do
@@ -358,7 +358,7 @@ describe "NestedAttributes plugin" do
358
358
  ar = @Artist.load(:id=>20, :name=>'Ar')
359
359
  ar.associations[:albums] = [al]
360
360
  proc{ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])}.should raise_error(Sequel::Error)
361
- proc{ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
361
+ proc{ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])}.should_not raise_error
362
362
  end
363
363
 
364
364
  it "should not raise an Error if an unmatched primary key is given, if the :strict=>false option is used" do
@@ -388,7 +388,7 @@ describe "NestedAttributes plugin" do
388
388
  co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
389
389
  ar.associations[:concerts] = [co]
390
390
  proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-04', :_delete=>'t'}])}.should raise_error(Sequel::Error)
391
- proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
391
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error
392
392
  end
393
393
 
394
394
  it "should not raise an Error if an unmatched composite primary key is given, if the :strict=>false option is used" do
@@ -16,37 +16,48 @@ describe "prepared_statements plugin" do
16
16
  @db.sqls.should == ["SELECT * FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
17
17
  end
18
18
 
19
- specify "should correctly delete instance" do
20
- @p.destroy.should == @p
21
- @db.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
22
- end
23
-
24
- specify "should correctly update instance" do
25
- @p.update(:name=>'bar').should == @c.load(:id=>1, :name=>'bar', :i => 2)
26
- @db.sqls.should == ["UPDATE people SET name = 'bar' WHERE (id = 1)"]
27
- end
28
-
29
- specify "should correctly create instance" do
30
- @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
31
- @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo')", "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
32
- end
19
+ shared_examples_for "prepared_statements plugin" do
20
+ specify "should correctly delete instance" do
21
+ @p.destroy.should == @p
22
+ @db.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
23
+ end
33
24
 
34
- specify "should correctly create instance if dataset supports insert_select" do
35
- ds = @c.instance_dataset
36
- def ds.supports_insert_select?
37
- true
25
+ specify "should correctly update instance" do
26
+ @p.update(:name=>'bar').should == @c.load(:id=>1, :name=>'bar', :i => 2)
27
+ @db.sqls.should == ["UPDATE people SET name = 'bar' WHERE (id = 1)"]
38
28
  end
39
- def @ds.supports_insert_select?
40
- true
29
+
30
+ specify "should correctly create instance" do
31
+ @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
32
+ @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo')", "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
41
33
  end
42
- def @ds.insert_select(h)
43
- {:id=>1, :name=>'foo', :i => 2}
34
+
35
+ specify "should correctly create instance if dataset supports insert_select" do
36
+ @c.dataset_module do
37
+ def supports_insert_select?
38
+ true
39
+ end
40
+ def insert_select(h)
41
+ self._fetch = {:id=>1, :name=>'foo', :i => 2}
42
+ returning.server(:default).with_sql(:insert_sql, h).first
43
+ end
44
+ def insert_sql(*)
45
+ "#{super}#{' RETURNING *' if opts.has_key?(:returning)}"
46
+ end
47
+ end
48
+ @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
49
+ @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo') RETURNING *"]
44
50
  end
45
- def @ds.insert_sql(*)
46
- "#{super}#{' RETURNING *' if opts.has_key?(:returning)}"
51
+ end
52
+
53
+ it_should_behave_like "prepared_statements plugin"
54
+
55
+ describe "when #use_prepared_statements_for? returns false" do
56
+ before do
57
+ @c.class_eval{def use_prepared_statements_for?(type) false end}
47
58
  end
48
- @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
49
- @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo') RETURNING *"]
59
+
60
+ it_should_behave_like "prepared_statements plugin"
50
61
  end
51
62
 
52
63
  specify "should work correctly when subclassing" do
@@ -68,6 +68,19 @@ describe "Sequel::Plugins::UpdatePrimaryKey" do
68
68
  DB.sqls.should == ["SELECT * FROM a LIMIT 1", "UPDATE a SET a = 2 WHERE (a = 1)", "UPDATE a SET b = 4 WHERE (a = 2)", "UPDATE a SET b = 5 WHERE (a = 2)", "SELECT * FROM a"]
69
69
  end
70
70
 
71
+ specify "should work correctly when using the prepared_statements plugin" do
72
+ @c.plugin :prepared_statements
73
+ @ds._fetch = [[{:a=>1, :b=>3}], [{:a=>2, :b=>4}]]
74
+ o = @c.first
75
+ o.update(:a=>2, :b=>4)
76
+ @c.all.should == [@c.load(:a=>2, :b=>4)]
77
+ sqls = DB.sqls
78
+ ["UPDATE a SET a = 2, b = 4 WHERE (a = 1)", "UPDATE a SET b = 4, a = 2 WHERE (a = 1)"].should include(sqls.slice!(1))
79
+ sqls.should == ["SELECT * FROM a LIMIT 1", "SELECT * FROM a"]
80
+
81
+ o.delete
82
+ end
83
+
71
84
  specify "should clear the associations cache of non-many_to_one associations when changing the primary key" do
72
85
  @c.one_to_many :cs, :class=>@c
73
86
  @c.many_to_one :c, :class=>@c
@@ -1671,3 +1671,39 @@ describe "Emulated functions" do
1671
1671
  @ds.get(Sequel.trim(:a)).should == 'foo22'
1672
1672
  end
1673
1673
  end
1674
+
1675
+ describe "Dataset replace" do
1676
+ before do
1677
+ DB.create_table(:items){Integer :id, :unique=>true; Integer :value}
1678
+ sqls = []
1679
+ DB.loggers << Class.new{%w'info error'.each{|m| define_method(m){|sql| sqls << sql}}}.new
1680
+
1681
+ @d = DB[:items]
1682
+ sqls.clear
1683
+ end
1684
+
1685
+ after do
1686
+ DB.drop_table?(:items)
1687
+ end
1688
+
1689
+ specify "should use support arrays, datasets, and multiple values" do
1690
+ @d.replace([1, 2])
1691
+ @d.all.should == [{:id=>1, :value=>2}]
1692
+ @d.replace(1, 2)
1693
+ @d.all.should == [{:id=>1, :value=>2}]
1694
+ @d.replace(@d)
1695
+ @d.all.should == [{:id=>1, :value=>2}]
1696
+ end
1697
+
1698
+ specify "should create a record if the condition is not met" do
1699
+ @d.replace(:id => 111, :value => 333)
1700
+ @d.all.should == [{:id => 111, :value => 333}]
1701
+ end
1702
+
1703
+ specify "should update a record if the condition is met" do
1704
+ @d << {:id => 111}
1705
+ @d.all.should == [{:id => 111, :value => nil}]
1706
+ @d.replace(:id => 111, :value => 333)
1707
+ @d.all.should == [{:id => 111, :value => 333}]
1708
+ end
1709
+ end if DB.dataset.supports_replace?
@@ -134,8 +134,8 @@ describe Sequel::Model, "associate" do
134
134
  it "should allow cloning of one_to_many to one_to_one associations and vice-versa" do
135
135
  c = Class.new(Sequel::Model(:c))
136
136
  c.one_to_one :c
137
- proc{c.one_to_many :cs, :clone=>:c}.should_not raise_error(Sequel::Error)
138
- proc{c.one_to_one :c2, :clone=>:cs}.should_not raise_error(Sequel::Error)
137
+ proc{c.one_to_many :cs, :clone=>:c}.should_not raise_error
138
+ proc{c.one_to_one :c2, :clone=>:cs}.should_not raise_error
139
139
  end
140
140
 
141
141
  it "should clear associations cache when refreshing object manually" do
@@ -1239,6 +1239,14 @@ describe Sequel::Model, "one_to_many" do
1239
1239
  DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1240
1240
  end
1241
1241
 
1242
+ it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
1243
+ @c2.one_to_many :attributes, :class => @c1
1244
+ n = @c2.new(:id => 1234)
1245
+ @c1.dataset._fetch = []
1246
+ proc{n.add_attribute(234)}.should raise_error(Sequel::NoMatchingRow)
1247
+ DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234"]
1248
+ end
1249
+
1242
1250
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
1243
1251
  @c2.one_to_many :attributes, :class => @c1
1244
1252
  proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.should raise_error(Sequel::Error)
@@ -2013,6 +2021,16 @@ describe Sequel::Model, "many_to_many" do
2013
2021
  sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2014
2022
  end
2015
2023
 
2024
+ it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
2025
+ @c2.many_to_many :attributes, :class => @c1
2026
+
2027
+ n = @c2.load(:id => 1234)
2028
+ a = @c1.load(:id => 2345)
2029
+ @c1.dataset._fetch = []
2030
+ proc{n.add_attribute(2345)}.should raise_error(Sequel::NoMatchingRow)
2031
+ DB.sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2032
+ end
2033
+
2016
2034
  it "should allow passing a hash to the add_ method which creates a new record" do
2017
2035
  @c2.many_to_many :attributes, :class => @c1
2018
2036
 
@@ -24,7 +24,7 @@ describe "Model#before_create && Model#after_create" do
24
24
  @c.send(:define_method, :before_create){false}
25
25
  proc{@c.create(:x => 2)}.should raise_error(Sequel::BeforeHookFailed)
26
26
  DB.sqls.should == []
27
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
27
+ proc{@c.load(:id => 2233).save}.should_not raise_error
28
28
  end
29
29
 
30
30
  specify ".create should cancel the save and return nil if before_create returns false and raise_on_save_failure is false" do
@@ -55,7 +55,6 @@ describe "Model#before_update && Model#after_update" do
55
55
 
56
56
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_save_failure is true" do
57
57
  @c.send(:define_method, :before_update){false}
58
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
59
58
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
60
59
  DB.sqls.should == []
61
60
  end
@@ -63,7 +62,6 @@ describe "Model#before_update && Model#after_update" do
63
62
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_failure option is true" do
64
63
  @c.send(:define_method, :before_update){false}
65
64
  @c.raise_on_save_failure = false
66
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
67
65
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
68
66
  DB.sqls.should == []
69
67
  end
@@ -104,7 +102,6 @@ describe "Model#before_save && Model#after_save" do
104
102
 
105
103
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_save_failure is true" do
106
104
  @c.send(:define_method, :before_save){false}
107
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
108
105
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
109
106
  DB.sqls.should == []
110
107
  end
@@ -112,7 +109,6 @@ describe "Model#before_save && Model#after_save" do
112
109
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_failure option is true" do
113
110
  @c.send(:define_method, :before_save){false}
114
111
  @c.raise_on_save_failure = false
115
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
116
112
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
117
113
  DB.sqls.should == []
118
114
  end
@@ -208,7 +204,6 @@ describe "Model#before_validation && Model#after_validation" do
208
204
 
209
205
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_save_failure is true" do
210
206
  @c.send(:define_method, :before_validation){false}
211
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
212
207
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
213
208
  DB.sqls.should == []
214
209
  end
@@ -216,7 +211,6 @@ describe "Model#before_validation && Model#after_validation" do
216
211
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_failure option is true" do
217
212
  @c.send(:define_method, :before_validation){false}
218
213
  @c.raise_on_save_failure = false
219
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
220
214
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
221
215
  DB.sqls.should == []
222
216
  end