sequel 5.19.0 → 5.20.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/doc/release_notes/5.20.0.txt +89 -0
  4. data/doc/transactions.rdoc +38 -0
  5. data/lib/sequel/adapters/mysql2.rb +2 -2
  6. data/lib/sequel/adapters/shared/postgres.rb +5 -7
  7. data/lib/sequel/database/query.rb +1 -1
  8. data/lib/sequel/database/schema_generator.rb +1 -1
  9. data/lib/sequel/database/transactions.rb +57 -5
  10. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  11. data/lib/sequel/dataset/query.rb +1 -1
  12. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  13. data/lib/sequel/model/associations.rb +35 -9
  14. data/lib/sequel/model/plugins.rb +104 -0
  15. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  16. data/lib/sequel/plugins/association_pks.rb +14 -4
  17. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  18. data/lib/sequel/plugins/composition.rb +13 -9
  19. data/lib/sequel/plugins/finder.rb +2 -2
  20. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  21. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  22. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
  23. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  24. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  25. data/lib/sequel/version.rb +1 -1
  26. data/spec/adapters/postgres_spec.rb +32 -0
  27. data/spec/core/database_spec.rb +73 -2
  28. data/spec/core/schema_spec.rb +6 -0
  29. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  30. data/spec/extensions/core_refinements_spec.rb +1 -1
  31. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  32. data/spec/extensions/migration_spec.rb +13 -0
  33. data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
  34. data/spec/extensions/s_spec.rb +1 -1
  35. data/spec/extensions/schema_dumper_spec.rb +4 -2
  36. data/spec/integration/plugin_test.rb +15 -0
  37. data/spec/integration/transaction_test.rb +50 -0
  38. data/spec/model/associations_spec.rb +84 -4
  39. data/spec/model/plugins_spec.rb +111 -0
  40. metadata +4 -2
@@ -26,8 +26,8 @@ module Sequel
26
26
  # Album.where(Album.published_conditions | {ready: true}).sql
27
27
  # # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
28
28
  module SubsetConditions
29
- def self.apply(mod, &block)
30
- mod.instance_exec do
29
+ def self.apply(model, &block)
30
+ model.instance_exec do
31
31
  @dataset_module_class = Class.new(@dataset_module_class) do
32
32
  include DatasetModuleMethods
33
33
  end
@@ -188,13 +188,17 @@ module Sequel
188
188
  # Sequel will attempt to insert a NULL value into the database, instead of using the
189
189
  # database's default.
190
190
  # :allow_nil :: Whether to skip the validation if the value is nil.
191
- # :if :: A symbol (indicating an instance_method) or proc (which is instance_execed)
191
+ # :if :: A symbol (indicating an instance_method) or proc (which is used to define an instance method)
192
192
  # skipping this validation if it returns nil or false.
193
193
  # :tag :: The tag to use for this validation.
194
194
  def validates_each(*atts, &block)
195
195
  opts = extract_options!(atts)
196
196
  blank_meth = db.method(:blank_object?).to_proc
197
197
  blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
198
+ if i.is_a?(Proc)
199
+ i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i)
200
+ end
201
+
198
202
  proc do |o,a,v|
199
203
  next if i && !validation_if_proc(o, i)
200
204
  next if an && Array(v).all?(&:nil?)
@@ -434,8 +438,6 @@ module Sequel
434
438
  case i
435
439
  when Symbol
436
440
  o.get_column_value(i)
437
- when Proc
438
- o.instance_exec(&i)
439
441
  else
440
442
  raise(::Sequel::Error, "invalid value for :if validation option")
441
443
  end
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 19
9
+ MINOR = 20
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
@@ -104,6 +104,24 @@ describe "PostgreSQL", '#create_table' do
104
104
  @db.check_constraints(:tmp_dolls).must_equal(:ic=>{:definition=>"CHECK ((i > 2))", :columns=>[:i]}, :jc=>{:definition=>"CHECK ((j > 2))", :columns=>[:j]}, :ijc=>{:definition=>"CHECK (((i - j) > 2))", :columns=>[:i, :j]})
105
105
  end
106
106
 
