sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -84,7 +84,9 @@ describe "A connection pool handling connections" do
84
84
  end
85
85
 
86
86
  specify "#make_new should not make more than max_size connections" do
87
- 50.times{Thread.new{@cpool.hold{sleep 0.001}}}
87
+ q = Queue.new
88
+ 50.times{Thread.new{@cpool.hold{q.pop}}}
89
+ 50.times{q.push nil}
88
90
  @cpool.created_count.should <= @max_size
89
91
  end
90
92
 
@@ -96,7 +98,8 @@ describe "A connection pool handling connections" do
96
98
 
97
99
  specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
98
100
  @cpool.created_count.should == 0
99
- @cpool.hold{Thread.new{@cpool.hold{}}; sleep 0.03}
101
+ q, q1 = Queue.new, Queue.new
102
+ @cpool.hold{Thread.new{@cpool.hold{q1.pop; q.push nil}; q1.pop; q.push nil}; q1.push nil; q.pop; q1.push nil; q.pop}
100
103
  @cpool.created_count.should == 2
101
104
  proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
102
105
  @cpool.created_count.should == 1
@@ -121,36 +124,30 @@ describe "A connection pool handling connection errors" do
121
124
  end
122
125
  end
123
126
 
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
127
  describe "ConnectionPool#hold" do
136
128
  before do
137
- @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
129
+ value = 0
130
+ @c = Class.new do
131
+ define_method(:initialize){value += 1}
132
+ define_method(:value){value}
133
+ end
134
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS){@c.new}
138
135
  end
139
136
 
140
137
  specify "should pass the result of the connection maker proc to the supplied block" do
141
138
  res = nil
142
139
  @pool.hold {|c| res = c}
143
- res.should be_a_kind_of(DummyConnection)
140
+ res.should be_a_kind_of(@c)
144
141
  res.value.should == 1
145
142
  @pool.hold {|c| res = c}
146
- res.should be_a_kind_of(DummyConnection)
143
+ res.should be_a_kind_of(@c)
147
144
  res.value.should == 1 # the connection maker is invoked only once
148
145
  end
149
146
 
150
147
  specify "should be re-entrant by the same thread" do
151
148
  cc = nil
152
149
  @pool.hold {|c| @pool.hold {|c| @pool.hold {|c| cc = c}}}
153
- cc.should be_a_kind_of(DummyConnection)
150
+ cc.should be_a_kind_of(@c)
154
151
  end
155
152
 
156
153
  specify "should catch exceptions and reraise them" do
@@ -166,15 +163,14 @@ describe "A connection pool with a max size of 1" do
166
163
 
167
164
  specify "should let only one thread access the connection at any time" do
168
165
  cc,c1, c2 = nil
169
- m = (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx') ? 30 : 1
166
+ q, q1 = Queue.new, Queue.new
170
167
 
171
- t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; while c == 'herro';sleep 0.01;end}}
172
- sleep 0.02 * m
168
+ t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; q1.push nil; q.pop}}
169
+ q1.pop
173
170
  cc.should == 'herro'
174
171
  c1.should == 'herro'
175
172
 
176
- t2 = Thread.new {@pool.hold {|c| c2 = c.dup; while c == 'hello';sleep 0.01;end}}
177
- sleep 0.02 * m
173
+ t2 = Thread.new {@pool.hold {|c| c2 = c.dup; q1.push nil; q.pop;}}
178
174
 
179
175
  # connection held by t1
180
176
  t1.should be_alive
@@ -186,26 +182,22 @@ describe "A connection pool with a max size of 1" do
186
182
 
187
183
  @pool.available_connections.should be_empty
188
184
  @pool.allocated.should == {t1=>cc}
189
-
185
+
190
186
  cc.gsub!('rr', 'll')
191
- sleep 0.05 * m
192
-
193
- # connection held by t2
194
- t1.should_not be_alive
195
- t2.should be_alive
187
+ q.push nil
188
+ q1.pop
196
189
 
190
+ t1.join
191
+ t2.should be_alive
192
+
197
193
  c2.should == 'hello'
198
194
 
199
195
  @pool.available_connections.should be_empty
200
196
  @pool.allocated.should == {t2=>cc}
201
197
 
202
- cc.gsub!('ll', 'rr')
203
- sleep 0.05 * m
204
-
205
198
  #connection released
206
- t2.should_not be_alive
207
-
208
- cc.should == 'herro'
199
+ q.push nil
200
+ t2.join
209
201
 
210
202
  @invoked_count.should == 1
211
203
  @pool.size.should == 1
@@ -236,34 +228,81 @@ describe "A connection pool with a max size of 1" do
236
228
  end
237
229
 
238
230
  shared_examples_for "A threaded connection pool" do
