viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,808 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ CONNECTION_POOL_DEFAULTS = {:pool_timeout=>5, :pool_sleep_time=>0.001, :max_connections=>4}
3
+
4
+ context "An empty ConnectionPool" do
5
+ before do
6
+ @cpool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS){}
7
+ end
8
+
9
+ specify "should have no available connections" do
10
+ @cpool.available_connections.should == []
11
+ end
12
+
13
+ specify "should have no allocated connections" do
14
+ @cpool.allocated.should == {}
15
+ end
16
+
17
+ specify "should have a created_count of zero" do
18
+ @cpool.created_count.should == 0
19
+ end
20
+ end
21
+
22
+ context "ConnectionPool options" do
23
+ specify "should support string option values" do
24
+ cpool = Sequel::ConnectionPool.get_pool({:max_connections=>'5', :pool_timeout=>'3', :pool_sleep_time=>'0.01'}){}
25
+ cpool.max_size.should == 5
26
+ cpool.instance_variable_get(:@timeout).should == 3
27
+ cpool.instance_variable_get(:@sleep_time).should == 0.01
28
+ end
29
+
30
+ specify "should raise an error unless size is positive" do
31
+ lambda{Sequel::ConnectionPool.get_pool(:max_connections=>0)}.should raise_error(Sequel::Error)
32
+ lambda{Sequel::ConnectionPool.get_pool(:max_connections=>-10)}.should raise_error(Sequel::Error)
33
+ lambda{Sequel::ConnectionPool.get_pool(:max_connections=>'-10')}.should raise_error(Sequel::Error)
34
+ lambda{Sequel::ConnectionPool.get_pool(:max_connections=>'0')}.should raise_error(Sequel::Error)
35
+ end
36
+ end
37
+
38
+ context "A connection pool handling connections" do
39
+ before do
40
+ @max_size = 2
41
+ @cpool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| @max_size=3}, :max_connections=>@max_size)) {:got_connection}
42
+ end
43
+
44
+ specify "#hold should increment #created_count" do
45
+ @cpool.hold do
46
+ @cpool.created_count.should == 1
47
+ @cpool.hold {@cpool.hold {@cpool.created_count.should == 1}}
48
+ Thread.new{@cpool.hold {@cpool.created_count.should == 2}}.join
49
+ end
50
+ end
51
+
52
+ specify "#hold should add the connection to the #allocated array" do
53
+ @cpool.hold do
54
+ @cpool.allocated.size.should == 1
55
+
56
+ @cpool.allocated.should == {Thread.current=>:got_connection}
57
+ end
58
+ end
59
+
60
+ specify "#hold should yield a new connection" do
61
+ @cpool.hold {|conn| conn.should == :got_connection}
62
+ end
63
+
64
+ specify "a connection should be de-allocated after it has been used in #hold" do
65
+ @cpool.hold {}
66
+ @cpool.allocated.size.should == 0
67
+ end
68
+
69
+ specify "#hold should return the value of its block" do
70
+ @cpool.hold {:block_return}.should == :block_return
71
+ end
72
+
73
+ if RUBY_VERSION < '1.9.0' and (!defined?(RUBY_ENGINE) or RUBY_ENGINE != 'jruby')
74
+ specify "#hold should remove dead threads from the pool if it reaches its max_size" do
75
+ Thread.new{@cpool.hold{Thread.current.exit!}}.join
76
+ @cpool.allocated.keys.map{|t| t.alive?}.should == [false]
77
+
78
+ Thread.new{@cpool.hold{Thread.current.exit!}}.join
79
+ @cpool.allocated.keys.map{|t| t.alive?}.should == [false, false]
80
+
81
+ Thread.new{@cpool.hold{}}.join
82
+ @cpool.allocated.should == {}
83
+ end
84
+ end
85
+
86
+ specify "#make_new should not make more than max_size connections" do
87
+ 50.times{Thread.new{@cpool.hold{sleep 0.001}}}
88
+ @cpool.created_count.should <= @max_size
89
+ end
90
+
91
+ specify ":disconnection_proc option should set the disconnection proc to use" do
92
+ @max_size.should == 2
93
+ proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
94
+ @max_size.should == 3
95
+ end
96
+
97
+ specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
98
+ @cpool.created_count.should == 0
99
+ @cpool.hold{Thread.new{@cpool.hold{}}; sleep 0.01}
100
+ @cpool.created_count.should == 2
101
+ proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
102
+ @cpool.created_count.should == 1
103
+ proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
104
+ @cpool.created_count.should == 0
105
+ proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
106
+ @cpool.created_count.should == 0
107
+ end
108
+ end
109
+
110
+ context "A connection pool handling connection errors" do
111
+ specify "#hold should raise a Sequel::DatabaseConnectionError if an exception is raised by the connection_proc" do
112
+ cpool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS){raise Interrupt}
113
+ proc{cpool.hold{:block_return}}.should raise_error(Sequel::DatabaseConnectionError)
114
+ cpool.created_count.should == 0
115
+ end
116
+
117
+ specify "#hold should raise a Sequel::DatabaseConnectionError if nil is returned by the connection_proc" do
118
+ cpool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS){nil}
119
+ proc{cpool.hold{:block_return}}.should raise_error(Sequel::DatabaseConnectionError)
120
+ cpool.created_count.should == 0
121
+ end
122
+ end
123
+
124
+ class DummyConnection
125
+ @@value = 0
126
+ def initialize
127
+ @@value += 1
128
+ end
129
+
130
+ def value
131
+ @@value
132
+ end
133
+ end
134
+
135
+ context "ConnectionPool#hold" do
136
+ before do
137
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
138
+ end
139
+
140
+ specify "should pass the result of the connection maker proc to the supplied block" do
141
+ res = nil
142
+ @pool.hold {|c| res = c}
143
+ res.should be_a_kind_of(DummyConnection)
144
+ res.value.should == 1
145
+ @pool.hold {|c| res = c}
146
+ res.should be_a_kind_of(DummyConnection)
147
+ res.value.should == 1 # the connection maker is invoked only once
148
+ end
149
+
150
+ specify "should be re-entrant by the same thread" do
151
+ cc = nil
152
+ @pool.hold {|c| @pool.hold {|c| @pool.hold {|c| cc = c}}}
153
+ cc.should be_a_kind_of(DummyConnection)
154
+ end
155
+
156
+ specify "should catch exceptions and reraise them" do
157
+ proc {@pool.hold {|c| c.foobar}}.should raise_error(NoMethodError)
158
+ end
159
+ end
160
+
161
+ context "A connection pool with a max size of 1" do
162
+ before do
163
+ @invoked_count = 0
164
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>1)) {@invoked_count += 1; 'herro'}
165
+ end
166
+
167
+ specify "should let only one thread access the connection at any time" do
168
+ cc,c1, c2 = nil
169
+
170
+ t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; while c == 'herro';sleep 0.01;end}}
171
+ sleep 0.02
172
+ cc.should == 'herro'
173
+ c1.should == 'herro'
174
+
175
+ t2 = Thread.new {@pool.hold {|c| c2 = c.dup; while c == 'hello';sleep 0.01;end}}
176
+ sleep 0.02
177
+
178
+ # connection held by t1
179
+ t1.should be_alive
180
+ t2.should be_alive
181
+
182
+ cc.should == 'herro'
183
+ c1.should == 'herro'
184
+ c2.should be_nil
185
+
186
+ @pool.available_connections.should be_empty
187
+ @pool.allocated.should == {t1=>cc}
188
+
189
+ cc.gsub!('rr', 'll')
190
+ sleep 0.05
191
+
192
+ # connection held by t2
193
+ t1.should_not be_alive
194
+ t2.should be_alive
195
+
196
+ c2.should == 'hello'
197
+
198
+ @pool.available_connections.should be_empty
199
+ @pool.allocated.should == {t2=>cc}
200
+
201
+ cc.gsub!('ll', 'rr')
202
+ sleep 0.05
203
+
204
+ #connection released
205
+ t2.should_not be_alive
206
+
207
+ cc.should == 'herro'
208
+
209
+ @invoked_count.should == 1
210
+ @pool.size.should == 1
211
+ @pool.available_connections.should == [cc]
212
+ @pool.allocated.should be_empty
213
+ end
214
+
215
+ specify "should let the same thread reenter #hold" do
216
+ c1, c2, c3 = nil
217
+ @pool.hold do |c|
218
+ c1 = c
219
+ @pool.hold do |c|
220
+ c2 = c
221
+ @pool.hold do |c|
222
+ c3 = c
223
+ end
224
+ end
225
+ end
226
+ c1.should == 'herro'
227
+ c2.should == 'herro'
228
+ c3.should == 'herro'
229
+
230
+ @invoked_count.should == 1
231
+ @pool.size.should == 1
232
+ @pool.available_connections.size.should == 1
233
+ @pool.allocated.should be_empty
234
+ end
235
+ end
236
+
237
+ shared_examples_for "A threaded connection pool" do
238
+ specify "should let five threads simultaneously access separate connections" do
239
+ cc = {}
240
+ threads = []
241
+ stop = nil
242
+
243
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.01;end}}; sleep 0.01}
244
+ sleep 0.02
245
+ threads.each {|t| t.should be_alive}
246
+ cc.size.should == 5
247
+ @invoked_count.should == 5
248
+ @pool.size.should == 5
249
+ @pool.available_connections.should be_empty
250
+ i = 0
251
+ h = {}
252
+ threads.each{|t| h[t] = (i+=1)}
253
+ @pool.allocated.should == h
254
+
255
+ threads[0].raise "your'e dead"
256
+ sleep 0.01
257
+ threads[3].raise "your'e dead too"
258
+
259
+ sleep 0.01
260
+
261
+ @pool.available_connections.should == [1, 4]
262
+ @pool.allocated.should == {threads[1]=>2, threads[2]=>3, threads[4]=>5}
263
+
264
+ stop = true
265
+ sleep 0.02
266
+
267
+ @pool.available_connections.size.should == 5
268
+ @pool.allocated.should be_empty
269
+ end
270
+
271
+ specify "should block threads until a connection becomes available" do
272
+ cc = {}
273
+ threads = []
274
+ stop = nil
275
+
276
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.01;end}}; sleep 0.01}
277
+ sleep 0.02
278
+ threads.each {|t| t.should be_alive}
279
+ @pool.available_connections.should be_empty
280
+
281
+ 3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c}}}
282
+
283
+ sleep 0.02
284
+ threads[5].should be_alive
285
+ threads[6].should be_alive
286
+ threads[7].should be_alive
287
+ cc.size.should == 5
288
+ cc[5].should be_nil
289
+ cc[6].should be_nil
290
+ cc[7].should be_nil
291
+
292
+ stop = true
293
+ sleep 0.05
294
+
295
+ threads.each {|t| t.should_not be_alive}
296
+
297
+ @pool.size.should == 5
298
+ @invoked_count.should == 5
299
+ @pool.available_connections.size.should == 5
300
+ @pool.allocated.should be_empty
301
+ end
302
+ end
303
+
304
+ context "Threaded Unsharded Connection Pool" do
305
+ before do
306
+ @invoked_count = 0
307
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {@invoked_count += 1}
308
+ end
309
+
310
+ it_should_behave_like "A threaded connection pool"
311
+ end
312
+
313
+ context "Threaded Sharded Connection Pool" do
314
+ before do
315
+ @invoked_count = 0
316
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5, :servers=>{})) {@invoked_count += 1}
317
+ end
318
+
319
+ it_should_behave_like "A threaded connection pool"
320
+ end
321
+
322
+ context "ConnectionPool#disconnect" do
323
+ before do
324
+ @count = 0
325
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5, :servers=>{})) {{:id => @count += 1}}
326
+ end
327
+
328
+ specify "should invoke the given block for each available connection" do
329
+ threads = []
330
+ stop = nil
331
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
332
+ while @pool.size < 5
333
+ sleep 0.02
334
+ end
335
+ stop = true
336
+ sleep 0.1
337
+ threads.each {|t| t.join}
338
+
339
+ @pool.size.should == 5
340
+ @pool.available_connections.size.should == 5
341
+ @pool.available_connections.each {|c| c[:id].should_not be_nil}
342
+ conns = []
343
+ @pool.disconnect {|c| conns << c}
344
+ conns.size.should == 5
345
+ end
346
+
347
+ specify "should remove all available connections" do
348
+ threads = []
349
+ stop = nil
350
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
351
+ while @pool.size < 5
352
+ sleep 0.02
353
+ end
354
+ stop = true
355
+ sleep 0.1
356
+ threads.each {|t| t.join}
357
+
358
+ @pool.size.should == 5
359
+ @pool.disconnect
360
+ @pool.size.should == 0
361
+ end
362
+
363
+ specify "should disconnect connections in use as soon as they are no longer in use" do
364
+ threads = []
365
+ stop = nil
366
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
367
+ while @pool.size < 5
368
+ sleep 0.02
369
+ end
370
+ stop = true
371
+ sleep 0.1
372
+ threads.each {|t| t.join}
373
+
374
+ @pool.size.should == 5
375
+
376
+ @pool.hold do |conn|
377
+ @pool.available_connections.size.should == 4
378
+ @pool.available_connections.each {|c| c.should_not be(conn)}
379
+ conns = []
380
+ @pool.disconnect {|c| conns << c}
381
+ conns.size.should == 4
382
+ @pool.size.should == 1
383
+ end
384
+ @pool.size.should == 0
385
+ end
386
+ end
387
+
388
+ context "A connection pool with multiple servers" do
389
+ before do
390
+ @invoked_counts = Hash.new(0)
391
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:servers=>{:read_only=>{}})){|server| "#{server}#{@invoked_counts[server] += 1}"}
392
+ end
393
+
394
+ specify "#servers should return symbols for all servers" do
395
+ @pool.servers.sort_by{|s| s.to_s}.should == [:default, :read_only]
396
+ end
397
+
398
+ specify "should use the :default server by default" do
399
+ @pool.size.should == 0
400
+ @pool.hold do |c|
401
+ c.should == "default1"
402
+ @pool.allocated.should == {Thread.current=>"default1"}
403
+ end
404
+ @pool.available_connections.should == ["default1"]
405
+ @pool.size.should == 1
406
+ @invoked_counts.should == {:default=>1}
407
+ end
408
+
409
+ specify "should use the :default server an invalid server is used" do
410
+ @pool.hold do |c1|
411
+ c1.should == "default1"
412
+ @pool.hold(:blah) do |c2|
413
+ c2.should == c1
414
+ @pool.hold(:blah2) do |c3|
415
+ c2.should == c3
416
+ end
417
+ end
418
+ end
419
+ end
420
+
421
+ specify "should use the requested server if server is given" do
422
+ @pool.size(:read_only).should == 0
423
+ @pool.hold(:read_only) do |c|
424
+ c.should == "read_only1"
425
+ @pool.allocated(:read_only).should == {Thread.current=>"read_only1"}
426
+ end
427
+ @pool.available_connections(:read_only).should == ["read_only1"]
428
+ @pool.size(:read_only).should == 1
429
+ @invoked_counts.should == {:read_only=>1}
430
+ end
431
+
432
+ specify "#hold should only yield connections for the server requested" do
433
+ @pool.hold(:read_only) do |c|
434
+ c.should == "read_only1"
435
+ @pool.allocated(:read_only).should == {Thread.current=>"read_only1"}
436
+ @pool.hold do |d|
437
+ d.should == "default1"
438
+ @pool.hold do |e|
439
+ e.should == d
440
+ @pool.hold(:read_only){|b| b.should == c}
441
+ end
442
+ @pool.allocated.should == {Thread.current=>"default1"}
443
+ end
444
+ end
445
+ @invoked_counts.should == {:read_only=>1, :default=>1}
446
+ end
447
+
448
+ specify "#disconnect should disconnect from all servers" do
449
+ @pool.hold(:read_only){}
450
+ @pool.hold{}
451
+ conns = []
452
+ @pool.size.should == 1
453
+ @pool.size(:read_only).should == 1
454
+ @pool.disconnect{|c| conns << c}
455
+ conns.sort.should == %w'default1 read_only1'
456
+ @pool.size.should == 0
457
+ @pool.size(:read_only).should == 0
458
+ @pool.hold(:read_only){|c| c.should == 'read_only2'}
459
+ @pool.hold{|c| c.should == 'default2'}
460
+ end
461
+
462
+ specify "#add_servers should add new servers to the pool" do
463
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}){|s| s}
464
+
465
+ pool.hold{}
466
+ pool.hold(:server2){}
467
+ pool.hold(:server3){}
468
+ pool.hold(:server1) do
469
+ pool.allocated.length.should == 0
470
+ pool.allocated(:server1).length.should == 1
471
+ pool.allocated(:server2).should == nil
472
+ pool.allocated(:server3).should == nil
473
+ pool.available_connections.length.should == 1
474
+ pool.available_connections(:server1).length.should == 0
475
+ pool.available_connections(:server2).should == nil
476
+ pool.available_connections(:server3).should == nil
477
+
478
+ pool.add_servers([:server2, :server3])
479
+ pool.hold(:server2){}
480
+ pool.hold(:server3) do
481
+ pool.allocated.length.should == 0
482
+ pool.allocated(:server1).length.should == 1
483
+ pool.allocated(:server2).length.should == 0
484
+ pool.allocated(:server3).length.should == 1
485
+ pool.available_connections.length.should == 1
486
+ pool.available_connections(:server1).length.should == 0
487
+ pool.available_connections(:server2).length.should == 1
488
+ pool.available_connections(:server3).length.should == 0
489
+ end
490
+ end
491
+ end
492
+
493
+ specify "#add_servers should ignore existing keys" do
494
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}){|s| s}
495
+
496
+ pool.allocated.length.should == 0
497
+ pool.allocated(:server1).length.should == 0
498
+ pool.available_connections.length.should == 0
499
+ pool.available_connections(:server1).length.should == 0
500
+ pool.hold do |c1|
501
+ c1.should == :default
502
+ pool.allocated.length.should == 1
503
+ pool.allocated(:server1).length.should == 0
504
+ pool.available_connections.length.should == 0
505
+ pool.available_connections(:server1).length.should == 0
506
+ pool.hold(:server1) do |c2|
507
+ c2.should == :server1
508
+ pool.allocated.length.should == 1
509
+ pool.allocated(:server1).length.should == 1
510
+ pool.available_connections.length.should == 0
511
+ pool.available_connections(:server1).length.should == 0
512
+ pool.add_servers([:default, :server1])
513
+ pool.allocated.length.should == 1
514
+ pool.allocated(:server1).length.should == 1
515
+ pool.available_connections.length.should == 0
516
+ pool.available_connections(:server1).length.should == 0
517
+ end
518
+ pool.allocated.length.should == 1
519
+ pool.allocated(:server1).length.should == 0
520
+ pool.available_connections.length.should == 0
521
+ pool.available_connections(:server1).length.should == 1
522
+ pool.add_servers([:default, :server1])
523
+ pool.allocated.length.should == 1
524
+ pool.allocated(:server1).length.should == 0
525
+ pool.available_connections.length.should == 0
526
+ pool.available_connections(:server1).length.should == 1
527
+ end
528
+ pool.allocated.length.should == 0
529
+ pool.allocated(:server1).length.should == 0
530
+ pool.available_connections.length.should == 1
531
+ pool.available_connections(:server1).length.should == 1
532
+ pool.add_servers([:default, :server1])
533
+ pool.allocated.length.should == 0
534
+ pool.allocated(:server1).length.should == 0
535
+ pool.available_connections.length.should == 1
536
+ pool.available_connections(:server1).length.should == 1
537
+ end
538
+
539
+ specify "#remove_servers should disconnect available connections immediately" do
540
+ pool = Sequel::ConnectionPool.get_pool(:max_connections=>5, :servers=>{:server1=>{}}){|s| s}
541
+ threads = []
542
+ stop = nil
543
+ 5.times {|i| threads << Thread.new{pool.hold(:server1){|c| sleep 0.05}}}
544
+ sleep 0.1
545
+ threads.each {|t| t.join}
546
+
547
+ pool.size(:server1).should == 5
548
+ pool.remove_servers([:server1])
549
+ pool.size(:server1).should == 0
550
+ end
551
+
552
+ specify "#remove_servers should disconnect connections in use as soon as they are returned to the pool" do
553
+ dc = []
554
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}, :disconnection_proc=>proc{|c| dc << c}){|s| s}
555
+ c1 = nil
556
+ pool.hold(:server1) do |c|
557
+ pool.size(:server1).should == 1
558
+ dc.should == []
559
+ pool.remove_servers([:server1])
560
+ pool.size(:server1).should == 0
561
+ dc.should == []
562
+ c1 = c
563
+ end
564
+ pool.size(:server1).should == 0
565
+ dc.should == [c1]
566
+ end
567
+
568
+ specify "#remove_servers should remove server related data structures immediately" do
569
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}){|s| s}
570
+ pool.available_connections(:server1).should == []
571
+ pool.allocated(:server1).should == {}
572
+ pool.remove_servers([:server1])
573
+ pool.available_connections(:server1).should == nil
574
+ pool.allocated(:server1).should == nil
575
+ end
576
+
577
+ specify "#remove_servers should not allow the removal of the default server" do
578
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}){|s| s}
579
+ proc{pool.remove_servers([:server1])}.should_not raise_error
580
+ proc{pool.remove_servers([:default])}.should raise_error(Sequel::Error)
581
+ end
582
+
583
+ specify "#remove_servers should ignore servers that have already been removed" do
584
+ dc = []
585
+ pool = Sequel::ConnectionPool.get_pool(:servers=>{:server1=>{}}, :disconnection_proc=>proc{|c| dc << c}){|s| s}
586
+ c1 = nil
587
+ pool.hold(:server1) do |c|
588
+ pool.size(:server1).should == 1
589
+ dc.should == []
590
+ pool.remove_servers([:server1])
591
+ pool.remove_servers([:server1])
592
+ pool.size(:server1).should == 0
593
+ dc.should == []
594
+ c1 = c
595
+ end
596
+ pool.size(:server1).should == 0
597
+ dc.should == [c1]
598
+ end
599
+ end
600
+
601
+ ST_CONNECTION_POOL_DEFAULTS = CONNECTION_POOL_DEFAULTS.merge(:single_threaded=>true)
602
+
603
+ context "SingleConnectionPool" do
604
+ before do
605
+ @pool = Sequel::ConnectionPool.get_pool(ST_CONNECTION_POOL_DEFAULTS){1234}
606
+ end
607
+
608
+ specify "should provide a #hold method" do
609
+ conn = nil
610
+ @pool.hold{|c| conn = c}
611
+ conn.should == 1234
612
+ end
613
+
614
+ specify "should provide a #disconnect method" do
615
+ conn = nil
616
+ x = nil
617
+ pool = Sequel::ConnectionPool.get_pool(ST_CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| conn = c})){1234}
618
+ pool.hold{|c| x = c}
619
+ x.should == 1234
620
+ pool.disconnect
621
+ conn.should == 1234
622
+ end
623
+ end
624
+
625
+ context "A single threaded pool with multiple servers" do
626
+ before do
627
+ @max_size=2
628
+ @pool = Sequel::ConnectionPool.get_pool(ST_CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| @max_size=3}, :servers=>{:read_only=>{}})){|server| server}
629
+ end
630
+
631
+ specify "#servers should return symbols for all servers" do
632
+ @pool.servers.sort_by{|s| s.to_s}.should == [:default, :read_only]
633
+ end
634
+
635
+ specify "#add_servers should add new servers to the pool" do
636
+ @pool.hold(:blah){|c| c.should == :default}
637
+ @pool.add_servers([:blah])
638
+ @pool.hold(:blah){|c| c.should == :blah}
639
+ end
640
+
641
+ specify "#add_servers should ignore keys already existing" do
642
+ @pool.hold{|c| c.should == :default}
643
+ @pool.hold(:read_only){|c| c.should == :read_only}
644
+ @pool.add_servers([:default, :read_only])
645
+ @pool.conn.should == :default
646
+ @pool.conn(:read_only).should == :read_only
647
+ end
648
+
649
+ specify "#remove_servers should remove servers from the pool" do
650
+ @pool.hold(:read_only){|c| c.should == :read_only}
651
+ @pool.remove_servers([:read_only])
652
+ @pool.hold(:read_only){|c| c.should == :default}
653
+ end
654
+
655
+ specify "#remove_servers should not allow the removal of the default server" do
656
+ proc{@pool.remove_servers([:default])}.should raise_error(Sequel::Error)
657
+ end
658
+
659
+ specify "#remove_servers should disconnect connection immediately" do
660
+ @pool.hold(:read_only){|c| c.should == :read_only}
661
+ @pool.conn(:read_only).should == :read_only
662
+ @pool.remove_servers([:read_only])
663
+ @pool.conn(:read_only).should == nil
664
+ @pool.hold{}
665
+ @pool.conn(:read_only).should == :default
666
+ end
667
+
668
+ specify "#remove_servers should ignore keys that do not exist" do
669
+ proc{@pool.remove_servers([:blah])}.should_not raise_error
670
+ end
671
+
672
+ specify "should use the :default server by default" do
673
+ @pool.hold{|c| c.should == :default}
674
+ @pool.conn.should == :default
675
+ end
676
+
677
+ specify "should use the :default server an invalid server is used" do
678
+ @pool.hold do |c1|
679
+ c1.should == :default
680
+ @pool.hold(:blah) do |c2|
681
+ c2.should == c1
682
+ @pool.hold(:blah2) do |c3|
683
+ c2.should == c3
684
+ end
685
+ end
686
+ end
687
+ end
688
+
689
+ specify "should use the requested server if server is given" do
690
+ @pool.hold(:read_only){|c| c.should == :read_only}
691
+ @pool.conn(:read_only).should == :read_only
692
+ end
693
+
694
+ specify "#hold should only yield connections for the server requested" do
695
+ @pool.hold(:read_only) do |c|
696
+ c.should == :read_only
697
+ @pool.hold do |d|
698
+ d.should == :default
699
+ @pool.hold do |e|
700
+ e.should == d
701
+ @pool.hold(:read_only){|b| b.should == c}
702
+ end
703
+ end
704
+ end
705
+ @pool.conn.should == :default
706
+ @pool.conn(:read_only).should == :read_only
707
+ end
708
+
709
+ specify "#disconnect should disconnect from all servers" do
710
+ @pool.hold(:read_only){}
711
+ @pool.hold{}
712
+ conns = []
713
+ @pool.conn.should == :default
714
+ @pool.conn(:read_only).should == :read_only
715
+ @pool.disconnect{|c| conns << c}
716
+ conns.sort_by{|x| x.to_s}.should == [:default, :read_only]
717
+ @pool.conn.should == nil
718
+ @pool.conn(:read_only).should == nil
719
+ end
720
+
721
+ specify ":disconnection_proc option should set the disconnection proc to use" do
722
+ @max_size.should == 2
723
+ proc{@pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
724
+ @max_size.should == 3
725
+ end
726
+
727
+ specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
728
+ @pool.instance_variable_get(:@conns).length.should == 0
729
+ @pool.hold{}
730
+ @pool.instance_variable_get(:@conns).length.should == 1
731
+ proc{@pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
732
+ @pool.instance_variable_get(:@conns).length.should == 0
733
+ end
734
+ end
735
+
736
+ shared_examples_for "All connection pools classes" do
737
+ specify "should yield a connection created by the initialize block to hold" do
738
+ x = nil
739
+ @class.new({}){123}.hold{|c| x = c}
740
+ x.should == 123
741
+ end
742
+
743
+ specify "should have the initialize block accept a shard/server argument" do
744
+ x = nil
745
+ @class.new({}){|c| [c, c]}.hold{|c| x = c}
746
+ x.should == [:default, :default]
747
+ end
748
+
749
+ specify "should have respect an :after_connect proc that is called with each newly created connection" do
750
+ x = nil
751
+ @class.new(:after_connect=>proc{|c| x = [c, c]}){|c| 123}.hold{}
752
+ x.should == [123, 123]
753
+ end
754
+
755
+ specify "should raise a DatabaseConnectionError if the connection raises an exception" do
756
+ proc{@class.new({}){|c| raise Exception}.hold{}}.should raise_error(Sequel::DatabaseConnectionError)
757
+ end
758
+
759
+ specify "should raise a DatabaseConnectionError if the initialize block returns nil" do
760
+ proc{@class.new({}){}.hold{}}.should raise_error(Sequel::DatabaseConnectionError)
761
+ end
762
+
763
+ specify "should call the disconnection_proc option if the hold block raises a DatabaseDisconnectError" do
764
+ x = nil
765
+ proc{@class.new(:disconnection_proc=>proc{|c| x = c}){123}.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
766
+ x.should == 123
767
+ end
768
+
769
+ specify "should have a disconnect method that calls the :disconnection_proc option with the connection" do
770
+ x = nil
771
+ c = @class.new(:disconnection_proc=>proc{|c| x = c}){123}
772
+ c.hold{}
773
+ x.should == nil
774
+ c.disconnect
775
+ x.should == 123
776
+ end
777
+
778
+ specify "should have a disconnect method that calls the given block with the connection" do
779
+ x = nil
780
+ y = nil
781
+ c = @class.new(:disconnection_proc=>proc{|c| x = c}){123}
782
+ c.hold{}
783
+ c.disconnect{|c| y = c}
784
+ x.should == nil
785
+ y.should == 123
786
+ end
787
+
788
+ specify "should have a servers method that returns an array of shard/server symbols" do
789
+ @class.new({}){123}.servers.should == [:default]
790
+ end
791
+
792
+ specify "should have a servers method that returns an array of shard/server symbols" do
793
+ c = @class.new({}){123}
794
+ c.size.should == 0
795
+ c.hold{}
796
+ c.size.should == 1
797
+ end
798
+ end
799
+
800
+ Sequel::ConnectionPool::CONNECTION_POOL_MAP.keys.each do |k, v|
801
+ opts = {:single_threaded=>k, :servers=>(v ? {} : nil)}
802
+ describe "Connection pool with #{opts.inspect}" do
803
+ before do
804
+ @class = Sequel::ConnectionPool.send(:connection_pool_class, opts)
805
+ end
806
+ it_should_behave_like "All connection pools classes"
807
+ end
808
+ end