107
+ it "should have #check_constraints return check constraints where columns are unknown" do
108
+ begin
109
+ @db.create_table(:tmp_dolls) do
110
+ Integer :i
111
+ Integer :j
112
+ end
113
+ @db.run "CREATE OR REPLACE FUNCTION valid_tmp_dolls(t1 tmp_dolls) RETURNS boolean AS 'SELECT false' LANGUAGE SQL;"
114
+ @db.alter_table(:tmp_dolls) do
115
+ add_constraint(:valid_tmp_dolls, Sequel.function(:valid_tmp_dolls, :tmp_dolls))
116
+ end
117
+
118
+ @db.check_constraints(:tmp_dolls).must_equal(:valid_tmp_dolls=>{:definition=>"CHECK (valid_tmp_dolls(tmp_dolls.*))", :columns=>[]})
119
+ ensure
120
+ @db.run "ALTER TABLE tmp_dolls DROP CONSTRAINT IF EXISTS valid_tmp_dolls"
121
+ @db.run "DROP FUNCTION IF EXISTS valid_tmp_dolls(tmp_dolls)"
122
+ end
123
+ end if DB.server_version >= 90000
124
+
107
125
  it "should not allow to pass both :temp and :unlogged" do
108
126
  proc do
109
127
  @db.create_table(:temp_unlogged_dolls, :temp => true, :unlogged => true){text :name}
@@ -3988,6 +4006,10 @@ describe "pg_auto_constraint_validations plugin" do
3988
4006
  constraint :valid_i, Sequel[:i] < 10
3989
4007
  constraint(:valid_i_id, Sequel[:i] + Sequel[:id] < 20)
3990
4008
  end
4009
+ @db.run "CREATE OR REPLACE FUNCTION valid_test1(t1 test1) RETURNS boolean AS 'SELECT t1.i != -100' LANGUAGE SQL;"
4010
+ @db.alter_table(:test1) do
4011
+ add_constraint(:valid_test1, Sequel.function(:valid_test1, :test1))
4012
+ end
3991
4013
  @db.create_table!(:test2) do
3992
4014
  Integer :test2_id, :primary_key=>true
3993
4015
  foreign_key :test1_id, :test1
@@ -4008,6 +4030,8 @@ describe "pg_auto_constraint_validations plugin" do
4008
4030
  @c2.insert(:test2_id=>3, :test1_id=>1)
4009
4031
  end
4010
4032
  after(:all) do
4033
+ @db.run "ALTER TABLE test1 DROP CONSTRAINT IF EXISTS valid_test1"
4034
+ @db.run "DROP FUNCTION IF EXISTS valid_test1(test1)"
4011
4035
  @db.drop_table?(:test2, :test1)
4012
4036
  end
4013
4037
 
@@ -4017,6 +4041,14 @@ describe "pg_auto_constraint_validations plugin" do
4017
4041
  o.errors.must_equal(:i=>['is invalid'])
4018
4042
  end
4019
4043
 
4044
+ it "should handle check constraint failures where the columns are unknown, if columns are explicitly specified" do
4045
+ o = @c1.new(:id=>5, :i=>-100)
4046
+ proc{o.save}.must_raise Sequel::CheckConstraintViolation
4047
+ @c1.pg_auto_constraint_validation_override(:valid_test1, :i, "should not be -100")
4048
+ proc{o.save}.must_raise Sequel::ValidationFailed
4049
+ o.errors.must_equal(:i=>['should not be -100'])
4050
+ end
4051
+
4020
4052
  it "should handle check constraint failures as validation errors when updating" do
4021
4053
  o = @c1.new(:id=>5, :i=>3)
4022
4054
  o.save
@@ -1140,15 +1140,86 @@ describe "Database#transaction with savepoint support" do
1140
1140
  end
1141
1141
 
1142
1142
  it "should support after_rollback inside savepoints" do
1143
- @db.transaction do
1143
+ @db.transaction(:rollback=>:always) do
1144
1144
  @db.after_rollback{@db.execute('foo')}
1145
1145
  @db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
1146
1146
  @db.after_rollback{@db.execute('baz')}
1147
- raise Sequel::Rollback
1148
1147
  end
1149
1148
  @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
1150
1149
  end
1151
1150
 
