sequel 5.19.0 → 5.20.0

Sign up to get free protection for your applications and to get access to all the features.
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?