sequel 3.33.0 → 3.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -21,55 +21,55 @@ describe "AssociationDependencies plugin" do
21
21
  specify "should allow destroying associated many_to_one associated object" do
22
22
  @Album.add_association_dependencies :artist=>:destroy
23
23
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
24
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
24
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
25
25
  end
26
26
 
27
27
  specify "should allow deleting associated many_to_one associated object" do
28
28
  @Album.add_association_dependencies :artist=>:delete
29
29
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
30
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (artists.id = 2)']
30
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE (artists.id = 2)']
31
31
  end
32
32
 
33
33
  specify "should allow destroying associated one_to_one associated object" do
34
34
  @Artist.add_association_dependencies :first_album=>:destroy
35
35
  @Artist.load(:id=>2, :name=>'Ar').destroy
36
- MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (id = 2)']
36
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
37
37
  end
38
38
 
39
39
  specify "should allow deleting associated one_to_one associated object" do
40
40
  @Artist.add_association_dependencies :first_album=>:delete
41
41
  @Artist.load(:id=>2, :name=>'Ar').destroy
42
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
42
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
43
43
  end
44
44
 
45
45
  specify "should allow destroying associated one_to_many objects" do
46
46
  @Artist.add_association_dependencies :albums=>:destroy
47
47
  @Artist.load(:id=>2, :name=>'Ar').destroy
48
- MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (id = 2)']
48
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
49
49
  end
50
50
 
51
51
  specify "should allow deleting associated one_to_many objects" do
52
52
  @Artist.add_association_dependencies :albums=>:delete
53
53
  @Artist.load(:id=>2, :name=>'Ar').destroy
54
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM artists WHERE (id = 2)']
54
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM artists WHERE id = 2']
55
55
  end
56
56
 
57
57
  specify "should allow nullifying associated one_to_one objects" do
58
58
  @Artist.add_association_dependencies :first_album=>:nullify
59
59
  @Artist.load(:id=>2, :name=>'Ar').destroy
60
- MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
60
+ MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
61
61
  end
62
62
 
63
63
  specify "should allow nullifying associated one_to_many objects" do
64
64
  @Artist.add_association_dependencies :albums=>:nullify
65
65
  @Artist.load(:id=>2, :name=>'Ar').destroy
66
- MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE (artist_id = 2)', 'DELETE FROM artists WHERE (id = 2)']
66
+ MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE (artist_id = 2)', 'DELETE FROM artists WHERE id = 2']
67
67
  end
68
68
 
69
69
  specify "should allow nullifying associated many_to_many associations" do
70
70
  @Artist.add_association_dependencies :other_artists=>:nullify
71
71
  @Artist.load(:id=>2, :name=>'Ar').destroy
72
- MODEL_DB.sqls.should == ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE (id = 2)']
72
+ MODEL_DB.sqls.should == ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE id = 2']
73
73
  end
74
74
 
75
75
  specify "should raise an error if attempting to nullify a many_to_one association" do
@@ -97,21 +97,21 @@ describe "AssociationDependencies plugin" do
97
97
  specify "should allow specifying association dependencies in the plugin call" do
98
98
  @Album.plugin :association_dependencies, :artist=>:destroy
99
99
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
100
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
100
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
101
101
  end
102
102
 
103
103
  specify "should work with subclasses" do
104
104
  c = Class.new(@Album)
105
105
  c.add_association_dependencies :artist=>:destroy
106
106
  c.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
107
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
107
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
108
108
 
109
109
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
110
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)']
110
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1']
111
111
 
112
112
  @Album.add_association_dependencies :artist=>:destroy
113
113
  c2 = Class.new(@Album)
114
114
  c2.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
115
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
115
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
116
116
  end
117
117
  end
@@ -82,7 +82,7 @@ describe Sequel::Model, "caching" do
82
82
  Class.new(@c).cache_ttl.should == 1234
83
83
  end
84
84
 
85
- it "should generate a cache key appropriate to the class" do
85
+ it "should generate a cache key appropriate to the class via the Model#cache_key" do
86
86
  m = @c.new