1151
+ it "should run after_commit if savepoint rolled back" do
1152
+ @db.transaction do
1153
+ @db.after_commit{@db.execute('foo')}
1154
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit{@db.execute('bar')}}
1155
+ end
1156
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar']
1157
+ end
1158
+
1159
+ it "should not run after_commit if savepoint rolled back and :savepoint option used" do
1160
+ @db.transaction do
1161
+ @db.after_commit{@db.execute('foo')}
1162
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit(:savepoint=>true){@db.execute('bar')}}
1163
+ end
1164
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo']
1165
+ end
1166
+
1167
+ it "should not run after_commit if higher-level savepoint rolled back and :savepoint option used" do
1168
+ @db.transaction do
1169
+ @db.after_commit{@db.execute('foo')}
1170
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
1171
+ end
1172
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "COMMIT", "foo"]
1173
+ end
1174
+
1175
+ it "should not run after_commit if transaction rolled back and :savepoint option used" do
1176
+ @db.transaction(:rollback=>:always) do
1177
+ @db.after_commit{@db.execute('foo')}
1178
+ @db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
1179
+ end
1180
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK"]
1181
+ end
1182
+
1183
+ it "should run after_rollback if savepoint rolls back" do
1184
+ @db.transaction(:rollback=>:always) do
1185
+ @db.after_rollback{@db.execute('foo')}
1186
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback{@db.execute('bar')}}
1187
+ @db.after_rollback{@db.execute('baz')}
1188
+ end
1189
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
1190
+ end
1191
+
1192
+ it "should run after_rollback when savepoint rolls back if :savepoint option used" do
1193
+ @db.transaction(:rollback=>:always) do
1194
+ @db.after_rollback{@db.execute('foo')}
1195
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
1196
+ @db.after_rollback{@db.execute('baz')}
1197
+ end
1198
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'ROLLBACK', 'foo', 'baz']
1199
+ end
1200
+
1201
+ it "should run after_rollback if savepoint rolled back and :savepoint option used, even if transaction commits" do
1202
+ @db.transaction do
1203
+ @db.after_commit{@db.execute('foo')}
1204
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
1205
+ end
1206
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'COMMIT', 'foo']
1207
+ end
1208
+
1209
+ it "should run after_rollback if higher-level savepoint rolled back and :savepoint option used" do
1210
+ @db.transaction do
1211
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
1212
+ end
1213
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "bar", "COMMIT"]
1214
+ end
1215
+
1216
+ it "should run after_rollback if transaction rolled back and :savepoint option used" do
1217
+ @db.transaction(:rollback=>:always) do
1218
+ @db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
1219
+ end
1220
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK", "bar"]
1221
+ end
1222
+
1152
1223
  it "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
1153
1224
  @db.define_singleton_method(:supports_prepared_transactions?){true}
1154
1225
  proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.must_raise(Sequel::Error)
@@ -1777,17 +1777,21 @@ describe "Schema Parser" do
1777
1777
  @db.schema(:text).first.last[:type].must_equal :string
1778
1778
  @db.schema(:date).first.last[:type].must_equal :date
1779
1779
  @db.schema(:datetime).first.last[:type].must_equal :datetime
1780
+ @db.schema(:smalldatetime).first.last[:type].must_equal :datetime
1780
1781
  @db.schema(:timestamp).first.last[:type].must_equal :datetime
1781
1782
  @db.schema(:"timestamp with time zone").first.last[:type].must_equal :datetime
1782
1783
  @db.schema(:"timestamp without time zone").first.last[:type].must_equal :datetime
1783
1784
  @db.schema(:time).first.last[:type].must_equal :time
1784
1785
  @db.schema(:"time with time zone").first.last[:type].must_equal :time
1785
1786
  @db.schema(:"time without time zone").first.last[:type].must_equal :time
1787
+ @db.schema(:bool).first.last[:type].must_equal :boolean
1786
1788
  @db.schema(:boolean).first.last[:type].must_equal :boolean
1787
1789
  @db.schema(:real).first.last[:type].must_equal :float
1788
1790
  @db.schema(:float).first.last[:type].must_equal :float
1791
+ @db.schema(:"float unsigned").first.last[:type].must_equal :float
1789
1792
  @db.schema(:double).first.last[:type].must_equal :float
1790
1793
  @db.schema(:"double(1,2)").first.last[:type].must_equal :float