231
+ specify "should not have all_connections yield connections allocated to other threads" do
232
+ pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:max_connections=>2, :pool_timeout=>0)) {@invoked_count += 1}
233
+ q, q1 = Queue.new, Queue.new
234
+ t = Thread.new do
235
+ pool.hold do |c1|
236
+ q1.push nil
237
+ q.pop
238
+ end
239
+ end
240
+ pool.hold do |c1|
241
+ q1.pop
242
+ pool.all_connections{|c| c.should == c1}
243
+ q.push nil
244
+ end
245
+ t.join
246
+ end
247
+
248
+ specify "should not have all_connections yield all available connections" do
249
+ pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:max_connections=>2, :pool_timeout=>0)){@invoked_count += 1}
250
+ q, q1 = Queue.new, Queue.new
251
+ b = []
252
+ t = Thread.new do
253
+ pool.hold do |c1|
254
+ b << c1
255
+ q1.push nil
256
+ q.pop
257
+ end
258
+ end
259
+ pool.hold do |c1|
260
+ q1.pop
261
+ b << c1
262
+ q.push nil
263
+ end
264
+ t.join
265
+ a = []
266
+ pool.all_connections{|c| a << c}
267
+ a.sort.should == b.sort
268
+ end
269
+
270
+ specify "should raise a PoolTimeout error if a connection couldn't be acquired before timeout" do
271
+ x = nil
272
+ q, q1 = Queue.new, Queue.new
273
+ pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:max_connections=>1, :pool_timeout=>0)) {@invoked_count += 1}
274
+ t = Thread.new{pool.hold{|c| q1.push nil; q.pop}}
275
+ q1.pop
276
+ proc{pool.hold{|c|}}.should raise_error(Sequel::PoolTimeout)
277
+ q.push nil
278
+ t.join
279
+ end
280
+
281
+ it "should not add a disconnected connection back to the pool if the disconnection_proc raises an error" do
282
+ pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:max_connections=>1, :pool_timeout=>0, :disconnection_proc=>proc{|c| raise Sequel::Error})) {@invoked_count += 1}
283
+ proc{pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::Error)
284
+ pool.available_connections.length.should == 0
285
+ end
286
+
239
287
  specify "should let five threads simultaneously access separate connections" do
240
288
  cc = {}
241
289
  threads = []
242
- stop = nil
290
+ q, q1, q2 = Queue.new, Queue.new, Queue.new
243
291
 
244
- 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.02;end}}; sleep 0.02}
245
- sleep 0.04
292
+ 5.times{|i| threads << Thread.new{@pool.hold{|c| q.pop; cc[i] = c; q1.push nil; q2.pop}}; q.push nil; q1.pop}
246
293
  threads.each {|t| t.should be_alive}
247
294
  cc.size.should == 5
248
295
  @invoked_count.should == 5
249
296
  @pool.size.should == 5
250
297
  @pool.available_connections.should be_empty
251
- i = 0
298
+
252
299
  h = {}
300
+ i = 0
253
301
  threads.each{|t| h[t] = (i+=1)}
254
302
  @pool.allocated.should == h
255
-
256
- threads[0].raise "your'e dead"
257
- sleep 0.02
258
- threads[3].raise "your'e dead too"
259
-
260
- sleep 0.02
261
-
262
- @pool.available_connections.should == [1, 4]
263
- @pool.allocated.should == {threads[1]=>2, threads[2]=>3, threads[4]=>5}
264
-
265
- stop = true
266
- sleep 0.04
303
+ @pool.available_connections.should == []
304
+ 5.times{q2.push nil}
305
+ threads.each{|t| t.join}
267
306
 
268
307
  @pool.available_connections.size.should == 5
269
308
  @pool.allocated.should be_empty
@@ -272,16 +311,15 @@ shared_examples_for "A threaded connection pool" do
272
311
  specify "should block threads until a connection becomes available" do
273
312
  cc = {}
274
313
  threads = []
275
- stop = nil
314
+ q, q1 = Queue.new, Queue.new
276
315
 
277
- 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.01;end}}; sleep 0.01}
278
- sleep 0.02
316
+ 5.times{|i| threads << Thread.new{@pool.hold{|c| cc[i] = c; q1.push nil; q.pop}}}
317
+ 5.times{q1.pop}
279
318
  threads.each {|t| t.should be_alive}
280
319
  @pool.available_connections.should be_empty
281
320
 
282
- 3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c}}}
321
+ 3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c; q1.push nil}}}
283
322
 
284
- sleep 0.02
285
323
  threads[5].should be_alive
286
324
  threads[6].should be_alive
287
325
  threads[7].should be_alive