87
87
  m.values[:id] = 1
88
88
  m.cache_key.should == "#{m.class}:1"
@@ -114,6 +114,11 @@ describe Sequel::Model, "caching" do
114
114
  m.values[:b] = 789
115
115
  m.cache_key.should == "#{m.class}:123,789,456"
116
116
  end
117
+
118
+ it "should generate a cache key via the Model.cache_key method" do
119
+ @c.cache_key(1).should == "#{@c}:1"
120
+ @c.cache_key([1, 2]).should == "#{@c}:1,2"
121
+ end
117
122
 
118
123
  it "should raise error if attempting to generate cache_key and primary key value is null" do
119
124
  m = @c.new
@@ -156,6 +161,11 @@ describe Sequel::Model, "caching" do
156
161
  m2.should == m
157
162
  m2.values.should == {:name=>"sharon", :id=>1}
158
163
  end
164
+
165
+ it "should handle lookups by nil primary keys" do
166
+ @c[nil].should == nil
167
+ @c.db.sqls.should == []
168
+ end
159
169
 
160
170
  it "should delete the cache when writing to the database" do
161
171
  m = @c[1]
@@ -178,13 +188,13 @@ describe Sequel::Model, "caching" do
178
188
  @cache[m.cache_key].should == m
179
189
  m.delete
180
190
  @cache.has_key?(m.cache_key).should be_false
181
- @c.db.sqls.should == ["SELECT * FROM items WHERE id = 1", "DELETE FROM items WHERE (id = 1)"]
191
+ @c.db.sqls.should == ["SELECT * FROM items WHERE id = 1", "DELETE FROM items WHERE id = 1"]
182
192
 
183
193
  m = @c2[1]
184
194
  @cache[m.cache_key].should == m
185
195
  m.delete
186
196
  @cache.has_key?(m.cache_key).should be_false
187
- @c.db.sqls.should == ["SELECT * FROM items WHERE id = 1", "DELETE FROM items WHERE (id = 1)"]
197
+ @c.db.sqls.should == ["SELECT * FROM items WHERE id = 1", "DELETE FROM items WHERE id = 1"]
188
198
  end
189
199
 
190
200
  it "should support #[] as a shortcut to #find with hash" do
@@ -220,4 +230,17 @@ describe Sequel::Model, "caching" do
220
230
  it "should rescue an exception if cache_store is memcached and ignore_exception is enabled" do
221
231
  @c4[1].values.should == {:name => 'sharon', :id => 1}
222
232
  end
233
+
234
+ it "should support Model.cache_get_pk for getting a value from the cache by primary key" do
235
+ @c.cache_get_pk(1).should == nil
236
+ m = @c[1]
237
+ @c.cache_get_pk(1).should == m
238
+ end
239
+
240
+ it "should support Model.cache_delete_pk for removing a value from the cache by primary key" do
241
+ @c[1]
242
+ @c.cache_get_pk(1).should_not == nil
243
+ @c.cache_delete_pk(1).should == nil
244
+ @c.cache_get_pk(1).should == nil
245
+ end
223
246
  end
@@ -130,10 +130,10 @@ describe "class_table_inheritance plugin" do
130
130
  end
131
131
 
132
132
  it "should lazily load attributes for columns in subclass tables" do
133
- Manager.dataset._fetch = {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2}
133
+ Manager.instance_dataset._fetch = Manager.dataset._fetch = {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2}
134
134
  m = Manager[1]
135
135
  @db.sqls.should == ['SELECT * FROM employees INNER JOIN managers USING (id) WHERE (id = 1) LIMIT 1']
136
- Executive.dataset._fetch = {:num_managers=>3}
136
+ Executive.instance_dataset._fetch = Executive.dataset._fetch = {:num_managers=>3}
137
137
  m.num_managers.should == 3
138
138
  @db.sqls.should == ['SELECT num_managers FROM employees INNER JOIN managers USING (id) INNER JOIN executives USING (id) WHERE (id = 1) LIMIT 1']
