activerecord_csi 2.3.5.p6

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 (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,371 @@
1
+ require 'monitor'
2
+ require 'set'
3
+
4
+ module ActiveRecord
5
+ # Raised when a connection could not be obtained within the connection
6
+ # acquisition timeout period.
7
+ class ConnectionTimeoutError < ConnectionNotEstablished
8
+ end
9
+
10
+ module ConnectionAdapters
11
+ # Connection pool base class for managing ActiveRecord database
12
+ # connections.
13
+ #
14
+ # == Introduction
15
+ #
16
+ # A connection pool synchronizes thread access to a limited number of
17
+ # database connections. The basic idea is that each thread checks out a
18
+ # database connection from the pool, uses that connection, and checks the
19
+ # connection back in. ConnectionPool is completely thread-safe, and will
20
+ # ensure that a connection cannot be used by two threads at the same time,
21
+ # as long as ConnectionPool's contract is correctly followed. It will also
22
+ # handle cases in which there are more threads than connections: if all
23
+ # connections have been checked out, and a thread tries to checkout a
24
+ # connection anyway, then ConnectionPool will wait until some other thread
25
+ # has checked in a connection.
26
+ #
27
+ # == Obtaining (checking out) a connection
28
+ #
29
+ # Connections can be obtained and used from a connection pool in several
30
+ # ways:
31
+ #
32
+ # 1. Simply use ActiveRecord::Base.connection as with ActiveRecord 2.1 and
33
+ # earlier (pre-connection-pooling). Eventually, when you're done with
34
+ # the connection(s) and wish it to be returned to the pool, you call
35
+ # ActiveRecord::Base.clear_active_connections!. This will be the
36
+ # default behavior for ActiveRecord when used in conjunction with
37
+ # ActionPack's request handling cycle.
38
+ # 2. Manually check out a connection from the pool with
39
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
40
+ # returning this connection to the pool when finished by calling
41
+ # ActiveRecord::Base.connection_pool.checkin(connection).
42
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
43
+ # obtains a connection, yields it as the sole argument to the block,
44
+ # and returns it to the pool after the block completes.
45
+ #
46
+ # Connections in the pool are actually AbstractAdapter objects (or objects
47
+ # compatible with AbstractAdapter's interface).
48
+ #
49
+ # == Options
50
+ #
51
+ # There are two connection-pooling-related options that you can add to
52
+ # your database connection configuration:
53
+ #
54
+ # * +pool+: number indicating size of connection pool (default 5)
55
+ # * +wait_timeout+: number of seconds to block and wait for a connection
56
+ # before giving up and raising a timeout error (default 5 seconds).
57
+ class ConnectionPool
58
+ attr_reader :spec
59
+
60
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
61
+ # object which describes database connection information (e.g. adapter,
62
+ # host name, username, password, etc), as well as the maximum size for
63
+ # this ConnectionPool.
64
+ #
65
+ # The default ConnectionPool maximum size is 5.
66
+ def initialize(spec)
67
+ @spec = spec
68
+
69
+ # The cache of reserved connections mapped to threads
70
+ @reserved_connections = {}
71
+
72
+ # The mutex used to synchronize pool access
73
+ @connection_mutex = Monitor.new
74
+ @queue = @connection_mutex.new_cond
75
+
76
+ # default 5 second timeout unless on ruby 1.9
77
+ @timeout =
78
+ if RUBY_VERSION < '1.9'
79
+ spec.config[:wait_timeout] || 5
80
+ end
81
+
82
+ # default max pool size to 5
83
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
84
+
85
+ @connections = []
86
+ @checked_out = []
87
+ end
88
+
89
+ # Retrieve the connection associated with the current thread, or call
90
+ # #checkout to obtain one if necessary.
91
+ #
92
+ # #connection can be called any number of times; the connection is
93
+ # held in a hash keyed by the thread id.
94
+ def connection
95
+ if conn = @reserved_connections[current_connection_id]
96
+ conn
97
+ else
98
+ @reserved_connections[current_connection_id] = checkout
99
+ end
100
+ end
101
+
102
+ # Signal that the thread is finished with the current connection.
103
+ # #release_connection releases the connection-thread association
104
+ # and returns the connection to the pool.
105
+ def release_connection
106
+ conn = @reserved_connections.delete(current_connection_id)
107
+ checkin conn if conn
108
+ end
109
+
110
+ # Reserve a connection, and yield it to a block. Ensure the connection is
111
+ # checked back in when finished.
112
+ def with_connection
113
+ conn = checkout
114
+ yield conn
115
+ ensure
116
+ checkin conn
117
+ end
118
+
119
+ # Returns true if a connection has already been opened.
120
+ def connected?
121
+ !@connections.empty?
122
+ end
123
+
124
+ # Disconnects all connections in the pool, and clears the pool.
125
+ def disconnect!
126
+ @reserved_connections.each do |name,conn|
127
+ checkin conn
128
+ end
129
+ @reserved_connections = {}
130
+ @connections.each do |conn|
131
+ conn.disconnect!
132
+ end
133
+ @connections = []
134
+ end
135
+
136
+ # Clears the cache which maps classes
137
+ def clear_reloadable_connections!
138
+ @reserved_connections.each do |name, conn|
139
+ checkin conn
140
+ end
141
+ @reserved_connections = {}
142
+ @connections.each do |conn|
143
+ conn.disconnect! if conn.requires_reloading?
144
+ end
145
+ @connections = []
146
+ end
147
+
148
+ # Verify active connections and remove and disconnect connections
149
+ # associated with stale threads.
150
+ def verify_active_connections! #:nodoc:
151
+ clear_stale_cached_connections!
152
+ @connections.each do |connection|
153
+ connection.verify!
154
+ end
155
+ end
156
+
157
+ # Return any checked-out connections back to the pool by threads that
158
+ # are no longer alive.
159
+ def clear_stale_cached_connections!
160
+ remove_stale_cached_threads!(@reserved_connections) do |name, conn|
161
+ checkin conn
162
+ end
163
+ end
164
+
165
+ # Check-out a database connection from the pool, indicating that you want
166
+ # to use it. You should call #checkin when you no longer need this.
167
+ #
168
+ # This is done by either returning an existing connection, or by creating
169
+ # a new connection. If the maximum number of connections for this pool has
170
+ # already been reached, but the pool is empty (i.e. they're all being used),
171
+ # then this method will wait until a thread has checked in a connection.
172
+ # The wait time is bounded however: if no connection can be checked out
173
+ # within the timeout specified for this pool, then a ConnectionTimeoutError
174
+ # exception will be raised.
175
+ #
176
+ # Returns: an AbstractAdapter object.
177
+ #
178
+ # Raises:
179
+ # - ConnectionTimeoutError: no connection can be obtained from the pool
180
+ # within the timeout period.
181
+ def checkout
182
+ # Checkout an available connection
183
+ @connection_mutex.synchronize do
184
+ loop do
185
+ conn = if @checked_out.size < @connections.size
186
+ checkout_existing_connection
187
+ elsif @connections.size < @size
188
+ checkout_new_connection
189
+ end
190
+ return conn if conn
191
+ # No connections available; wait for one
192
+ if @queue.wait(@timeout)
193
+ next
194
+ else
195
+ # try looting dead threads
196
+ clear_stale_cached_connections!
197
+ if @size == @checked_out.size
198
+ raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # Check-in a database connection back into the pool, indicating that you
206
+ # no longer need this connection.
207
+ #
208
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
209
+ # calling +checkout+ on this pool.
210
+ def checkin(conn)
211
+ @connection_mutex.synchronize do
212
+ conn.run_callbacks :checkin
213
+ @checked_out.delete conn
214
+ @queue.signal
215
+ end
216
+ end
217
+
218
+ synchronize :clear_reloadable_connections!, :verify_active_connections!,
219
+ :connected?, :disconnect!, :with => :@connection_mutex
220
+
221
+ private
222
+ def new_connection
223
+ ActiveRecord::Base.send(spec.adapter_method, spec.config)
224
+ end
225
+
226
+ def current_connection_id #:nodoc:
227
+ Thread.current.object_id
228
+ end
229
+
230
+ # Remove stale threads from the cache.
231
+ def remove_stale_cached_threads!(cache, &block)
232
+ keys = Set.new(cache.keys)
233
+
234
+ Thread.list.each do |thread|
235
+ keys.delete(thread.object_id) if thread.alive?
236
+ end
237
+ keys.each do |key|
238
+ next unless cache.has_key?(key)
239
+ block.call(key, cache[key])
240
+ cache.delete(key)
241
+ end
242
+ end
243
+
244
+ def checkout_new_connection
245
+ c = new_connection
246
+ @connections << c
247
+ checkout_and_verify(c)
248
+ end
249
+
250
+ def checkout_existing_connection
251
+ c = (@connections - @checked_out).first
252
+ checkout_and_verify(c)
253
+ end
254
+
255
+ def checkout_and_verify(c)
256
+ c.verify!
257
+ c.run_callbacks :checkout
258
+ @checked_out << c
259
+ c
260
+ end
261
+ end
262
+
263
+ # ConnectionHandler is a collection of ConnectionPool objects. It is used
264
+ # for keeping separate connection pools for ActiveRecord models that connect
265
+ # to different databases.
266
+ #
267
+ # For example, suppose that you have 5 models, with the following hierarchy:
268
+ #
269
+ # |
270
+ # +-- Book
271
+ # | |
272
+ # | +-- ScaryBook
273
+ # | +-- GoodBook
274
+ # +-- Author
275
+ # +-- BankAccount
276
+ #
277
+ # Suppose that Book is to connect to a separate database (i.e. one other
278
+ # than the default database). Then Book, ScaryBook and GoodBook will all use
279
+ # the same connection pool. Likewise, Author and BankAccount will use the
280
+ # same connection pool. However, the connection pool used by Author/BankAccount
281
+ # is not the same as the one used by Book/ScaryBook/GoodBook.
282
+ #
283
+ # Normally there is only a single ConnectionHandler instance, accessible via
284
+ # ActiveRecord::Base.connection_handler. ActiveRecord models use this to
285
+ # determine that connection pool that they should use.
286
+ class ConnectionHandler
287
+ def initialize(pools = {})
288
+ @connection_pools = pools
289
+ end
290
+
291
+ def connection_pools
292
+ @connection_pools ||= {}
293
+ end
294
+
295
+ def establish_connection(name, spec)
296
+ @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
297
+ end
298
+
299
+ # Returns any connections in use by the current thread back to the pool,
300
+ # and also returns connections to the pool cached by threads that are no
301
+ # longer alive.
302
+ def clear_active_connections!
303
+ @connection_pools.each_value {|pool| pool.release_connection }
304
+ end
305
+
306
+ # Clears the cache which maps classes
307
+ def clear_reloadable_connections!
308
+ @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
309
+ end
310
+
311
+ def clear_all_connections!
312
+ @connection_pools.each_value {|pool| pool.disconnect! }
313
+ end
314
+
315
+ # Verify active connections.
316
+ def verify_active_connections! #:nodoc:
317
+ @connection_pools.each_value {|pool| pool.verify_active_connections! }
318
+ end
319
+
320
+ # Locate the connection of the nearest super class. This can be an
321
+ # active or defined connection: if it is the latter, it will be
322
+ # opened and set as the active connection for the class it was defined
323
+ # for (not necessarily the current class).
324
+ def retrieve_connection(klass) #:nodoc:
325
+ pool = retrieve_connection_pool(klass)
326
+ (pool && pool.connection) or raise ConnectionNotEstablished
327
+ end
328
+
329
+ # Returns true if a connection that's accessible to this class has
330
+ # already been opened.
331
+ def connected?(klass)
332
+ conn = retrieve_connection_pool(klass)
333
+ conn ? conn.connected? : false
334
+ end
335
+
336
+ # Remove the connection for this class. This will close the active
337
+ # connection and the defined connection (if they exist). The result
338
+ # can be used as an argument for establish_connection, for easily
339
+ # re-establishing the connection.
340
+ def remove_connection(klass)
341
+ pool = @connection_pools[klass.name]
342
+ @connection_pools.delete_if { |key, value| value == pool }
343
+ pool.disconnect! if pool
344
+ pool.spec.config if pool
345
+ end
346
+
347
+ def retrieve_connection_pool(klass)
348
+ pool = @connection_pools[klass.name]
349
+ return pool if pool
350
+ return nil if ActiveRecord::Base == klass
351
+ retrieve_connection_pool klass.superclass
352
+ end
353
+ end
354
+
355
+ class ConnectionManagement
356
+ def initialize(app)
357
+ @app = app
358
+ end
359
+
360
+ def call(env)
361
+ @app.call(env)
362
+ ensure
363
+ # Don't return connection (and peform implicit rollback) if
364
+ # this request is a part of integration test
365
+ unless env.key?("rack.test")
366
+ ActiveRecord::Base.clear_active_connections!
367
+ end
368
+ end
369
+ end
370
+ end
371
+ end
@@ -0,0 +1,139 @@
1
+ module ActiveRecord
2
+ class Base
3
+ class ConnectionSpecification #:nodoc:
4
+ attr_reader :config, :adapter_method
5
+ def initialize (config, adapter_method)
6
+ @config, @adapter_method = config, adapter_method
7
+ end
8
+ end
9
+
10
+ ##
11
+ # :singleton-method:
12
+ # The connection handler
13
+ cattr_accessor :connection_handler, :instance_writer => false
14
+ @@connection_handler = ConnectionAdapters::ConnectionHandler.new
15
+
16
+ # Returns the connection currently associated with the class. This can
17
+ # also be used to "borrow" the connection to do database work that isn't
18
+ # easily done without going straight to SQL.
19
+ def connection
20
+ self.class.connection
21
+ end
22
+
23
+ # Establishes the connection to the database. Accepts a hash as input where
24
+ # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
25
+ # example for regular databases (MySQL, Postgresql, etc):
26
+ #
27
+ # ActiveRecord::Base.establish_connection(
28
+ # :adapter => "mysql",
29
+ # :host => "localhost",
30
+ # :username => "myuser",
31
+ # :password => "mypass",
32
+ # :database => "somedatabase"
33
+ # )
34
+ #
35
+ # Example for SQLite database:
36
+ #
37
+ # ActiveRecord::Base.establish_connection(
38
+ # :adapter => "sqlite",
39
+ # :database => "path/to/dbfile"
40
+ # )
41
+ #
42
+ # Also accepts keys as strings (for parsing from YAML for example):
43
+ #
44
+ # ActiveRecord::Base.establish_connection(
45
+ # "adapter" => "sqlite",
46
+ # "database" => "path/to/dbfile"
47
+ # )
48
+ #
49
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
50
+ # may be returned on an error.
51
+ def self.establish_connection(spec = nil)
52
+ case spec
53
+ when nil
54
+ raise AdapterNotSpecified unless defined? RAILS_ENV
55
+ establish_connection(RAILS_ENV)
56
+ when ConnectionSpecification
57
+ @@connection_handler.establish_connection(name, spec)
58
+ when Symbol, String
59
+ if configuration = configurations[spec.to_s]
60
+ establish_connection(configuration)
61
+ else
62
+ raise AdapterNotSpecified, "#{spec} database is not configured"
63
+ end
64
+ else
65
+ spec = spec.symbolize_keys
66
+ unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
67
+
68
+ begin
69
+ require 'rubygems'
70
+ gem "activerecord-#{spec[:adapter]}-adapter"
71
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
72
+ rescue LoadError
73
+ begin
74
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
75
+ rescue LoadError
76
+ raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
77
+ end
78
+ end
79
+
80
+ adapter_method = "#{spec[:adapter]}_connection"
81
+ if !respond_to?(adapter_method)
82
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
83
+ end
84
+
85
+ remove_connection
86
+ establish_connection(ConnectionSpecification.new(spec, adapter_method))
87
+ end
88
+ end
89
+
90
+ class << self
91
+ # Deprecated and no longer has any effect.
92
+ def allow_concurrency
93
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency has been deprecated and no longer has any effect. Please remove all references to allow_concurrency.")
94
+ end
95
+
96
+ # Deprecated and no longer has any effect.
97
+ def allow_concurrency=(flag)
98
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency= has been deprecated and no longer has any effect. Please remove all references to allow_concurrency=.")
99
+ end
100
+
101
+ # Deprecated and no longer has any effect.
102
+ def verification_timeout
103
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout has been deprecated and no longer has any effect. Please remove all references to verification_timeout.")
104
+ end
105
+
106
+ # Deprecated and no longer has any effect.
107
+ def verification_timeout=(flag)
108
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout= has been deprecated and no longer has any effect. Please remove all references to verification_timeout=.")
109
+ end
110
+
111
+ # Returns the connection currently associated with the class. This can
112
+ # also be used to "borrow" the connection to do database work unrelated
113
+ # to any of the specific Active Records.
114
+ def connection
115
+ retrieve_connection
116
+ end
117
+
118
+ def connection_pool
119
+ connection_handler.retrieve_connection_pool(self)
120
+ end
121
+
122
+ def retrieve_connection
123
+ connection_handler.retrieve_connection(self)
124
+ end
125
+
126
+ # Returns true if +ActiveRecord+ is connected.
127
+ def connected?
128
+ connection_handler.connected?(self)
129
+ end
130
+
131
+ def remove_connection(klass = self)
132
+ connection_handler.remove_connection(klass)
133
+ end
134
+
135
+ delegate :clear_active_connections!, :clear_reloadable_connections!,
136
+ :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
137
+ end
138
+ end
139
+ end