@@ -290,8 +328,10 @@ shared_examples_for "A threaded connection pool" do
290
328
  cc[6].should be_nil
291
329
  cc[7].should be_nil
292
330
 
293
- stop = true
294
- sleep 0.1
331
+ 5.times{q.push nil}
332
+ 5.times{|i| threads[i].join}
333
+ 3.times{q1.pop}
334
+ 3.times{|i| threads[i+5].join}
295
335
 
296
336
  threads.each {|t| t.should_not be_alive}
297
337
 
@@ -300,12 +340,38 @@ shared_examples_for "A threaded connection pool" do
300
340
  @pool.available_connections.size.should == 5
301
341
  @pool.allocated.should be_empty
302
342
  end
343
+
344
+ specify "should store connections in a stack by default" do
345
+ c2 = nil
346
+ c = @pool.hold{|cc| Thread.new{@pool.hold{|cc2| c2 = cc2}}.join; cc}
347
+ @pool.size.should == 2
348
+ @pool.hold{|cc| cc.should == c}
349
+ @pool.hold{|cc| cc.should == c}
350
+ @pool.hold do |cc|
351
+ cc.should == c
352
+ Thread.new{@pool.hold{|cc2| cc2.should == c2}}
353
+ end
354
+ end
355
+
356
+ specify "should store connections in a queue if :connection_handling=>:queue" do
357
+ @pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:connection_handling=>:queue)){@invoked_count += 1}
358
+ c2 = nil
359
+ c = @pool.hold{|cc| Thread.new{@pool.hold{|cc2| c2 = cc2}}.join; cc}
360
+ @pool.size.should == 2
361
+ @pool.hold{|cc| cc.should == c2}
362
+ @pool.hold{|cc| cc.should == c}
363
+ @pool.hold do |cc|
364
+ cc.should == c2
365
+ Thread.new{@pool.hold{|cc2| cc2.should == c}}
366
+ end
367
+ end
303
368
  end
304
369
 
305
370
  describe "Threaded Unsharded Connection Pool" do
306
371
  before do
307
372
  @invoked_count = 0
308
- @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {@invoked_count += 1}
373
+ @cp_opts = CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)
374
+ @pool = Sequel::ConnectionPool.get_pool(@cp_opts) {@invoked_count += 1}
309
375
  end
310
376
 
311
377
  it_should_behave_like "A threaded connection pool"
@@ -314,7 +380,8 @@ end
314
380
  describe "Threaded Sharded Connection Pool" do
315
381
  before do
316
382
  @invoked_count = 0
317
- @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5, :servers=>{})) {@invoked_count += 1}
383
+ @cp_opts = CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5, :servers=>{})
384
+ @pool = Sequel::ConnectionPool.get_pool(@cp_opts) {@invoked_count += 1}
318
385
  end
319
386
 
320
387
  it_should_behave_like "A threaded connection pool"
@@ -324,19 +391,15 @@ describe "ConnectionPool#disconnect" do
324
391
  before do
325
392
  @count = 0
326
393
  @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5, :servers=>{})) {{:id => @count += 1}}
394
+ threads = []
395
+ q, q1 = Queue.new, Queue.new
396
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| q1.push nil; q.pop}}}
397
+ 5.times{q1.pop}
398
+ 5.times{q.push nil}
399
+ threads.each {|t| t.join}
327
400
  end
328
401
 
329
402
  specify "should invoke the given block for each available connection" do
330
- threads = []
331
- stop = nil
332
- 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
333
- while @pool.size < 5
334
- sleep 0.02
335
- end
336
- stop = true
337
- sleep 0.1
338
- threads.each {|t| t.join}
339
-
340
403
  @pool.size.should == 5
341
404
  @pool.available_connections.size.should == 5
342
405
  @pool.available_connections.each {|c| c[:id].should_not be_nil}
@@ -346,34 +409,13 @@ describe "ConnectionPool#disconnect" do
346
409
  end
347
410
 
348
411
  specify "should remove all available connections" do
349
- threads = []
350
- stop = nil
351
- 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
352
- while @pool.size < 5
353
- sleep 0.02
354
- end
355
- stop = true
356
- sleep 0.1
357
- threads.each {|t| t.join}
358
-
359
412
  @pool.size.should == 5
360
413
  @pool.disconnect
361
414
  @pool.size.should == 0
362
415
  end
363
416
 
364
417
  specify "should disconnect connections in use as soon as they are no longer in use" do
365
- threads = []
366
- stop = nil
367
- 5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.01;end}}; sleep 0.01}
368
- while @pool.size < 5
369
- sleep 0.02
370
- end
371
- stop = true
372
- sleep 0.1
373
- threads.each {|t| t.join}
374
-
375
418
  @pool.size.should == 5