139
139
  m.values.should == {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2, :num_managers=>3}
@@ -1,5 +1,23 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
+ describe "Sequel core extensions" do
4
+ specify "should not be used inside Sequel, to insure Sequel works without them" do
5
+ usage = []
6
+ match_re = /(\.(all_two_pairs\?|sql_value_list|sql_array|sql_expr|sql_negate|sql_or|sql_string_join|lit|to_sequel_blob|case|sql_expr_if_all_two_pairs)\W)|:\w+\.(qualify|identifier|as|cast|asc|desc|sql_subscript|\*|sql_function)\W/
7
+ comment_re = /^\s*#|# core_sql/
8
+ Dir['lib/sequel/**/*.rb'].each do |f|
9
+ lines = File.read(f).split("\n").grep(match_re).delete_if{|l| l =~ comment_re}
10
+ usage << [f, lines] unless lines.empty?
11
+ end
12
+ puts usage unless usage.empty?
13
+ usage.should be_empty
14
+ end
15
+
16
+ specify "should have Sequel.core_extensions? be true if enabled" do
17
+ Sequel.core_extensions?.should be_true
18
+ end
19
+ end
20
+
3
21
  describe "Array#all_two_pairs?" do
4
22
  specify "should return false if empty" do
5
23
  [].all_two_pairs?.should == false
@@ -285,41 +303,11 @@ describe "Symbol" do
285
303
  @oe.class.should == Sequel::SQL::OrderedExpression
286
304
  @oe.descending.should == false
287
305
  end
288
- end
289
-
290
- describe "Dataset#literal" do
291
- before do
292
- @ds = Sequel::Database.new.dataset
293
- end
294
-
295
- specify "should convert qualified symbol notation into dot notation" do
296
- @ds.literal(:abc__def).should == 'abc.def'
297
- end
298
-
299
- specify "should convert AS symbol notation into SQL AS notation" do
300
- @ds.literal(:xyz___x).should == 'xyz AS x'
301
- @ds.literal(:abc__def___x).should == 'abc.def AS x'
302
- end
303
-
304
- specify "should support names with digits" do
305
- @ds.literal(:abc2).should == 'abc2'
306
- @ds.literal(:xx__yy3).should == 'xx.yy3'
307
- @ds.literal(:ab34__temp3_4ax).should == 'ab34.temp3_4ax'
308
- @ds.literal(:x1___y2).should == 'x1 AS y2'
309
- @ds.literal(:abc2__def3___ggg4).should == 'abc2.def3 AS ggg4'
310
- end
311
-
312
- specify "should support upper case and lower case" do
313
- @ds.literal(:ABC).should == 'ABC'
314
- @ds.literal(:Zvashtoy__aBcD).should == 'Zvashtoy.aBcD'
315
- end
316
306
 
317
- specify "should support spaces inside column names" do
318
- @ds.quote_identifiers = true
319
- @ds.literal(:"AB C").should == '"AB C"'
320
- @ds.literal(:"Zvas htoy__aB cD").should == '"Zvas htoy"."aB cD"'
321
- @ds.literal(:"aB cD___XX XX").should == '"aB cD" AS "XX XX"'
322
- @ds.literal(:"Zva shtoy__aB cD___XX XX").should == '"Zva shtoy"."aB cD" AS "XX XX"'
307
+ specify "should work correctly with objects" do
308
+ o = Object.new
309
+ def o.sql_literal(ds) "(foo)" end
310
+ @ds.literal(:column.qualify(o)).should == '(foo)."COLUMN"'
323
311
  end
324
312
  end
325
313
 
@@ -341,6 +329,7 @@ describe "Symbol" do
341
329
  @ds.literal(:abc.sql_subscript(1)).should == "abc[1]"
342
330
  @ds.literal(:abc__def.sql_subscript(1)).should == "abc.def[1]"
343
331
  @ds.literal(:abc.sql_subscript(1)|2).should == "abc[1, 2]"