1794
+ @db.schema(:"double(1,2) unsigned").first.last[:type].must_equal :float
1791
1795
  @db.schema(:"double precision").first.last[:type].must_equal :float
1792
1796
  @db.schema(:number).first.last[:type].must_equal :decimal
1793
1797
  @db.schema(:numeric).first.last[:type].must_equal :decimal
@@ -1801,6 +1805,8 @@ describe "Schema Parser" do
1801
1805
  @db.schema(:nchar).first.last[:type].must_equal :string
1802
1806
  @db.schema(:nvarchar).first.last[:type].must_equal :string
1803
1807
  @db.schema(:ntext).first.last[:type].must_equal :string
1808
+ @db.schema(:clob).first.last[:type].must_equal :string
1809
+ @db.schema(:ntext).first.last[:type].must_equal :string
1804
1810
  @db.schema(:smalldatetime).first.last[:type].must_equal :datetime
1805
1811
  @db.schema(:binary).first.last[:type].must_equal :blob
1806
1812
  @db.schema(:varbinary).first.last[:type].must_equal :blob
@@ -5,8 +5,8 @@ describe "class_table_inheritance plugin" do
5
5
  @db = Sequel.mock(:numrows=>1, :autoid=>proc{|sql| 1})
6
6
  def @db.supports_schema_parsing?() true end
7
7
  def @db.schema(table, opts={})
8
- {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
9
- :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}] ],
8
+ {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string, :allow_null=>false}], [:kind, {:type=>:string}]],
9
+ :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer, :allow_null=>false}] ],
10
10
  :executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
11
11
  :staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
12
12
  }[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
@@ -23,7 +23,9 @@ describe "class_table_inheritance plugin" do
23
23
  }[opts[:from] + (opts[:join] || []).map{|x| x.table}]
24
24
  end
25
25
  end
26
- class ::Employee < Sequel::Model(@db)
26
+ base = Sequel::Model(@db)
27
+ base.plugin :auto_validations if @use_auto_validations
28
+ class ::Employee < base
27
29
  def _save_refresh; @values[:id] = 1 end
28
30
  def self.columns
29
31
  dataset.columns || dataset.opts[:from].first.expression.columns
@@ -114,6 +116,26 @@ describe "class_table_inheritance plugin" do
114
116
  "SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN ('Ceo'))) AS employees WHERE (id = 1) LIMIT 1"]
115
117
  end
116
118
 
119
+ describe "with auto_validations plugin" do
120
+ before(:all) do
121
+ @use_auto_validations = true
122
+ end
123
+
124
+ it "should work" do
125
+ e = Employee.new
126
+ e.valid?.must_equal false
127
+ e.errors.must_equal(:name=>["is not present"])
128
+
129
+ e = Manager.new
130
+ e.valid?.must_equal false
131
+ e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
132
+
133
+ e = Executive.new
134
+ e.valid?.must_equal false
135
+ e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
136
+ end
137
+ end
138
+
117
139
  it "should return rows with the current class if sti_key is nil" do
118
140
  Employee.plugin :class_table_inheritance
119
141
  Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}, {:kind=>'Intern'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee, Employee]
@@ -232,11 +254,11 @@ describe "class_table_inheritance plugin" do
232
254
  end
233
255
 
234
256
  it "should include schema for columns for tables for ancestor classes" do
235
- Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
236
- Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
237
- Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
238
- Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
239
- Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
257
+ Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
258
+ Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false})
259
+ Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false}, :num_managers=>{:type=>:integer})
260
+ Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
261
+ Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
240
262
  end
241
263
 
242
264
  it "should use the correct primary key (which should have the same name in all subclasses)" do
@@ -1,6 +1,6 @@
1
1
  require_relative "spec_helper"
2
2
 
3
- if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') # || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby')
3
+ if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_ENGINE == 'jruby' && (JRUBY_VERSION >= '9.3' || (JRUBY_VERSION.match(/\A9\.2\.(\d+)/) && $1.to_i >= 7)))
4
4
  Sequel.extension :core_refinements, :pg_array, :pg_hstore, :pg_row, :pg_range, :pg_row_ops, :pg_range_ops, :pg_array_ops, :pg_hstore_ops, :pg_json, :pg_json_ops
5
5
  using Sequel::CoreRefinements
6
6
 
@@ -22,6 +22,28 @@ describe Sequel::Model, "hook_class_methods plugin" do
22
22
  hooks.values.all?(&:frozen?).must_equal true