376
-
377
419
  @pool.hold do |conn|
378
420
  @pool.available_connections.size.should == 4
379
421
  @pool.available_connections.each {|c| c.should_not be(conn)}
@@ -392,6 +434,16 @@ describe "A connection pool with multiple servers" do
392
434
  @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS.merge(:servers=>{:read_only=>{}})){|server| "#{server}#{@invoked_counts[server] += 1}"}
393
435
  end
394
436
 
437
+ specify "#all_connections should return connections for all servers" do
438
+ @pool.hold{}
439
+ @pool.all_connections{|c1| c1.should == "default1"}
440
+ a = []
441
+ @pool.hold(:read_only) do |c|
442
+ @pool.all_connections{|c1| a << c1}
443
+ end
444
+ a.sort_by{|c| c.to_s}.should == ["default1", "read_only1"]
445
+ end
446
+
395
447
  specify "#servers should return symbols for all servers" do
396
448
  @pool.servers.sort_by{|s| s.to_s}.should == [:default, :read_only]
397
449
  end
@@ -556,9 +608,10 @@ describe "A connection pool with multiple servers" do
556
608
  specify "#remove_servers should disconnect available connections immediately" do
557
609
  pool = Sequel::ConnectionPool.get_pool(:max_connections=>5, :servers=>{:server1=>{}}){|s| s}
558
610
  threads = []
559
- stop = nil
560
- 5.times {|i| threads << Thread.new{pool.hold(:server1){|c| sleep 0.05}}}
561
- sleep 0.1
611
+ q, q1 = Queue.new, Queue.new
612
+ 5.times {|i| threads << Thread.new {pool.hold(:server1){|c| q1.push nil; q.pop}}}
613
+ 5.times{q1.pop}
614
+ 5.times{q.push nil}
562
615
  threads.each {|t| t.join}
563
616
 
564
617
  pool.size(:server1).should == 5
@@ -645,6 +698,16 @@ describe "A single threaded pool with multiple servers" do
645
698
  @pool = Sequel::ConnectionPool.get_pool(ST_CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| @max_size=3}, :servers=>{:read_only=>{}})){|server| server}
646
699
  end
647
700
 
701
+ specify "#all_connections should return connections for all servers" do
702
+ @pool.hold{}
703
+ @pool.all_connections{|c1| c1.should == :default}
704
+ a = []
705
+ @pool.hold(:read_only) do
706
+ @pool.all_connections{|c1| a << c1}
707
+ end
708
+ a.sort_by{|c| c.to_s}.should == [:default, :read_only]
709
+ end
710
+
648
711
  specify "#servers should return symbols for all servers" do
649
712
  @pool.servers.sort_by{|s| s.to_s}.should == [:default, :read_only]
650
713
  end
@@ -751,6 +814,32 @@ describe "A single threaded pool with multiple servers" do
751
814
  end
752
815
 
753
816
  shared_examples_for "All connection pools classes" do
817
+ specify "should have all_connections yield current and available connections" do
818
+ p = @class.new({}){123}
819
+ p.hold{|c| p.all_connections{|c1| c.should == c1}}
820
+ end
821
+
822
+ specify "should be able to modify after_connect proc after the pool is created" do
823
+ a = []
824
+ p = @class.new({}){123}
825
+ p.after_connect = pr = proc{|c| a << c}
826
+ p.after_connect.should == pr
827
+ a.should == []
828
+ p.hold{}
829
+ a.should == [123]
830
+ end
831
+
832
+ specify "should be able to modify disconnection_proc after the pool is created" do
833
+ a = []
834
+ p = @class.new({}){123}
835
+ p.disconnection_proc = pr = proc{|c| a << c}
836
+ p.disconnection_proc.should == pr
837
+ p.hold{}
838
+ a.should == []
839
+ p.disconnect
840
+ a.should == [123]
841
+ end
842
+
754
843
  specify "should not raise an error when disconnecting twice" do
755
844
  c = @class.new({}){123}
756
845
  proc{c.disconnect}.should_not raise_error
@@ -808,6 +897,20 @@ shared_examples_for "All connection pools classes" do
808
897
  y.should == 123
809
898
  end
810
899
 
900
+ specify "should have a reentrent hold method" do
901
+ o = Object.new
902
+ c = @class.new({}){o}
903
+ c.hold do |x|
904
+ x.should == o
905
+ c.hold do |x1|
906
+ x1.should == o
907
+ c.hold do |x2|
908
+ x2.should == o
909
+ end
910
+ end
911
+ end
912
+ end
913
+
811
914
  specify "should have a servers method that returns an array of shard/server symbols" do
812
915
  @class.new({}){123}.servers.should == [:default]
813
916
  end