332
+ @ds.literal(:abc.sql_subscript(1)[2]).should == "abc[1][2]"
344
333
  end
345
334
 
346
335
  specify "should support cast_numeric and cast_string" do
@@ -380,63 +369,3 @@ describe "Symbol" do
380
369
  :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
381
370
  end
382
371
  end
383
-
384
- describe "Sequel::SQL::Function#==" do
385
- specify "should be true for functions with the same name and arguments, false otherwise" do
386
- a = :date.sql_function(:t)
387
- b = :date.sql_function(:t)
388
- a.should == b
389
- (a == b).should == true
390
- c = :date.sql_function(:c)
391
- a.should_not == c
392
- (a == c).should == false
393
- d = :time.sql_function(:c)
394
- a.should_not == d
395
- c.should_not == d
396
- (a == d).should == false
397
- (c == d).should == false
398
- end
399
- end
400
-
401
- describe "Sequel::SQL::OrderedExpression" do
402
- specify "should #desc" do
403
- @oe = :column.asc
404
- @oe.descending.should == false
405
- @oe.desc.descending.should == true
406
- end
407
-
408
- specify "should #asc" do
409
- @oe = :column.desc
410
- @oe.descending.should == true
411
- @oe.asc.descending.should == false
412
- end
413
-
414
- specify "should #invert" do
415
- @oe = :column.desc
416
- @oe.invert.descending.should == false
417
- @oe.invert.invert.descending.should == true
418
- end
419
- end
420
-
421
- describe "Expression" do
422
- specify "should consider objects == only if they have the same attributes" do
423
- :column.qualify(:table).cast(:type).*(:numeric_column).asc.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc
424
- :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc
425
-
426
- :column.qualify(:table).cast(:type).*(:numeric_column).asc.should eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
427
- :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
428
- end
429
-
430
- specify "should use the same hash value for objects that have the same attributes" do
431
- :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
432
- :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
433
-
434
- h = {}
435
- a = :column.qualify(:table).cast(:type).*(:numeric_column).asc
436
- b = :column.qualify(:table).cast(:type).*(:numeric_column).asc
437
- h[a] = 1
438
- h[b] = 2
439
- h[a].should == 2
440
- h[b].should == 2
441
- end
442
- end
@@ -74,12 +74,13 @@ describe "force_encoding plugin" do
74
74
 
75
75
  specify "should work when saving new model instances" do
76
76
  o = @c.new
77
- @c.dataset = ds = MODEL_DB[:a]
77
+ ds = MODEL_DB[:a]
78
78
  def ds.first
79
79
  s = 'blah'
80
80
  s.force_encoding('US-ASCII')
81
81
  {:id=>1, :x=>s}
82
82
  end
83
+ @c.dataset = ds
83
84
  o.save
84
85
  o.x.should == 'blah'
85
86
  o.x.encoding.should == @e1
@@ -87,12 +88,13 @@ describe "force_encoding plugin" do
87
88
 
88
89
  specify "should work when refreshing model instances" do
89
90
  o = @c.load(:id=>1, :x=>'as')
90
- @c.dataset = ds = MODEL_DB[:a]
91
+ ds = MODEL_DB[:a]
91
92
  def ds.first
92
93
  s = 'blah'
93
94
  s.force_encoding('US-ASCII')
94
95
  {:id=>1, :x=>s}
95
96
  end
97
+ @c.dataset = ds
96
98
  o.refresh
97
99
  o.x.should == 'blah'
98
100
  o.x.encoding.should == @e1
@@ -308,7 +308,7 @@ describe "Model#before_destroy && Model#after_destroy" do
308
308
  @c.before_destroy {MODEL_DB << "BLAH before"}
309
309
  m = @c.load(:id => 2233)
310
310
  m.destroy
311
- MODEL_DB.sqls.should == ['BLAH before', "DELETE FROM items WHERE (id = 2233)", 'BLAH after']
311
+ MODEL_DB.sqls.should == ['BLAH before', "DELETE FROM items WHERE id = 2233", 'BLAH after']
312
312
  end