23
23
  end
24
24
 
25
+ deprecated ".hook_blocks method should yield each hook block" do
26
+ c = model_class.call Sequel::Model
27
+ a = []
28
+ c.hook_blocks(:before_save){|b| a << b}
29
+ a.must_equal []
30
+
31
+ pr = proc{adds << 'hi'}
32
+ c.before_save(&pr)
33
+ a = []
34
+ c.hook_blocks(:before_save){|b| a << b}
35
+ a.must_equal [pr]
36
+
37
+ c.before_save(&pr)
38
+ a = []
39
+ c.hook_blocks(:before_save){|b| a << b}
40
+ a.must_equal [pr, pr]
41
+
42
+ a = []
43
+ c.hook_blocks(:after_save){|b| a << b}
44
+ a.must_equal []
45
+ end
46
+
25
47
  it "should be definable using a block" do
26
48
  adds = []
27
49
  c = model_class.call Sequel::Model do
@@ -195,6 +195,19 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
195
195
  [:drop_table, :a, {:foo=>:bar}]]
196
196
  end
197
197
 
198
+ it "should reverse add_foreign_key with :type option" do
199
+ Sequel.migration{change{alter_table(:t){add_foreign_key :b, :c, :type=>:f}}}.apply(@db, :down)
200
+ actions = @db.actions
201
+ actions.must_equal [[:alter_table, [[:drop_foreign_key, :b, {:type=>:f}]]]]
202
+ @db.sqls
203
+ db = Sequel.mock
204
+ args = nil
205
+ db.define_singleton_method(:foreign_key_list){|*a| args = a; [{:name=>:fbc, :columns=>[:b]}]}
206
+ db.alter_table(:t){send(*actions[0][1][0])}
207
+ db.sqls.must_equal ["ALTER TABLE t DROP CONSTRAINT fbc", "ALTER TABLE t DROP COLUMN b"]
208
+ args.must_equal [:t]
209
+ end
210
+
198
211
  it "should reverse add_foreign_key with :foreign_key_constraint_name option" do
199
212
  Sequel.migration{change{alter_table(:t){add_foreign_key :b, :c, :foreign_key_constraint_name=>:f}}}.apply(@db, :down)
200
213
  actions = @db.actions
@@ -161,4 +161,12 @@ describe "pg_auto_constraint_validations plugin" do
161
161
  proc{o.save}.must_raise Sequel::ValidationFailed
162
162
  o.errors.must_equal(:i=>['is invalid'], :id=>['is invalid'])
163
163
  end
164
+
165
+ it "should handle overridden constraint failures as validation errors when updating" do
166
+ o = @c.load(:i=>3)
167
+ @c.pg_auto_constraint_validation_override(:items_i_ocheck, :i, "foo bar")
168
+ @set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_ocheck']
169
+ proc{o.update(:i=>12)}.must_raise Sequel::ValidationFailed
170
+ o.errors.must_equal(:i=>['foo bar'])
171
+ end
164
172
  end
@@ -29,7 +29,7 @@ describe "s extension as refinement" do
29
29
  end
30
30
 
31
31
 
32
- if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') # || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby')
32
+ if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_ENGINE == 'jruby' && (JRUBY_VERSION >= '9.3' || (JRUBY_VERSION.match(/\A9\.2\.(\d+)/) && $1.to_i >= 7)))
33
33
  using Sequel::S
34
34
 
35
35
  describe "s extension as refinement" do
@@ -800,14 +800,16 @@ END_MIG
800
800
  it "should convert mysql types to ruby types" do
801
801
  def @d.schema(t, *o)
802
802
  i = 0
803
- ['double(15,2)', 'double(7,1) unsigned'].map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
803
+ ['float unsigned', 'double(15,2)', 'double(7,1) unsigned'].map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
804
804
  end
805
805
  @d.dump_table_schema(:x).must_equal((<<END_MIG).chomp)
806
806
  create_table(:x) do
807
807
  Float :c1
808
808
  Float :c2
809
+ Float :c3
809
810
 
810
- check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c2), 0)
811
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c1), 0)
812
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c3), 0)
811
813
  end
812
814
  END_MIG
813
815
  end
