sequel 4.43.0 → 4.44.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.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/doc/active_record.rdoc +2 -2
- data/doc/code_order.rdoc +15 -0
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/model_dataset_method_design.rdoc +132 -0
- data/doc/opening_databases.rdoc +2 -2
- data/doc/release_notes/4.44.0.txt +125 -0
- data/lib/sequel/adapters/jdbc.rb +5 -1
- data/lib/sequel/adapters/jdbc/as400.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +23 -14
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +27 -0
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/extensions/eval_inspect.rb +4 -4
- data/lib/sequel/extensions/implicit_subquery.rb +48 -0
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +3 -5
- data/lib/sequel/model/associations.rb +107 -4
- data/lib/sequel/model/base.rb +98 -12
- data/lib/sequel/model/dataset_module.rb +1 -1
- data/lib/sequel/plugins/active_model.rb +11 -3
- data/lib/sequel/plugins/association_dependencies.rb +7 -0
- data/lib/sequel/plugins/auto_validations.rb +10 -0
- data/lib/sequel/plugins/blacklist_security.rb +13 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/column_conflicts.rb +8 -0
- data/lib/sequel/plugins/composition.rb +12 -2
- data/lib/sequel/plugins/constraint_validations.rb +12 -0
- data/lib/sequel/plugins/csv_serializer.rb +9 -0
- data/lib/sequel/plugins/defaults_setter.rb +6 -0
- data/lib/sequel/plugins/force_encoding.rb +4 -3
- data/lib/sequel/plugins/hook_class_methods.rb +6 -0
- data/lib/sequel/plugins/input_transformer.rb +9 -0
- data/lib/sequel/plugins/insert_returning_select.rb +8 -0
- data/lib/sequel/plugins/instance_hooks.rb +4 -3
- data/lib/sequel/plugins/json_serializer.rb +9 -0
- data/lib/sequel/plugins/lazy_attributes.rb +7 -0
- data/lib/sequel/plugins/many_through_many.rb +13 -2
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_array_associations.rb +18 -2
- data/lib/sequel/plugins/pg_row.rb +3 -3
- data/lib/sequel/plugins/pg_typecast_on_load.rb +7 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +7 -0
- data/lib/sequel/plugins/serialization.rb +9 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +13 -1
- data/lib/sequel/plugins/subclasses.rb +15 -1
- data/lib/sequel/plugins/touch.rb +7 -0
- data/lib/sequel/plugins/tree.rb +7 -0
- data/lib/sequel/plugins/typecast_on_load.rb +7 -0
- data/lib/sequel/plugins/update_refresh.rb +24 -13
- data/lib/sequel/plugins/validation_class_methods.rb +13 -0
- data/lib/sequel/sql.rb +28 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +18 -15
- data/spec/core/dataset_spec.rb +5 -0
- data/spec/core/expression_filters_spec.rb +33 -0
- data/spec/extensions/active_model_spec.rb +15 -1
- data/spec/extensions/association_dependencies_spec.rb +8 -0
- data/spec/extensions/auto_validations_spec.rb +8 -0
- data/spec/extensions/blacklist_security_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +9 -0
- data/spec/extensions/column_conflicts_spec.rb +6 -0
- data/spec/extensions/composition_spec.rb +8 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +12 -0
- data/spec/extensions/csv_serializer_spec.rb +7 -0
- data/spec/extensions/defaults_setter_spec.rb +7 -0
- data/spec/extensions/force_encoding_spec.rb +14 -0
- data/spec/extensions/hook_class_methods_spec.rb +10 -0
- data/spec/extensions/implicit_subquery_spec.rb +60 -0
- data/spec/extensions/input_transformer_spec.rb +10 -0
- data/spec/extensions/insert_returning_select_spec.rb +6 -0
- data/spec/extensions/json_serializer_spec.rb +7 -0
- data/spec/extensions/lazy_attributes_spec.rb +6 -0
- data/spec/extensions/many_through_many_spec.rb +44 -0
- data/spec/extensions/nested_attributes_spec.rb +5 -0
- data/spec/extensions/pg_array_associations_spec.rb +46 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +5 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +5 -0
- data/spec/extensions/serialization_spec.rb +7 -0
- data/spec/extensions/single_table_inheritance_spec.rb +19 -2
- data/spec/extensions/subclasses_spec.rb +13 -0
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +6 -0
- data/spec/extensions/tree_spec.rb +6 -0
- data/spec/extensions/typecast_on_load_spec.rb +6 -0
- data/spec/extensions/update_refresh_spec.rb +7 -1
- data/spec/extensions/validation_class_methods_spec.rb +13 -0
- data/spec/model/association_reflection_spec.rb +177 -0
- data/spec/model/associations_spec.rb +16 -0
- data/spec/model/dataset_methods_spec.rb +59 -0
- data/spec/model/model_spec.rb +59 -0
- metadata +8 -2
data/spec/core/dataset_spec.rb
CHANGED
@@ -2057,6 +2057,11 @@ describe "Dataset#from_self" do
|
|
2057
2057
|
@ds.from_self.sql.must_equal 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS t1'
|
2058
2058
|
end
|
2059
2059
|
|
2060
|
+
it "should keep any existing columns" do
|
2061
|
+
@ds.columns(:id, :a)
|
2062
|
+
@ds.from_self.columns.must_equal [:id, :a]
|
2063
|
+
end
|
2064
|
+
|
2060
2065
|
it "should modify only the new dataset" do
|
2061
2066
|
@ds.from_self.select(:bogus).sql.must_equal 'SELECT bogus FROM (SELECT name FROM test LIMIT 1) AS t1'
|
2062
2067
|
end
|
@@ -248,6 +248,10 @@ describe "Blockless Ruby Filters" do
|
|
248
248
|
@d.l(~((((Sequel.lit('x') - :y)/(Sequel.expr(:x) + :y))*:z) <= 100)).must_equal '((((x - y) / (x + y)) * z) > 100)'
|
249
249
|
end
|
250
250
|
|
251
|
+
it "should have LiteralString#inspect show it is a literal string" do
|
252
|
+
Sequel.lit('x').inspect.must_equal "#<Sequel::LiteralString \"x\">"
|
253
|
+
end
|
254
|
+
|
251
255
|
it "should support hashes by ANDing the conditions" do
|
252
256
|
@d.l(:x => 100, :y => 'a')[1...-1].split(' AND ').sort.must_equal ['(x = 100)', '(y = \'a\')']
|
253
257
|
@d.l(:x => true, :y => false)[1...-1].split(' AND ').sort.must_equal ['(x IS TRUE)', '(y IS FALSE)']
|
@@ -270,6 +274,10 @@ describe "Blockless Ruby Filters" do
|
|
270
274
|
@d.l([:x, :y]=>Sequel.value_list([[1,2], [3,4]])).must_equal '(((x = 1) AND (y = 2)) OR ((x = 3) AND (y = 4)))'
|
271
275
|
@d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).must_equal '(((x = 1) AND (y = 2) AND (z = 5)) OR ((x = 3) AND (y = 4) AND (z = 6)))'
|
272
276
|
end
|
277
|
+
|
278
|
+
it "should have SQL::ValueList#inspect show it is a value list" do
|
279
|
+
Sequel.value_list([[1,2], [3,4]]).inspect.must_equal "#<Sequel::SQL::ValueList [[1, 2], [3, 4]]>"
|
280
|
+
end
|
273
281
|
|
274
282
|
it "should support StringExpression#+ for concatenation of SQL strings" do
|
275
283
|
@d.lit(Sequel.expr(:x).sql_string + :y).must_equal '(x || y)'
|
@@ -952,6 +960,21 @@ describe "Sequel core extension replacements" do
|
|
952
960
|
Sequel.blob(o).must_be_same_as(o)
|
953
961
|
end
|
954
962
|
|
963
|
+
it "Sequel.blob#inspect output should indicate it is a blob and the size" do
|
964
|
+
o = Sequel.blob('a')
|
965
|
+
o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=1 content=\"a\">"
|
966
|
+
o = Sequel.blob(('a'..'z').to_a.join)
|
967
|
+
o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"abcdefghij\" end=\"qrstuvwxyz\">"
|
968
|
+
o = Sequel.blob(255.chr)
|
969
|
+
o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=1 content=\"#{RUBY_VERSION >= '1.9' ? "\\xFF" : "\\377"}\">"
|
970
|
+
o = Sequel.blob((230..255).map(&:chr).join)
|
971
|
+
if RUBY_VERSION >= '1.9'
|
972
|
+
o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"\\xE6\\xE7\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\" end=\"\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF\">"
|
973
|
+
else
|
974
|
+
o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"\\346\\347\\350\\351\\352\\353\\354\\355\\356\\357\" end=\"\\366\\367\\370\\371\\372\\373\\374\\375\\376\\377\">"
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
955
978
|
it "Sequel.deep_qualify should do a deep qualification into nested structors" do
|
956
979
|
l(Sequel.deep_qualify(:t, Sequel.+(:c, 1)), "(t.c + 1)")
|
957
980
|
end
|
@@ -1138,6 +1161,11 @@ describe "Sequel::SQLTime" do
|
|
1138
1161
|
Sequel::SQLTime.create(1, 2, 3).strftime('%Y-%m-%d').must_equal Date.new(2000).strftime('%Y-%m-%d')
|
1139
1162
|
end
|
1140
1163
|
|
1164
|
+
it "#inspect should show class and time by default" do
|
1165
|
+
Sequel::SQLTime.create(1, 2, 3).inspect.must_equal "#<Sequel::SQLTime 01:02:03>"
|
1166
|
+
Sequel::SQLTime.create(13, 24, 35).inspect.must_equal "#<Sequel::SQLTime 13:24:35>"
|
1167
|
+
end
|
1168
|
+
|
1141
1169
|
it "#to_s should include hour, minute, and second by default" do
|
1142
1170
|
Sequel::SQLTime.create(1, 2, 3).to_s.must_equal "01:02:03"
|
1143
1171
|
Sequel::SQLTime.create(1, 2, 3, 500000).to_s.must_equal "01:02:03"
|
@@ -1225,6 +1253,11 @@ describe "Sequel.recursive_map" do
|
|
1225
1253
|
Sequel.recursive_map([nil], proc{|s| s.to_i}).must_equal [nil]
|
1226
1254
|
Sequel.recursive_map([[nil]], proc{|s| s.to_i}).must_equal [[nil]]
|
1227
1255
|
end
|
1256
|
+
|
1257
|
+
it "should call callable for falsey value" do
|
1258
|
+
Sequel.recursive_map([false], proc{|s| s.to_s}).must_equal ['false']
|
1259
|
+
Sequel.recursive_map([[false]], proc{|s| s.to_s}).must_equal [['false']]
|
1260
|
+
end
|
1228
1261
|
end
|
1229
1262
|
|
1230
1263
|
describe "Sequel.delay" do
|
@@ -19,6 +19,7 @@ describe "ActiveModel plugin" do
|
|
19
19
|
end
|
20
20
|
@c = AMLintTest
|
21
21
|
@c.plugin :active_model
|
22
|
+
@c.freeze if @freeze_class
|
22
23
|
@m = @model = @c.new
|
23
24
|
@o = @c.load({})
|
24
25
|
end
|
@@ -26,7 +27,6 @@ describe "ActiveModel plugin" do
|
|
26
27
|
Object.send(:remove_const, :AMLintTest)
|
27
28
|
Object.send(:remove_const, :Blog)
|
28
29
|
end
|
29
|
-
include ActiveModel::Lint::Tests
|
30
30
|
|
31
31
|
it ".to_model should return self, not a proxy object" do
|
32
32
|
@m.object_id.must_equal @m.to_model.object_id
|
@@ -40,6 +40,7 @@ describe "ActiveModel plugin" do
|
|
40
40
|
@o.to_key.must_be_nil
|
41
41
|
|
42
42
|
@c.set_primary_key [:id2, :id]
|
43
|
+
@c.freeze
|
43
44
|
@o.to_key.must_be_nil
|
44
45
|
@o.id = 1
|
45
46
|
@o.id2 = 2
|
@@ -55,6 +56,7 @@ describe "ActiveModel plugin" do
|
|
55
56
|
@o.id = 1
|
56
57
|
@o.to_param.must_equal '1'
|
57
58
|
@c.set_primary_key [:id2, :id]
|
59
|
+
@c.freeze
|
58
60
|
@o.id2 = 2
|
59
61
|
@o.to_param.must_equal '2-1'
|
60
62
|
@o.meta_def(:to_param_joiner){'|'}
|
@@ -81,5 +83,17 @@ describe "ActiveModel plugin" do
|
|
81
83
|
@m.to_partial_path.must_equal 'am_lint_tests/am_lint_test'
|
82
84
|
Blog::Post.new.to_partial_path.must_equal 'blog/posts/post'
|
83
85
|
end
|
86
|
+
|
87
|
+
describe "with unfrozen model class" do
|
88
|
+
include ActiveModel::Lint::Tests
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "with frozen model class" do
|
92
|
+
before do
|
93
|
+
@freeze_class = true
|
94
|
+
end
|
95
|
+
|
96
|
+
include ActiveModel::Lint::Tests
|
97
|
+
end
|
84
98
|
end
|
85
99
|
end
|
@@ -72,6 +72,14 @@ describe "AssociationDependencies plugin" do
|
|
72
72
|
DB.sqls.must_equal ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE id = 2']
|
73
73
|
end
|
74
74
|
|
75
|
+
it "should not allow modifications if class is frozen" do
|
76
|
+
@Artist.add_association_dependencies :other_artists=>:nullify
|
77
|
+
@Artist.freeze
|
78
|
+
proc{@Artist.add_association_dependencies :albums=>:nullify}.must_raise RuntimeError, TypeError
|
79
|
+
@Artist.association_dependencies.frozen?.must_equal true
|
80
|
+
@Artist.association_dependencies[:before_nullify].frozen?.must_equal true
|
81
|
+
end
|
82
|
+
|
75
83
|
it "should raise an error if attempting to nullify a many_to_one association" do
|
76
84
|
proc{@Album.add_association_dependencies :artist=>:nullify}.must_raise(Sequel::Error)
|
77
85
|
end
|
@@ -189,4 +189,12 @@ describe "Sequel::Plugins::AutoValidations" do
|
|
189
189
|
@m.valid?.must_equal false
|
190
190
|
@m.errors.must_equal([:name, :num]=>["u_message"])
|
191
191
|
end
|
192
|
+
|
193
|
+
it "should not allow modifying auto validation information for frozen model classes" do
|
194
|
+
@c.freeze
|
195
|
+
@c.auto_validate_not_null_columns.frozen?.must_equal true
|
196
|
+
@c.auto_validate_explicit_not_null_columns.frozen?.must_equal true
|
197
|
+
@c.auto_validate_max_length_columns.frozen?.must_equal true
|
198
|
+
@c.auto_validate_unique_columns.frozen?.must_equal true
|
199
|
+
end
|
192
200
|
end
|
@@ -85,4 +85,10 @@ describe Sequel::Model, ".restricted_columns " do
|
|
85
85
|
i.values.must_equal(:y => 7)
|
86
86
|
DB.sqls.must_equal ["INSERT INTO blahblah (y) VALUES (7)", "SELECT * FROM blahblah WHERE id = 10"]
|
87
87
|
end
|
88
|
+
|
89
|
+
it "should freeze restricted_columns when freezing class" do
|
90
|
+
@c.set_restricted_columns :z
|
91
|
+
@c.freeze
|
92
|
+
@c.restricted_columns.frozen?.must_equal true
|
93
|
+
end
|
88
94
|
end
|
@@ -51,6 +51,15 @@ describe "class_table_inheritance plugin" do
|
|
51
51
|
Object.send(:remove_const, :Employee)
|
52
52
|
end
|
53
53
|
|
54
|
+
it "should freeze CTI information when freezing model class" do
|
55
|
+
Employee.freeze
|
56
|
+
Employee.cti_models.frozen?.must_equal true
|
57
|
+
Employee.cti_tables.frozen?.must_equal true
|
58
|
+
Employee.cti_instance_dataset.frozen?.must_equal true
|
59
|
+
Employee.cti_table_columns.frozen?.must_equal true
|
60
|
+
Employee.cti_table_map.frozen?.must_equal true
|
61
|
+
end
|
62
|
+
|
54
63
|
it "should not attempt to use prepared statements" do
|
55
64
|
Manager.plugin :prepared_statements
|
56
65
|
Manager[1]
|
@@ -55,4 +55,10 @@ describe "column_conflicts plugin" do
|
|
55
55
|
o.get_column_value(:object_id).must_equal 3
|
56
56
|
o.object_id.wont_equal 3
|
57
57
|
end
|
58
|
+
|
59
|
+
it "should freeze column conflict information when freezing model class" do
|
60
|
+
@c.freeze
|
61
|
+
@c.get_column_conflicts.frozen?.must_equal true
|
62
|
+
@c.set_column_conflicts.frozen?.must_equal true
|
63
|
+
end
|
58
64
|
end
|
@@ -241,4 +241,12 @@ describe "Composition plugin" do
|
|
241
241
|
sql.must_include("month = 2")
|
242
242
|
sql.must_include("day = 3")
|
243
243
|
end
|
244
|
+
|
245
|
+
it "should freeze composition metadata when freezing model class" do
|
246
|
+
@c.composition :date, :mapping=>[:year, :month, :day]
|
247
|
+
@c.freeze
|
248
|
+
@c.compositions.frozen?.must_equal true
|
249
|
+
@c.compositions[:date].frozen?.must_equal true
|
250
|
+
@c.composition_module.frozen?.must_equal true
|
251
|
+
end
|
244
252
|
end
|
@@ -285,4 +285,16 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
285
285
|
c.constraint_validations.must_equal [[:validates_presence, :name]]
|
286
286
|
c.constraint_validation_reflections.must_equal(:name=>[[:presence, {}]])
|
287
287
|
end
|
288
|
+
|
289
|
+
it "should freeze constraint validations data when freezing model class" do
|
290
|
+
@c = model_class
|
291
|
+
@c.freeze
|
292
|
+
@c.constraint_validations.frozen?.must_equal true
|
293
|
+
@c.constraint_validations.all?(&:frozen?).must_equal true
|
294
|
+
@c.constraint_validation_reflections.frozen?.must_equal true
|
295
|
+
@c.constraint_validation_reflections.values.all?(&:frozen?).must_equal true
|
296
|
+
@c.constraint_validation_reflections.values.all?{|r| r.all?(&:frozen?)}.must_equal true
|
297
|
+
@c.instance_variable_get(:@constraint_validation_options).frozen?.must_equal true
|
298
|
+
@c.instance_variable_get(:@constraint_validation_options).values.all?(&:frozen?).must_equal true
|
299
|
+
end
|
288
300
|
end
|
@@ -177,5 +177,12 @@ describe "Sequel::Plugins::CsvSerializer" do
|
|
177
177
|
@album.to_csv(:only=>:name).must_equal "RF\n"
|
178
178
|
@album.to_csv(:except=>[:id, :artist_id]).must_equal "RF\n"
|
179
179
|
end
|
180
|
+
|
181
|
+
it "should freeze csv serializier opts when model class is frozen" do
|
182
|
+
@Album.csv_serializer_opts[:only] = [:id]
|
183
|
+
@Album.freeze
|
184
|
+
@Album.csv_serializer_opts.frozen?.must_equal true
|
185
|
+
@Album.csv_serializer_opts[:only].frozen?.must_equal true
|
186
|
+
end
|
180
187
|
end
|
181
188
|
end
|
@@ -99,4 +99,11 @@ describe "Sequel::Plugins::DefaultsSetter" do
|
|
99
99
|
c.plugin :defaults_setter
|
100
100
|
c.default_values.must_equal(:a=>2)
|
101
101
|
end
|
102
|
+
|
103
|
+
it "should freeze default values when freezing model class" do
|
104
|
+
c = Class.new(Sequel::Model(@db[:bar]))
|
105
|
+
c.plugin :defaults_setter
|
106
|
+
c.freeze
|
107
|
+
c.default_values.frozen?.must_equal true
|
108
|
+
end
|
102
109
|
end
|
@@ -24,6 +24,20 @@ describe "force_encoding plugin" do
|
|
24
24
|
o.x.encoding.must_equal @e1
|
25
25
|
end
|
26
26
|
|
27
|
+
it "should not force encoding of blobs to given encoding on load" do
|
28
|
+
s = Sequel.blob('blah'.dup.force_encoding('BINARY'))
|
29
|
+
o = @c.load(:id=>1, :x=>s)
|
30
|
+
o.x.must_equal 'blah'
|
31
|
+
o.x.encoding.must_equal Encoding.find('BINARY')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not force encoding of blobs to given encoding when setting column values" do
|
35
|
+
s = Sequel.blob('blah'.dup.force_encoding('BINARY'))
|
36
|
+
o = @c.new(:x=>s)
|
37
|
+
o.x.must_equal 'blah'
|
38
|
+
o.x.encoding.must_equal Encoding.find('BINARY')
|
39
|
+
end
|
40
|
+
|
27
41
|
it "should work correctly when given a frozen string" do
|
28
42
|
s = 'blah'.dup
|
29
43
|
s.force_encoding('US-ASCII')
|
@@ -12,6 +12,16 @@ describe Sequel::Model, "hook_class_methods plugin" do
|
|
12
12
|
DB.reset
|
13
13
|
end
|
14
14
|
|
15
|
+
it "should freeze hooks when freezing model class" do
|
16
|
+
c = model_class.call Sequel::Model do
|
17
|
+
before_save{adds << 'hi'}
|
18
|
+
end
|
19
|
+
c.freeze
|
20
|
+
hooks = c.instance_variable_get(:@hooks)
|
21
|
+
hooks.frozen?.must_equal true
|
22
|
+
hooks.values.all?(&:frozen?).must_equal true
|
23
|
+
end
|
24
|
+
|
15
25
|
it "should be definable using a block" do
|
16
26
|
adds = []
|
17
27
|
c = model_class.call Sequel::Model do
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel::Dataset::ImplicitSubquery" do
|
4
|
+
it "should implicitly use a subquery for most dataset query methods" do
|
5
|
+
db = Sequel.mock
|
6
|
+
db.extend_datasets{def supports_cte?; true end}
|
7
|
+
ds = db["SELECT * FROM table"].extension(:implicit_subquery)
|
8
|
+
ds.columns(:id, :a)
|
9
|
+
ods = db[:c]
|
10
|
+
ods.columns(:id, :b)
|
11
|
+
|
12
|
+
ds.and(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
|
13
|
+
ds.cross_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 CROSS JOIN c"
|
14
|
+
ds.distinct.sql.must_equal "SELECT DISTINCT * FROM (SELECT * FROM table) AS t1"
|
15
|
+
ds.except(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 EXCEPT SELECT * FROM c) AS t1"
|
16
|
+
ds.exclude(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE NOT c"
|
17
|
+
ds.exclude_having(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 HAVING NOT c"
|
18
|
+
ds.exclude_where(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE NOT c"
|
19
|
+
ds.filter(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
|
20
|
+
ds.for_update.sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FOR UPDATE"
|
21
|
+
ds.full_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FULL JOIN c"
|
22
|
+
ds.full_outer_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FULL OUTER JOIN c"
|
23
|
+
ds.graph(ods).sql.must_equal "SELECT t1.id, t1.a, c.id AS c_id, c.b FROM (SELECT * FROM table) AS t1 LEFT OUTER JOIN c"
|
24
|
+
ds.grep(:c, 'a').sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE ((c LIKE 'a' ESCAPE '\\'))"
|
25
|
+
ds.group(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
|
26
|
+
ds.group_append(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
|
27
|
+
ds.group_and_count(:c).sql.must_equal "SELECT c, count(*) AS count FROM (SELECT * FROM table) AS t1 GROUP BY c"
|
28
|
+
ds.group_by(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
|
29
|
+
ds.having(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 HAVING c"
|
30
|
+
ds.inner_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
|
31
|
+
ds.intersect(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 INTERSECT SELECT * FROM c) AS t1"
|
32
|
+
ds.invert.sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE 'f'"
|
33
|
+
ds.join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
|
34
|
+
ds.join_table(:inner, :c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
|
35
|
+
ds.left_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LEFT JOIN c USING (d)"
|
36
|
+
ds.left_outer_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LEFT OUTER JOIN c USING (d)"
|
37
|
+
ds.limit(1).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LIMIT 1"
|
38
|
+
ds.lock_style('FOR UPDATE').sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FOR UPDATE"
|
39
|
+
ds.natural_full_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL FULL JOIN c"
|
40
|
+
ds.natural_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL JOIN c"
|
41
|
+
ds.natural_left_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL LEFT JOIN c"
|
42
|
+
ds.natural_right_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL RIGHT JOIN c"
|
43
|
+
ds.offset(1).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 OFFSET 1"
|
44
|
+
ds.order(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
|
45
|
+
ds.order_append(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
|
46
|
+
ds.order_by(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
|
47
|
+
ds.order_more(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
|
48
|
+
ds.order_prepend(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
|
49
|
+
ds.right_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 RIGHT JOIN c USING (d)"
|
50
|
+
ds.right_outer_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 RIGHT OUTER JOIN c USING (d)"
|
51
|
+
ds.select(:c).sql.must_equal "SELECT c FROM (SELECT * FROM table) AS t1"
|
52
|
+
ds.select_append(:c).sql.must_equal "SELECT *, c FROM (SELECT * FROM table) AS t1"
|
53
|
+
ds.select_group(:c).sql.must_equal "SELECT c FROM (SELECT * FROM table) AS t1 GROUP BY c"
|
54
|
+
ds.select_more(:c).sql.must_equal "SELECT *, c FROM (SELECT * FROM table) AS t1"
|
55
|
+
ds.union(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 UNION SELECT * FROM c) AS t1"
|
56
|
+
ds.where(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
|
57
|
+
ds.with(:d, ods).sql.must_equal "WITH d AS (SELECT * FROM c) SELECT * FROM (SELECT * FROM table) AS t1"
|
58
|
+
ds.with_recursive(:d, ods, ods).sql.must_equal "WITH d AS (SELECT * FROM c UNION ALL SELECT * FROM c) SELECT * FROM (SELECT * FROM table) AS t1"
|
59
|
+
end
|
60
|
+
end
|
@@ -51,4 +51,14 @@ describe "Sequel::Plugins::InputTransformer" do
|
|
51
51
|
@o.name = ' name '.dup
|
52
52
|
@o.name.must_equal 'raboof eman '
|
53
53
|
end
|
54
|
+
|
55
|
+
it "should freeze input transformers when freezing model class" do
|
56
|
+
@c.skip_input_transformer :reverser, :name
|
57
|
+
@c.freeze
|
58
|
+
@c.input_transformers.frozen?.must_equal true
|
59
|
+
@c.input_transformer_order.frozen?.must_equal true
|
60
|
+
skip = @c.instance_variable_get(:@skip_input_transformer_columns)
|
61
|
+
skip.frozen?.must_equal true
|
62
|
+
skip.values.all?(&:frozen?).must_equal true
|
63
|
+
end
|
54
64
|
end
|
@@ -63,4 +63,10 @@ describe "Sequel::Plugins::InsertReturningSelect" do
|
|
63
63
|
b.create(:x=>2).must_equal b.load(:id=>1, :x=>2)
|
64
64
|
@db.sqls.must_equal ['INSERT INTO albums (x) VALUES (2) RETURNING id, x']
|
65
65
|
end
|
66
|
+
|
67
|
+
it "should freeze instance_insert_dataset when freezing model class" do
|
68
|
+
@Album.plugin :insert_returning_select
|
69
|
+
@Album.freeze
|
70
|
+
@Album.instance_insert_dataset.frozen?.must_equal true
|
71
|
+
end
|
66
72
|
end
|
@@ -303,4 +303,11 @@ describe "Sequel::Plugins::JsonSerializer" do
|
|
303
303
|
it "should raise an error if using an unsupported :associations option" do
|
304
304
|
proc{Artist.from_json(@artist.to_json, :associations=>'')}.must_raise(Sequel::Error)
|
305
305
|
end
|
306
|
+
|
307
|
+
it "should freeze json serializier opts when model class is frozen" do
|
308
|
+
Album.json_serializer_opts[:only] = [:id]
|
309
|
+
Album.freeze
|
310
|
+
Album.json_serializer_opts.frozen?.must_equal true
|
311
|
+
Album.json_serializer_opts[:only].frozen?.must_equal true
|
312
|
+
end
|
306
313
|
end
|
@@ -167,4 +167,10 @@ describe "Sequel::Plugins::LazyAttributes" do
|
|
167
167
|
m.name.must_equal 3
|
168
168
|
@db.sqls.must_equal ["SELECT la.id FROM la LIMIT 1", "SELECT la.name FROM la WHERE (id = 1) LIMIT 1"]
|
169
169
|
end
|
170
|
+
|
171
|
+
it "should freeze lazy_attributes_module when freezing model class" do
|
172
|
+
@c.plugin :lazy_attributes, :blah
|
173
|
+
@c.freeze
|
174
|
+
@c.lazy_attributes_module.frozen?.must_equal true
|
175
|
+
end
|
170
176
|
end
|
@@ -2115,3 +2115,47 @@ describe "one_through_many eager loading methods" do
|
|
2115
2115
|
@c1.order(:artists__blah2, :artists__blah3).eager_graph(:tag).sql.must_equal 'SELECT artists.id, tag.id AS tag_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags AS tag ON (tag.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3, tag.blah__id, tag.blah__id DESC, blah.id DESC, blah.id, tag.album_id, tag.album_id DESC, 1, RANDOM(), b.a'
|
2116
2116
|
end
|
2117
2117
|
end
|
2118
|
+
|
2119
|
+
describe "Sequel::Model.finalize_associations" do
|
2120
|
+
before do
|
2121
|
+
class ::Item < Sequel::Model
|
2122
|
+
plugin :many_through_many
|
2123
|
+
many_through_many :items, [[:foos, :item1_id, :foo1_id], [:bars, :foo2_id, :item2_id]]
|
2124
|
+
one_through_many :item, [[:foos, :item1_id, :foo1_id], [:bars, :foo2_id, :item2_id]]
|
2125
|
+
finalize_associations
|
2126
|
+
end
|
2127
|
+
end
|
2128
|
+
after do
|
2129
|
+
Object.send(:remove_const, :Item)
|
2130
|
+
end
|
2131
|
+
|
2132
|
+
it "should finalize one_through_many associations" do
|
2133
|
+
r = Item.association_reflection(:item)
|
2134
|
+
r[:class].must_equal Item
|
2135
|
+
r[:_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) LIMIT 1"
|
2136
|
+
r[:associated_eager_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
|
2137
|
+
r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT foos.item1_id FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) WHERE (foos.item1_id IS NOT NULL)"
|
2138
|
+
r[:placeholder_loader].wont_be_nil
|
2139
|
+
r[:predicate_key].must_equal Sequel.qualify(:foos, :item1_id)
|
2140
|
+
r[:associated_key_table].must_equal :foos
|
2141
|
+
r[:edges].must_equal [{:table=>:foos, :left=>:id, :right=>:item1_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}, {:table=>:bars, :left=>:foo1_id, :right=>:foo2_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}]
|
2142
|
+
r[:final_edge].must_equal(:table=>:items, :left=>:item2_id, :right=>:id, :conditions=>nil, :join_type=>nil, :block=>nil)
|
2143
|
+
r[:final_reverse_edge].must_equal(:table=>:foos, :left=>:foo1_id, :right=>:foo2_id, :alias=>:foos)
|
2144
|
+
r[:reverse_edges].must_equal [{:table=>:bars, :left=>:item2_id, :right=>:id, :alias=>:bars}]
|
2145
|
+
end
|
2146
|
+
|
2147
|
+
it "should finalize many_through_many associations" do
|
2148
|
+
r = Item.association_reflection(:items)
|
2149
|
+
r[:class].must_equal Item
|
2150
|
+
r[:_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
|
2151
|
+
r[:associated_eager_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
|
2152
|
+
r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT foos.item1_id FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) WHERE (foos.item1_id IS NOT NULL)"
|
2153
|
+
r[:placeholder_loader].wont_be_nil
|
2154
|
+
r[:predicate_key].must_equal Sequel.qualify(:foos, :item1_id)
|
2155
|
+
r[:associated_key_table].must_equal :foos
|
2156
|
+
r[:edges].must_equal [{:table=>:foos, :left=>:id, :right=>:item1_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}, {:table=>:bars, :left=>:foo1_id, :right=>:foo2_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}]
|
2157
|
+
r[:final_edge].must_equal(:table=>:items, :left=>:item2_id, :right=>:id, :conditions=>nil, :join_type=>nil, :block=>nil)
|
2158
|
+
r[:final_reverse_edge].must_equal(:table=>:foos, :left=>:foo1_id, :right=>:foo2_id, :alias=>:foos)
|
2159
|
+
r[:reverse_edges].must_equal [{:table=>:bars, :left=>:item2_id, :right=>:id, :alias=>:bars}]
|
2160
|
+
end
|
2161
|
+
end
|