313
313
 
314
314
  specify "#destroy should cancel the destroy and raise an error if before_destroy returns false and raise_on_save_failure is true" do
@@ -402,7 +402,7 @@ end
402
402
 
403
403
  describe "Model#add_hook_type" do
404
404
  before do
405
- class Foo < Sequel::Model(:items)
405
+ class ::Foo < Sequel::Model(:items)
406
406
  add_hook_type :before_bar, :after_bar
407
407
 
408
408
  def bar
@@ -413,6 +413,9 @@ describe "Model#add_hook_type" do
413
413
  end
414
414
  @f = Class.new(Foo)
415
415
  end
416
+ after do
417
+ Object.send(:remove_const, :Foo)
418
+ end
416
419
 
417
420
  specify "should have before_bar and after_bar class methods" do
418
421
  @f.should respond_to(:before_bar)
@@ -218,6 +218,23 @@ describe "Sequel::Plugins::IdentityMap" do
218
218
  end
219
219
  end
220
220
 
221
+ it "should not use the identity map as a lookup cache if a dynamic callback is used" do
222
+ @c1.many_to_one :artist, :class=>@c2
223
+ @c.with_identity_map do
224
+ MODEL_DB.sqls.length.should == 0
225
+ o = @c1.load(:id=>1, :artist_id=>2)
226
+ a = o.artist
227
+ a.should be_a_kind_of(@c2)
228
+ MODEL_DB.sqls.length.should == 1
229
+ o = @c1.load(:id=>2, :artist_id=>2)
230
+ o.artist.should == a
231
+ MODEL_DB.sqls.length.should == 0
232
+ o = @c1.load(:id=>3, :artist_id=>3)
233
+ o.artist.should_not == a
234
+ MODEL_DB.sqls.length.should == 1
235
+ end
236
+ end
237
+
221
238
  it "should not override custom :eager_loaders for many_to_many associations" do
222
239
  @c1.columns :id
223
240
  @c2.columns :id
@@ -21,7 +21,7 @@ describe "instance_filters plugin" do
21
21
 
22
22
  specify "should raise an error when destroying a stale record" do
23
23
  @p.destroy
24
- MODEL_DB.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
24
+ MODEL_DB.sqls.should == ["DELETE FROM people WHERE id = 1"]
25
25
  @p.instance_filter(:name=>'Jim')
26
26
  @p.this.numrows = 0
27
27
  proc{@p.destroy}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
@@ -11,7 +11,7 @@ describe "Sequel::Plugins::LazyAttributes" do
11
11
  meta_def(:columns){[:id, :name]}
12
12
  lazy_attributes :name
13
13
  meta_def(:columns){[:id]}
14
- dataset._fetch = proc do |sql|
14
+ instance_dataset._fetch = dataset._fetch = proc do |sql|
15
15
  if sql !~ /WHERE/
16
16
  if sql =~ /name/
17
17
  [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
@@ -133,7 +133,7 @@ describe "Sequel::Plugins::LazyAttributes" do
133
133
 
134
134
  it "should work with the serialization plugin" do
135
135
  @c.plugin :serialization, :yaml, :name
136
- @ds._fetch = [[{:id=>1}, {:id=>2}], [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}], [{:id=>1}], [{:name=>"--- 3\n"}]]
136
+ @c.instance_dataset._fetch = @ds._fetch = [[{:id=>1}, {:id=>2}], [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}], [{:id=>1}], [{:name=>"--- 3\n"}]]
137
137
  @c.with_identity_map do
138
138
  ms = @ds.all
139
139
  ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
@@ -76,8 +76,8 @@ describe "List plugin" do
76
76
  end
77
77
 
78
78
  it "should have position field set to max+1 when creating if not already set" do
