sequel 3.38.0 → 3.39.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +62 -0
- data/README.rdoc +2 -2
- data/bin/sequel +12 -2
- data/doc/advanced_associations.rdoc +1 -1
- data/doc/association_basics.rdoc +13 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +4 -4
- data/lib/sequel/adapters/jdbc/derby.rb +1 -0
- data/lib/sequel/adapters/mock.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +8 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/postgres.rb +72 -8
- data/lib/sequel/adapters/shared/db2.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +57 -0
- data/lib/sequel/adapters/shared/mysql.rb +95 -19
- data/lib/sequel/adapters/shared/oracle.rb +14 -0
- data/lib/sequel/adapters/shared/postgres.rb +63 -24
- data/lib/sequel/adapters/shared/sqlite.rb +6 -9
- data/lib/sequel/connection_pool/sharded_threaded.rb +8 -3
- data/lib/sequel/connection_pool/threaded.rb +9 -4
- data/lib/sequel/database/query.rb +60 -48
- data/lib/sequel/database/schema_generator.rb +13 -6
- data/lib/sequel/database/schema_methods.rb +65 -12
- data/lib/sequel/dataset/actions.rb +22 -4
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/graph.rb +2 -3
- data/lib/sequel/dataset/misc.rb +2 -2
- data/lib/sequel/dataset/query.rb +0 -2
- data/lib/sequel/dataset/sql.rb +33 -12
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/extensions/eval_inspect.rb +17 -2
- data/lib/sequel/extensions/pg_array_ops.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -2
- data/lib/sequel/extensions/pg_row_ops.rb +18 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -11
- data/lib/sequel/model/associations.rb +3 -2
- data/lib/sequel/model/base.rb +57 -13
- data/lib/sequel/model/exceptions.rb +20 -2
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/defaults_setter.rb +15 -1
- data/lib/sequel/plugins/dirty.rb +2 -2
- data/lib/sequel/plugins/identity_map.rb +12 -8
- data/lib/sequel/plugins/subclasses.rb +19 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/validation_helpers.rb +24 -4
- data/lib/sequel/sql.rb +64 -24
- data/lib/sequel/timezones.rb +10 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +26 -25
- data/spec/adapters/mysql_spec.rb +57 -23
- data/spec/adapters/oracle_spec.rb +34 -49
- data/spec/adapters/postgres_spec.rb +226 -128
- data/spec/adapters/sqlite_spec.rb +50 -49
- data/spec/core/connection_pool_spec.rb +22 -0
- data/spec/core/database_spec.rb +53 -47
- data/spec/core/dataset_spec.rb +36 -32
- data/spec/core/expression_filters_spec.rb +14 -2
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +0 -13
- data/spec/core/schema_spec.rb +64 -5
- data/spec/core_extensions_spec.rb +1 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/defaults_setter_spec.rb +24 -0
- data/spec/extensions/eval_inspect_spec.rb +9 -0
- data/spec/extensions/identity_map_spec.rb +11 -2
- data/spec/extensions/pg_array_ops_spec.rb +9 -0
- data/spec/extensions/pg_row_ops_spec.rb +11 -1
- data/spec/extensions/pg_row_plugin_spec.rb +4 -0
- data/spec/extensions/schema_dumper_spec.rb +8 -5
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/validation_helpers_spec.rb +15 -2
- data/spec/integration/dataset_test.rb +75 -1
- data/spec/integration/plugin_test.rb +146 -0
- data/spec/integration/schema_test.rb +34 -0
- data/spec/model/dataset_methods_spec.rb +38 -0
- data/spec/model/hooks_spec.rb +6 -0
- data/spec/model/validations_spec.rb +27 -2
- metadata +8 -2
@@ -60,10 +60,12 @@ describe "Blockless Ruby Filters" do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should support ~ via Hash and Regexp (if supported by database)" do
|
63
|
+
def @d.supports_regexp?; true end
|
63
64
|
@d.l(:x => /blah/).should == '(x ~ \'blah\')'
|
64
65
|
end
|
65
66
|
|
66
67
|
it "should support !~ via inverted Hash and Regexp" do
|
68
|
+
def @d.supports_regexp?; true end
|
67
69
|
@d.l(~Sequel.expr(:x => /blah/)).should == '(x !~ \'blah\')'
|
68
70
|
end
|
69
71
|
|
@@ -141,7 +143,7 @@ describe "Blockless Ruby Filters" do
|
|
141
143
|
@d.l(Sequel.expr(Sequel.lit('y') => Sequel.lit('z')) & Sequel.lit('x')).should == '((y = z) AND x)'
|
142
144
|
@d.l((Sequel.lit('x') > 200) & (Sequel.lit('y') < 200)).should == '((x > 200) AND (y < 200))'
|
143
145
|
@d.l(~(Sequel.lit('x') + 1 > 100)).should == '((x + 1) <= 100)'
|
144
|
-
@d.l(Sequel.lit('x').like(
|
146
|
+
@d.l(Sequel.lit('x').like('a')).should == '(x LIKE \'a\')'
|
145
147
|
@d.l(Sequel.lit('x') + 1 > 100).should == '((x + 1) > 100)'
|
146
148
|
@d.l((Sequel.lit('x') * :y) < 100.01).should == '((x * y) < 100.01)'
|
147
149
|
@d.l((Sequel.lit('x') - Sequel.expr(:y)/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
|
@@ -381,6 +383,14 @@ describe "Blockless Ruby Filters" do
|
|
381
383
|
@d.lit(d.like(:b)).should == '((SELECT a FROM items) LIKE b)'
|
382
384
|
@d.lit(d.ilike(:b)).should == '((SELECT a FROM items) ILIKE b)'
|
383
385
|
end
|
386
|
+
|
387
|
+
it "should handled emulated char_length function" do
|
388
|
+
@d.lit(Sequel.char_length(:a)).should == 'char_length(a)'
|
389
|
+
end
|
390
|
+
|
391
|
+
it "should handled emulated trim function" do
|
392
|
+
@d.lit(Sequel.trim(:a)).should == 'trim(a)'
|
393
|
+
end
|
384
394
|
end
|
385
395
|
|
386
396
|
describe Sequel::SQL::VirtualRow do
|
@@ -523,12 +533,14 @@ end
|
|
523
533
|
describe "Sequel core extension replacements" do
|
524
534
|
before do
|
525
535
|
@db = Sequel::Database.new
|
536
|
+
@ds = @db.dataset
|
537
|
+
def @ds.supports_regexp?; true end
|
526
538
|
@o = Object.new
|
527
539
|
def @o.sql_literal(ds) 'foo' end
|
528
540
|
end
|
529
541
|
|
530
542
|
def l(arg, should)
|
531
|
-
@
|
543
|
+
@ds.literal(arg).should == should
|
532
544
|
end
|
533
545
|
|
534
546
|
it "Sequel.expr should return items wrapped in Sequel objects" do
|
@@ -446,4 +446,8 @@ describe "Sequel Mock Adapter" do
|
|
446
446
|
specify "should stub out the primary_key method for postgres" do
|
447
447
|
Sequel.mock(:host=>'postgres').primary_key(:t).should == :id
|
448
448
|
end
|
449
|
+
|
450
|
+
specify "should handle creating tables on oracle" do
|
451
|
+
proc{Sequel.mock(:host=>'oracle').create_table(:a){String :b}}.should_not raise_error
|
452
|
+
end
|
449
453
|
end
|
@@ -92,19 +92,6 @@ describe Sequel::Dataset, " graphing" do
|
|
92
92
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
93
93
|
end
|
94
94
|
|
95
|
-
it "#graph should accept an object that responds to dataset as the dataset" do
|
96
|
-
oc = Class.new
|
97
|
-
o = oc.new
|
98
|
-
ds = @ds2
|
99
|
-
oc.send(:define_method, :dataset){ds}
|
100
|
-
ds = @ds1.graph(o, :x=>:id)
|
101
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
102
|
-
ds = :lines
|
103
|
-
oc.send(:define_method, :dataset){ds}
|
104
|
-
ds = @ds1.graph(o, :x=>:id)
|
105
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
106
|
-
end
|
107
|
-
|
108
95
|
it "#graph should raise an error if a symbol, dataset, or model is not used" do
|
109
96
|
proc{@ds1.graph(Object.new, :x=>:id)}.should raise_error(Sequel::Error)
|
110
97
|
end
|
data/spec/core/schema_spec.rb
CHANGED
@@ -757,20 +757,34 @@ describe "DB#alter_table" do
|
|
757
757
|
@db = Sequel.mock
|
758
758
|
end
|
759
759
|
|
760
|
-
specify "should allow adding not null constraint" do
|
760
|
+
specify "should allow adding not null constraint via set_column_allow_null with false argument" do
|
761
761
|
@db.alter_table(:cats) do
|
762
762
|
set_column_allow_null :score, false
|
763
763
|
end
|
764
764
|
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score SET NOT NULL"]
|
765
765
|
end
|
766
766
|
|
767
|
-
specify "should allow
|
767
|
+
specify "should allow removing not null constraint via set_column_allow_null with true argument" do
|
768
768
|
@db.alter_table(:cats) do
|
769
769
|
set_column_allow_null :score, true
|
770
770
|
end
|
771
771
|
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score DROP NOT NULL"]
|
772
772
|
end
|
773
773
|
|
774
|
+
specify "should allow adding not null constraint via set_column_not_null" do
|
775
|
+
@db.alter_table(:cats) do
|
776
|
+
set_column_not_null :score
|
777
|
+
end
|
778
|
+
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score SET NOT NULL"]
|
779
|
+
end
|
780
|
+
|
781
|
+
specify "should allow removing not null constraint via set_column_allow_null without argument" do
|
782
|
+
@db.alter_table(:cats) do
|
783
|
+
set_column_allow_null :score
|
784
|
+
end
|
785
|
+
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score DROP NOT NULL"]
|
786
|
+
end
|
787
|
+
|
774
788
|
specify "should support add_column" do
|
775
789
|
@db.alter_table(:cats) do
|
776
790
|
add_column :score, :integer
|
@@ -939,6 +953,40 @@ describe "DB#alter_table" do
|
|
939
953
|
"ALTER TABLE cats ALTER COLUMN score TYPE varchar(30)",
|
940
954
|
"ALTER TABLE cats ALTER COLUMN score TYPE enum('a', 'b')"]
|
941
955
|
end
|
956
|
+
|
957
|
+
specify "should combine operations into a single query if the database supports it" do
|
958
|
+
@db.meta_def(:supports_combining_alter_table_ops?){true}
|
959
|
+
@db.alter_table(:cats) do
|
960
|
+
add_column :a, Integer
|
961
|
+
drop_column :b
|
962
|
+
set_column_not_null :c
|
963
|
+
rename_column :d, :e
|
964
|
+
set_column_default :f, 'g'
|
965
|
+
set_column_type :h, Integer
|
966
|
+
add_constraint(:i){a > 1}
|
967
|
+
drop_constraint :j
|
968
|
+
end
|
969
|
+
@db.sqls.should == ["ALTER TABLE cats ADD COLUMN a integer, DROP COLUMN b, ALTER COLUMN c SET NOT NULL, RENAME COLUMN d TO e, ALTER COLUMN f SET DEFAULT 'g', ALTER COLUMN h TYPE integer, ADD CONSTRAINT i CHECK (a > 1), DROP CONSTRAINT j"]
|
970
|
+
end
|
971
|
+
|
972
|
+
specify "should combine operations into consecutive groups of combinable operations if the database supports combining operations" do
|
973
|
+
@db.meta_def(:supports_combining_alter_table_ops?){true}
|
974
|
+
@db.alter_table(:cats) do
|
975
|
+
add_column :a, Integer
|
976
|
+
drop_column :b
|
977
|
+
set_column_not_null :c
|
978
|
+
rename_column :d, :e
|
979
|
+
add_index :e
|
980
|
+
set_column_default :f, 'g'
|
981
|
+
set_column_type :h, Integer
|
982
|
+
add_constraint(:i){a > 1}
|
983
|
+
drop_constraint :j
|
984
|
+
end
|
985
|
+
@db.sqls.should == ["ALTER TABLE cats ADD COLUMN a integer, DROP COLUMN b, ALTER COLUMN c SET NOT NULL, RENAME COLUMN d TO e",
|
986
|
+
"CREATE INDEX cats_e_index ON cats (e)",
|
987
|
+
"ALTER TABLE cats ALTER COLUMN f SET DEFAULT 'g', ALTER COLUMN h TYPE integer, ADD CONSTRAINT i CHECK (a > 1), DROP CONSTRAINT j"]
|
988
|
+
end
|
989
|
+
|
942
990
|
end
|
943
991
|
|
944
992
|
describe "Database#create_table" do
|
@@ -1251,11 +1299,13 @@ describe "Schema Parser" do
|
|
1251
1299
|
end
|
1252
1300
|
|
1253
1301
|
specify "should correctly parse all supported data types" do
|
1254
|
-
|
1255
|
-
|
1302
|
+
sm = Module.new do
|
1303
|
+
def schema_parse_table(t, opts)
|
1304
|
+
[[:x, {:type=>schema_column_type(t.to_s)}]]
|
1305
|
+
end
|
1256
1306
|
end
|
1307
|
+
@db.extend(sm)
|
1257
1308
|
@db.schema(:tinyint).first.last[:type].should == :integer
|
1258
|
-
@db.schema(:interval).first.last[:type].should == :interval
|
1259
1309
|
@db.schema(:int).first.last[:type].should == :integer
|
1260
1310
|
@db.schema(:integer).first.last[:type].should == :integer
|
1261
1311
|
@db.schema(:bigint).first.last[:type].should == :integer
|
@@ -1277,6 +1327,7 @@ describe "Schema Parser" do
|
|
1277
1327
|
@db.schema(:real).first.last[:type].should == :float
|
1278
1328
|
@db.schema(:float).first.last[:type].should == :float
|
1279
1329
|
@db.schema(:double).first.last[:type].should == :float
|
1330
|
+
@db.schema(:"double(1,2)").first.last[:type].should == :float
|
1280
1331
|
@db.schema(:"double precision").first.last[:type].should == :float
|
1281
1332
|
@db.schema(:number).first.last[:type].should == :decimal
|
1282
1333
|
@db.schema(:numeric).first.last[:type].should == :decimal
|
@@ -1296,5 +1347,13 @@ describe "Schema Parser" do
|
|
1296
1347
|
@db.schema(:binary).first.last[:type].should == :blob
|
1297
1348
|
@db.schema(:varbinary).first.last[:type].should == :blob
|
1298
1349
|
@db.schema(:enum).first.last[:type].should == :enum
|
1350
|
+
|
1351
|
+
@db = Sequel.mock(:host=>'postgres')
|
1352
|
+
@db.extend(sm)
|
1353
|
+
@db.schema(:interval).first.last[:type].should == :interval
|
1354
|
+
|
1355
|
+
@db = Sequel.mock(:host=>'mysql')
|
1356
|
+
@db.extend(sm)
|
1357
|
+
@db.schema(:set).first.last[:type].should == :set
|
1299
1358
|
end
|
1300
1359
|
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel::Plugins::ConstraintValidations" do
|
4
|
+
def model_class(opts={})
|
5
|
+
return @c if @c
|
6
|
+
@c = Class.new(Sequel::Model(@db[:items]))
|
7
|
+
@c.columns :name
|
8
|
+
@db.sqls
|
9
|
+
set_fetch(opts)
|
10
|
+
@c.plugin :constraint_validations
|
11
|
+
@c
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_fetch(opts)
|
15
|
+
@db.fetch = {:table=>'items', :message=>nil, :allow_nil=>nil, :constraint_name=>nil, :validation_type=>'presence', :argument=>nil, :column=>'name'}.merge(opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
@db = Sequel.mock
|
20
|
+
set_fetch({})
|
21
|
+
@ds = @db[:items]
|
22
|
+
@ds.instance_variable_set(:@columns, [:name])
|
23
|
+
@ds2 = @db.dup[:items2]
|
24
|
+
@ds2.instance_variable_set(:@columns, [:name])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should load the validation_helpers plugin into the class" do
|
28
|
+
model_class.new.should respond_to(:validates_presence)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should parse constraint validations when loading plugin" do
|
32
|
+
@c = model_class
|
33
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
34
|
+
@db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
|
35
|
+
@c.constraint_validations.should == [[:validates_presence, :name]]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should parse constraint validations with a custom constraint validations table" do
|
39
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
40
|
+
@db.sqls
|
41
|
+
c.plugin :constraint_validations, :constraint_validations_table=>:foo
|
42
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
43
|
+
@db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
|
44
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should populate constraint_validations when subclassing" do
|
48
|
+
c = Class.new(Sequel::Model(@db))
|
49
|
+
c.plugin :constraint_validations
|
50
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
51
|
+
sc = Class.new(c)
|
52
|
+
sc.set_dataset @ds
|
53
|
+
@db.sqls.should == []
|
54
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should populate constraint_validations when changing the model's dataset" do
|
58
|
+
c = Class.new(Sequel::Model(@db[:foo]))
|
59
|
+
c.columns :name
|
60
|
+
@db.sqls
|
61
|
+
c.plugin :constraint_validations
|
62
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
63
|
+
sc = Class.new(c)
|
64
|
+
sc.set_dataset @ds
|
65
|
+
@db.sqls.should == []
|
66
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should reparse constraint validations when changing the model's database" do
|
70
|
+
c = Class.new(Sequel::Model(@ds2))
|
71
|
+
c.plugin :constraint_validations
|
72
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
73
|
+
sc = Class.new(c)
|
74
|
+
sc.set_dataset @ds
|
75
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
76
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should reparse constraint validations when changing the model's database with a custom constraint validations table" do
|
80
|
+
c = Class.new(Sequel::Model(@ds2))
|
81
|
+
c.plugin :constraint_validations, :constraint_validations_table=>:foo
|
82
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
83
|
+
sc = Class.new(c)
|
84
|
+
sc.set_dataset @ds
|
85
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
86
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should correctly retrieve :message option from constraint validations table" do
|
90
|
+
model_class(:message=>'foo').constraint_validations.should == [[:validates_presence, :name, {:message=>'foo'}]]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should correctly retrieve :allow_nil option from constraint validations table" do
|
94
|
+
model_class(:allow_nil=>true).constraint_validations.should == [[:validates_presence, :name, {:allow_nil=>true}]]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should handle presence validation" do
|
98
|
+
model_class(:validation_type=>'presence').constraint_validations.should == [[:validates_presence, :name]]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should handle exact_length validation" do
|
102
|
+
model_class(:validation_type=>'exact_length', :argument=>'5').constraint_validations.should == [[:validates_exact_length, 5, :name]]
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should handle min_length validation" do
|
106
|
+
model_class(:validation_type=>'min_length', :argument=>'5').constraint_validations.should == [[:validates_min_length, 5, :name]]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should handle max_length validation" do
|
110
|
+
model_class(:validation_type=>'max_length', :argument=>'5').constraint_validations.should == [[:validates_max_length, 5, :name]]
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should handle length_range validation" do
|
114
|
+
model_class(:validation_type=>'length_range', :argument=>'3..5').constraint_validations.should == [[:validates_length_range, 3..5, :name]]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should handle length_range validation with an exclusive end" do
|
118
|
+
model_class(:validation_type=>'length_range', :argument=>'3...5').constraint_validations.should == [[:validates_length_range, 3...5, :name]]
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should handle format validation" do
|
122
|
+
model_class(:validation_type=>'format', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/, :name]]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should handle format validation with case insensitive format" do
|
126
|
+
model_class(:validation_type=>'iformat', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/i, :name]]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should handle includes validation with array of strings" do
|
130
|
+
model_class(:validation_type=>'includes_str_array', :argument=>'a,b,c').constraint_validations.should == [[:validates_includes, %w'a b c', :name]]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should handle includes validation with array of integers" do
|
134
|
+
model_class(:validation_type=>'includes_int_array', :argument=>'1,2,3').constraint_validations.should == [[:validates_includes, [1, 2, 3], :name]]
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should handle includes validation with inclusive range of integers" do
|
138
|
+
model_class(:validation_type=>'includes_int_range', :argument=>'3..5').constraint_validations.should == [[:validates_includes, 3..5, :name]]
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should handle includes validation with exclusive range of integers" do
|
142
|
+
model_class(:validation_type=>'includes_int_range', :argument=>'3...5').constraint_validations.should == [[:validates_includes, 3...5, :name]]
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should handle like validation" do
|
146
|
+
model_class(:validation_type=>'like', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/, :name]]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should handle ilike validation" do
|
150
|
+
model_class(:validation_type=>'ilike', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/i, :name]]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should handle like validation with % metacharacter" do
|
154
|
+
model_class(:validation_type=>'like', :argument=>'%foo%').constraint_validations.should == [[:validates_format, /\A.*foo.*\z/, :name]]
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should handle like validation with %% metacharacter" do
|
158
|
+
model_class(:validation_type=>'like', :argument=>'%%foo%%').constraint_validations.should == [[:validates_format, /\A%foo%\z/, :name]]
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should handle like validation with _ metacharacter" do
|
162
|
+
model_class(:validation_type=>'like', :argument=>'f_o').constraint_validations.should == [[:validates_format, /\Af.o\z/, :name]]
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should handle like validation with Regexp metacharacter" do
|
166
|
+
model_class(:validation_type=>'like', :argument=>'\wfoo\d').constraint_validations.should == [[:validates_format, /\A\\wfoo\\d\z/, :name]]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should handle unique validation" do
|
170
|
+
model_class(:validation_type=>'unique').constraint_validations.should == [[:validates_unique, [:name]]]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should handle unique validation with multiple columns" do
|
174
|
+
model_class(:validation_type=>'unique', :column=>'name,id').constraint_validations.should == [[:validates_unique, [:name, :id]]]
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should used parsed constraint validations when validating" do
|
178
|
+
o = model_class.new
|
179
|
+
o.valid?.should == false
|
180
|
+
o.errors.full_messages.should == ['name is not present']
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should handle a table name specified as SQL::Identifier" do
|
184
|
+
set_fetch(:table=>'sch__items')
|
185
|
+
c = Class.new(Sequel::Model(@db[Sequel.identifier(:sch__items)]))
|
186
|
+
c.plugin :constraint_validations
|
187
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should handle a table name specified as SQL::QualifiedIdentifier" do
|
191
|
+
set_fetch(:table=>'sch.items')
|
192
|
+
c = Class.new(Sequel::Model(@db[Sequel.qualify(:sch, :items)]))
|
193
|
+
c.plugin :constraint_validations
|
194
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
describe "constraint_validations extension" do
|
4
|
+
def parse_insert(s)
|
5
|
+
m = /\AINSERT INTO sequel_constraint_validations \((.*)\) VALUES \((.*)\)\z/.match(s)
|
6
|
+
Hash[*m[1].split(', ').map{|v| v.to_sym}.zip(m[2].split(', ').map{|v| parse_insert_value(v)}).reject{|k, v| v.nil?}.flatten]
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_insert_value(s)
|
10
|
+
case s
|
11
|
+
when 'NULL'
|
12
|
+
nil
|
13
|
+
when /\A'(.*)'\z/
|
14
|
+
$1
|
15
|
+
else
|
16
|
+
raise Sequel::Error, "unhandled insert value: #{s.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
before do
|
21
|
+
@db = Sequel.mock
|
22
|
+
@db.extend(Module.new{attr_writer :schema; def schema(table, *) execute("parse schema for #{table}"); @schema; end})
|
23
|
+
@db.extension(:constraint_validations)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should allow creating the sequel_constraint_validations table" do
|
27
|
+
@db.create_constraint_validations_table
|
28
|
+
@db.sqls.should == ["CREATE TABLE sequel_constraint_validations (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow creating the sequel_constraint_validations table with a non-default table name" do
|
32
|
+
@db.constraint_validations_table = :foo
|
33
|
+
@db.create_constraint_validations_table
|
34
|
+
@db.sqls.should == ["CREATE TABLE foo (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should allow dropping the sequel_constraint_validations table" do
|
38
|
+
@db.drop_constraint_validations_table
|
39
|
+
@db.sqls.should == ["DROP TABLE sequel_constraint_validations"]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow dropping the sequel_constraint_validations table with a non-default table name" do
|
43
|
+
@db.constraint_validations_table = :foo
|
44
|
+
@db.drop_constraint_validations_table
|
45
|
+
@db.sqls.should == ["DROP TABLE foo"]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should allow dropping validations for a given table" do
|
49
|
+
@db.drop_constraint_validations_for(:table=>:foo)
|
50
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')"]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allow dropping validations for a given table and column" do
|
54
|
+
@db.drop_constraint_validations_for(:table=>:foo, :column=>:bar)
|
55
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (column = 'bar'))"]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should allow dropping validations for a given table and constraint" do
|
59
|
+
@db.drop_constraint_validations_for(:table=>:foo, :constraint=>:bar)
|
60
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (constraint_name = 'bar'))"]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should allow dropping validations for a non-default constraint_validations table" do
|
64
|
+
@db.constraint_validations_table = :cv
|
65
|
+
@db.drop_constraint_validations_for(:table=>:foo)
|
66
|
+
@db.sqls.should == ["DELETE FROM cv WHERE (table = 'foo')"]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should raise an error without deleting if attempting to drop validations with table, column, or constraint" do
|
70
|
+
proc{@db.drop_constraint_validations_for({})}.should raise_error(Sequel::Error)
|
71
|
+
@db.sqls.should == []
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should allow adding constraint validations via create_table validate" do
|
75
|
+
@db.create_table(:foo){String :name; validate{presence :name}}
|
76
|
+
sqls = @db.sqls
|
77
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
78
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should allow adding constraint validations via alter_table validate" do
|
82
|
+
@db.schema = [[:name, {:type=>:string}]]
|
83
|
+
@db.alter_table(:foo){validate{presence :name}}
|
84
|
+
sqls = @db.sqls
|
85
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
86
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK ((name IS NOT NULL) AND (trim(name) != ''))"]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should handle :message option when adding validations" do
|
90
|
+
@db.create_table(:foo){String :name; validate{presence :name, :message=>'not there'}}
|
91
|
+
sqls = @db.sqls
|
92
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :message=>'not there'}
|
93
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should handle :allow_nil option when adding validations" do
|
97
|
+
@db.create_table(:foo){String :name; validate{presence :name, :allow_nil=>true}}
|
98
|
+
sqls = @db.sqls
|
99
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
|
100
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK (trim(name) != ''))"]
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should handle :name option when adding validations" do
|
104
|
+
@db.create_table(:foo){String :name; validate{presence :name, :name=>'cons'}}
|
105
|
+
sqls = @db.sqls
|
106
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :constraint_name=>'cons'}
|
107
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CONSTRAINT cons CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should handle multiple columns when adding validations" do
|
111
|
+
@db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar]}}
|
112
|
+
sqls = @db.sqls
|
113
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
114
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
115
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (bar IS NOT NULL) AND (trim(name) != '') AND (trim(bar) != '')))"]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should handle presence validation on non-String columns" do
|
119
|
+
@db.create_table(:foo){Integer :name; validate{presence :name}}
|
120
|
+
sqls = @db.sqls
|
121
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
122
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (name IS NOT NULL))"]
|
123
|
+
|
124
|
+
@db.schema = [[:name, {:type=>:integer}]]
|
125
|
+
@db.alter_table(:foo){validate{presence :name}}
|
126
|
+
sqls = @db.sqls
|
127
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
128
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (name IS NOT NULL)"]
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should handle presence validation on Oracle with IS NOT NULL instead of != ''" do
|
132
|
+
@db = Sequel.mock(:host=>'oracle')
|
133
|
+
@db.extension(:constraint_validations)
|
134
|
+
@db.create_table(:foo){String :name; validate{presence :name}}
|
135
|
+
sqls = @db.sqls
|
136
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
137
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) IS NOT NULL)))"]
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should assume column is not a String if it can't determine the type" do
|
141
|
+
@db.create_table(:foo){Integer :name; validate{presence :bar}}
|
142
|
+
sqls = @db.sqls
|
143
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
144
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (bar IS NOT NULL))"]
|
145
|
+
|
146
|
+
@db.schema = [[:name, {:type=>:integer}]]
|
147
|
+
@db.alter_table(:foo){validate{presence :bar}}
|
148
|
+
sqls = @db.sqls
|
149
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
150
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (bar IS NOT NULL)"]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should handle presence validation on non-String columns with :allow_nil option" do
|
154
|
+
@db.create_table(:foo){Integer :name; validate{presence :name, :allow_nil=>true}}
|
155
|
+
sqls = @db.sqls
|
156
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
|
157
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer)"]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should support :exact_length constraint validation" do
|
161
|
+
@db.create_table(:foo){String :name; validate{exact_length 5, :name}}
|
162
|
+
sqls = @db.sqls
|
163
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"exact_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
164
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) = 5)))"]
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should support :min_length constraint validation" do
|
168
|
+
@db.create_table(:foo){String :name; validate{min_length 5, :name}}
|
169
|
+
sqls = @db.sqls
|
170
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"min_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
171
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 5)))"]
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should support :max_length constraint validation" do
|
175
|
+
@db.create_table(:foo){String :name; validate{max_length 5, :name}}
|
176
|
+
sqls = @db.sqls
|
177
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"max_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
178
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) <= 5)))"]
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should support :length_range constraint validation" do
|
182
|
+
@db.create_table(:foo){String :name; validate{length_range 3..5, :name}}
|
183
|
+
sqls = @db.sqls
|
184
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
|
185
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) <= 5)))"]
|
186
|
+
|
187
|
+
@db.create_table(:foo){String :name; validate{length_range 3...5, :name}}
|
188
|
+
sqls = @db.sqls
|
189
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
|
190
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) < 5)))"]
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should support :format constraint validation" do
|
194
|
+
@db = Sequel.mock(:host=>'postgres')
|
195
|
+
@db.extension(:constraint_validations)
|
196
|
+
@db.create_table(:foo){String :name; validate{format /^foo.*/, :name}}
|
197
|
+
sqls = @db.sqls
|
198
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"format", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
|
199
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~ '^foo.*')))"]
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should support :format constraint validation with case insensitive format" do
|
203
|
+
@db = Sequel.mock(:host=>'postgres')
|
204
|
+
@db.extension(:constraint_validations)
|
205
|
+
@db.create_table(:foo){String :name; validate{format /^foo.*/i, :name}}
|
206
|
+
sqls = @db.sqls
|
207
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"iformat", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
|
208
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~* '^foo.*')))"]
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should support :includes constraint validation with an array of strings" do
|
212
|
+
@db.create_table(:foo){String :name; validate{includes %w'a b c', :name}}
|
213
|
+
sqls = @db.sqls
|
214
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_str_array", :column=>"name", :table=>"foo", :argument=>'a,b,c'}
|
215
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN ('a', 'b', 'c'))))"]
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should support :includes constraint validation with an array of integers" do
|
219
|
+
@db.create_table(:foo){String :name; validate{includes [1, 2, 3], :name}}
|
220
|
+
sqls = @db.sqls
|
221
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_array", :column=>"name", :table=>"foo", :argument=>'1,2,3'}
|
222
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN (1, 2, 3))))"]
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should support :includes constraint validation with a inclusive range of integers" do
|
226
|
+
@db.create_table(:foo){String :name; validate{includes 3..5, :name}}
|
227
|
+
sqls = @db.sqls
|
228
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
|
229
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name <= 5)))"]
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should support :includes constraint validation with a exclusive range of integers" do
|
233
|
+
@db.create_table(:foo){String :name; validate{includes 3...5, :name}}
|
234
|
+
sqls = @db.sqls
|
235
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
|
236
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name < 5)))"]
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should support :like constraint validation" do
|
240
|
+
@db.create_table(:foo){String :name; validate{like 'foo%', :name}}
|
241
|
+
sqls = @db.sqls
|
242
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"like", :column=>"name", :table=>"foo", :argument=>'foo%'}
|
243
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name LIKE 'foo%')))"]
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should support :ilike constraint validation" do
|
247
|
+
@db.create_table(:foo){String :name; validate{ilike 'foo%', :name}}
|
248
|
+
sqls = @db.sqls
|
249
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"ilike", :column=>"name", :table=>"foo", :argument=>'foo%'}
|
250
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name ILIKE 'foo%')))"]
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should support :unique constraint validation" do
|
254
|
+
@db.create_table(:foo){String :name; validate{unique :name}}
|
255
|
+
sqls = @db.sqls
|
256
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
|
257
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), UNIQUE (name))"]
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should support :unique constraint validation with multiple columns" do
|
261
|
+
@db.create_table(:foo){String :name; Integer :id; validate{unique [:name, :id]}}
|
262
|
+
sqls = @db.sqls
|
263
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name,id", :table=>"foo"}
|
264
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), id integer, UNIQUE (name, id))"]
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should support :unique constraint validation in alter_table" do
|
268
|
+
@db.alter_table(:foo){validate{unique :name}}
|
269
|
+
sqls = @db.sqls
|
270
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
|
271
|
+
sqls.should == ["BEGIN", "COMMIT", "ALTER TABLE foo ADD UNIQUE (name)"]
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should drop constraints and validations when dropping a constraint validation" do
|
275
|
+
@db.alter_table(:foo){String :name; validate{drop :bar}}
|
276
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table, constraint_name) IN (('foo', 'bar')))", "ALTER TABLE foo DROP CONSTRAINT bar"]
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should raise an error if attempting to validate inclusion with a range of non-integers" do
|
280
|
+
proc{@db.create_table(:foo){String :name; validate{includes 'a'..'z', :name}}}.should raise_error(Sequel::Error)
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should raise an error if attempting to validate inclusion with a range of non-integers or strings" do
|
284
|
+
proc{@db.create_table(:foo){String :name; validate{includes [1.0, 2.0], :name}}}.should raise_error(Sequel::Error)
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should raise an error if attempting to validate inclusion with a unsupported object" do
|
288
|
+
proc{@db.create_table(:foo){String :name; validate{includes 'a', :name}}}.should raise_error(Sequel::Error)
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should raise an error if attempting to drop a constraint validation in a create_table generator" do
|
292
|
+
proc{@db.create_table(:foo){String :name; validate{drop :foo}}}.should raise_error(Sequel::Error)
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should raise an error if attempting to drop a constraint validation without a name" do
|
296
|
+
proc{@db.alter_table(:foo){String :name; validate{drop nil}}}.should raise_error(Sequel::Error)
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should raise an error if attempting attempting to process a constraint validation with an unsupported type" do
|
300
|
+
proc{@db.alter_table(:foo){String :name; validations << {:type=>:foo}}}.should raise_error(Sequel::Error)
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should allow adding constraint validations for tables specified as a SQL::Identifier" do
|
304
|
+
@db.create_table(Sequel.identifier(:sch__foo)){String :name; validate{presence :name}}
|
305
|
+
sqls = @db.sqls
|
306
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch__foo"}
|
307
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch__foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should allow adding constraint validations for tables specified as a SQL::QualifiedIdentifier" do
|
311
|
+
@db.create_table(Sequel.qualify(:sch, :foo)){String :name; validate{presence :name}}
|
312
|
+
sqls = @db.sqls
|
313
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch.foo"}
|
314
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch.foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
315
|
+
end
|
316
|
+
end
|