@@ -30,6 +30,7 @@ describe "Class Table Inheritance Plugin" do
30
30
  end
31
31
  class ::Manager < Employee
32
32
  one_to_many :staff_members, :class=>:Staff
33
+ one_to_one :first_staff_member, :clone=>:staff_members, :order=>:id
33
34
  end
34
35
  class ::Executive < Manager
35
36
  end
@@ -150,6 +151,8 @@ describe "Class Table Inheritance Plugin" do
150
151
  m = Staff.first.manager
151
152
  m.must_equal Manager[@i4]
152
153
  m.must_be_kind_of(Executive)
154
+ Staff.first.update(:manager => Manager[@i3])
155
+ Staff.first.manager.must_equal Manager[@i3]
153
156
  end
154
157
 
155
158
  it "should handle eagerly loading many_to_one relationships" do
@@ -164,6 +167,18 @@ describe "Class Table Inheritance Plugin" do
164
167
 
165
168
  it "should handle one_to_many relationships" do
166
169
  Executive.first(:name=>'Ex').staff_members.must_equal [Staff[@i2]]
170
+ i6 = @db[:employees].insert(:name=>'S2', :kind=>'Staff')
171
+ @db[:staff].insert(:id=>i6, :manager_id=>@i4)
172
+ Executive.first(:name=>'Ex').add_staff_member(i6)
173
+ Executive.first(:name=>'Ex').staff_members{|ds| ds.order(:id)}.must_equal [Staff[@i2], Staff[i6]]
174
+ end
175
+
176
+ it "should handle one_to_many relationships" do
177
+ Executive.first(:name=>'Ex').first_staff_member.must_equal Staff[@i2]
178
+ i6 = @db[:employees].insert(:name=>'S2', :kind=>'Staff')
179
+ @db[:staff].insert(:id=>i6, :manager_id=>@i4)
180
+ Executive.first(:name=>'Ex').first_staff_member = Staff[i6]
181
+ Executive.first(:name=>'Ex').staff_members.must_equal [Staff[i6]]
167
182
  end
168
183
 
169
184
  it "should handle eagerly loading one_to_many relationships" do
@@ -496,6 +496,56 @@ describe "Database transactions" do
496
496
  @db.transaction{@db.transaction(:savepoint=>true){@db.after_rollback{c = 1}}; c.must_be_nil; raise Sequel::Rollback}
497
497
  c.must_equal 1
498
498
  end
499
+
500
+ it "should support after_commit inside savepoints with :savepoint_option" do
501
+ c = nil
502
+ @db.transaction{@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){c = 1}}; c.must_be_nil}
503
+ c.must_equal 1
504
+
505
+ c = nil
506
+ @db.transaction{@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){c = 1}}}; c.must_be_nil}
507
+ c.must_equal 1
508
+
509
+ c = nil
510
+ @db.transaction{@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit(:savepoint=>true){c = 1}}}
511
+ c.must_be_nil
512
+
513
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){c = 1}}}
514
+ c.must_be_nil
515
+
516
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){c = 1}}}}
517
+ c.must_be_nil
518
+ end
519
+
520
+ it "should support after_rollback inside savepoints with :savepoint_option" do
521
+ c = nil
522
+ @db.transaction{@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){c = 1}; c.must_be_nil}; c.must_equal 1}
523
+ c.must_equal 1
524
+
525
+ c = nil
526
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){c = 1}}; c.must_be_nil}
527
+ c.must_equal 1
528
+
529
+ c = nil
530
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){c = 1}; c.must_be_nil}; c.must_equal 1}
531
+ c.must_equal 1
532
+
533
+ c = nil
534
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){c = 1}}; c.must_be_nil}
535
+ c.must_equal 1
536
+
537
+ c = nil
538
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){c = 1}}; c.must_be_nil}}
539
+ c.must_equal 1
540
+
541
+ c = nil
542
+ @db.transaction(:rollback=>:always){@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){c = 1}; c.must_be_nil}; c.must_equal 1}}
543
+ c.must_equal 1
544
+
545
+ c = nil
546
+ @db.transaction{@db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){c = 1}}; c.must_be_nil}; c.must_equal 1}
547
+ c.must_equal 1
548
+ end
499
549
  end
500
550
 
501
551
  if DB.supports_prepared_transactions?