79
- @c.dataset._fetch = [[{:pos=>nil}], [{:id=>1, :position=>1}], [{:pos=>1}], [{:id=>2, :position=>2}]]
80
- @c.dataset.autoid = 1
79
+ @c.instance_dataset._fetch = @c.dataset._fetch = [[{:pos=>nil}], [{:id=>1, :position=>1}], [{:pos=>1}], [{:id=>2, :position=>2}]]
80
+ @c.instance_dataset.autoid = @c.dataset.autoid = 1
81
81
  @c.create.values.should == {:id=>1, :position=>1}
82
82
  @c.create.values.should == {:id=>2, :position=>2}
83
83
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
@@ -89,8 +89,8 @@ describe "List plugin" do
89
89
  end
90
90
 
91
91
  it "should have position field set to max+1 in scope when creating if not already set" do
92
- @sc.dataset._fetch = [[{:pos=>nil}], [{:id=>1, :scope_id=>1, :position=>1}], [{:pos=>1}], [{:id=>2, :scope_id=>1, :position=>2}], [{:pos=>nil}], [{:id=>3, :scope_id=>2, :position=>1}]]
93
- @sc.dataset.autoid = 1
92
+ @sc.instance_dataset._fetch = @sc.dataset._fetch = [[{:pos=>nil}], [{:id=>1, :scope_id=>1, :position=>1}], [{:pos=>1}], [{:id=>2, :scope_id=>1, :position=>2}], [{:pos=>nil}], [{:id=>3, :scope_id=>2, :position=>1}]]
93
+ @sc.instance_dataset.autoid = @sc.dataset.autoid = 1
94
94
  @sc.create(:scope_id=>1).values.should == {:id=>1, :scope_id=>1, :position=>1}
95
95
  @sc.create(:scope_id=>1).values.should == {:id=>2, :scope_id=>1, :position=>2}
96
96
  @sc.create(:scope_id=>2).values.should == {:id=>3, :scope_id=>2, :position=>1}
@@ -0,0 +1,140 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::ManyToOnePkLookup" do
4
+ before do
5
+ class ::LookupModel < ::Sequel::Model
6
+ plugin :many_to_one_pk_lookup
7
+ columns :id, :caching_model_id, :caching_model_id2
8
+ many_to_one :caching_model
9
+ many_to_one :caching_model2, :key=>[:caching_model_id, :caching_model_id2], :class=>:CachingModel
10
+ end
11
+ @c = LookupModel
12
+
13
+ class ::CachingModel < Sequel::Model
14
+ columns :id, :id2
15
+ end
16
+ @cc = CachingModel
17
+
18
+ @db = MODEL_DB
19
+ end
20
+ after do
21
+ Object.send(:remove_const, :CachingModel)
22
+ Object.send(:remove_const, :LookupModel)
23
+ end
24
+
25
+ shared_examples_for "many_to_one_pk_lookup with composite keys" do
26
+ it "should use a simple primary key lookup when retrieving many_to_one associated records" do
27
+ @db.sqls.should == []
28
+ @c.load(:id=>3, :caching_model_id=>1, :caching_model_id2=>2).caching_model2.should equal(@cm12)
29
+ @c.load(:id=>3, :caching_model_id=>2, :caching_model_id2=>1).caching_model2.should equal(@cm21)
30
+ @db.sqls.should == []
31
+ @c.load(:id=>4, :caching_model_id=>2, :caching_model_id2=>2).caching_model2
32
+ @db.sqls.should_not == []
33
+ end
34
+ end
35
+
36
+ shared_examples_for "many_to_one_pk_lookup" do
37
+ it "should use a simple primary key lookup when retrieving many_to_one associated records via a composite key" do
38
+ @cc.set_primary_key([:id, :id2])
39
+ @db.sqls.should == []
40
+ @c.load(:id=>3, :caching_model_id=>1).caching_model.should equal(@cm1)
41
+ @c.load(:id=>4, :caching_model_id=>2).caching_model.should equal(@cm2)
42
+ @db.sqls.should == []
43
+ @c.load(:id=>4, :caching_model_id=>3).caching_model
44
+ @db.sqls.should_not == []
45
+ end
46
+
47
+ it "should not use a simple primary key lookup if the assocation has a nil :key option" do
48
+ @c.many_to_one :caching_model, :key=>nil, :dataset=>proc{CachingModel.filter(:caching_model_id=>caching_model_id)}
49
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
50
+ @db.sqls.should_not == []
51
+ end
52
+
53
+ it "should not use a simple primary key lookup if the assocation has a nil :key option" do
54
+ @c.many_to_one :caching_model, :many_to_one_pk_lookup=>false
55
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
56
+ @db.sqls.should_not == []
57
+ end
58
+
59
+ it "should not use a simple primary key lookup if the assocation's :primary_key option doesn't match the primary key of the associated class" do
60
+ @c.many_to_one :caching_model, :primary_key=>:id2
61
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
62
+ @db.sqls.should_not == []
63
+ end
64
+
65
+ it "should not use a simple primary key lookup if the prepared_statements_associations method is being used" do
66
+ c2 = Class.new(Sequel::Model(:not_caching_model))
67
+ c2.dataset._fetch = {:id=>1}
68
+ c = Class.new(Sequel::Model(:lookup_model))
69
+ c.class_eval do
70
+ plugin :prepared_statements_associations
71
+ plugin :many_to_one_pk_lookup
72
+ columns :id, :caching_model_id
73
+ many_to_one :caching_model, :class=>c2
74
+ end
75
+ c.load(:id=>3, :caching_model_id=>1).caching_model.should == c2.load(:id=>1)
76
+ @db.sqls.should_not == []
77
+ end
78
+
79
+ it "should use a simple primary key lookup if the prepared_statements_associations method is being used but associated model also uses caching" do
80
+ c = Class.new(Sequel::Model(:lookup_model))
81
+ c.class_eval do
82
+ plugin :prepared_statements_associations
83
+ plugin :many_to_one_pk_lookup
84
+ columns :id, :caching_model_id
85
+ many_to_one :caching_model
86
+ end
87
+ c.load(:id=>3, :caching_model_id=>1).caching_model.should equal(@cm1)
88
+ @db.sqls.should == []
89
+ end
90
+ end
91
+
92
+ describe "With caching plugin" do
93
+ before do
94
+ @cache_class = Class.new(Hash) do
95
+ attr_accessor :ttl
96
+ def set(k, v, ttl); self[k] = v; @ttl = ttl; end
97
+ def get(k); self[k]; end
98
+ end
99
+ cache = @cache_class.new
100
+ @cache = cache
101
+
102
+ @cc.plugin :caching, @cache
103
+ @cc.dataset._fetch = {:id=>1}
104
+ @cm1 = @cc[1]
105
+ @cm2 = @cc[2]
106
+ @cm12 = @cc[1, 2]
107
+ @cm21 = @cc[2, 1]
108
+
109
+ @db.reset
110
+ end
111
+
112
+ it_should_behave_like "many_to_one_pk_lookup"
113
+ it_should_behave_like "many_to_one_pk_lookup with composite keys"
114
+ end
115
+
116
+ describe "With static_cache plugin with single key" do
117
+ before do
118
+ @cc.dataset._fetch = [{:id=>1}, {:id=>2}]
119
+ @cc.plugin :static_cache
120
+ @cm1 = @cc[1]
121
+ @cm2 = @cc[2]
122
+ @db.reset
123
+ end
124
+
125
+ it_should_behave_like "many_to_one_pk_lookup"
126
+ end
127
+
128
+ describe "With static_cache plugin with composite key" do
129
+ before do
130
+ @cc.set_primary_key([:id, :id2])
131
+ @cc.dataset._fetch = [{:id=>1, :id2=>2}, {:id=>2, :id2=>1}]
132
+ @cc.plugin :static_cache
133
+ @cm12 = @cc[[1, 2]]
134
+ @cm21 = @cc[[2, 1]]
135
+ @db.reset
136
+ end
137
+
138
+ it_should_behave_like "many_to_one_pk_lookup with composite keys"
139
+ end
140
+ end