activerecord 1.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,638 @@
1
+ require 'thread'
2
+ require 'thread_safe'
3
+ require 'monitor'
4
+ require 'set'
5
+
6
+ module ActiveRecord
7
+ # Raised when a connection could not be obtained within the connection
8
+ # acquisition timeout period: because max connections in pool
9
+ # are in use.
10
+ class ConnectionTimeoutError < ConnectionNotEstablished
11
+ end
12
+
13
+ module ConnectionAdapters
14
+ # Connection pool base class for managing Active Record database
15
+ # connections.
16
+ #
17
+ # == Introduction
18
+ #
19
+ # A connection pool synchronizes thread access to a limited number of
20
+ # database connections. The basic idea is that each thread checks out a
21
+ # database connection from the pool, uses that connection, and checks the
22
+ # connection back in. ConnectionPool is completely thread-safe, and will
23
+ # ensure that a connection cannot be used by two threads at the same time,
24
+ # as long as ConnectionPool's contract is correctly followed. It will also
25
+ # handle cases in which there are more threads than connections: if all
26
+ # connections have been checked out, and a thread tries to checkout a
27
+ # connection anyway, then ConnectionPool will wait until some other thread
28
+ # has checked in a connection.
29
+ #
30
+ # == Obtaining (checking out) a connection
31
+ #
32
+ # Connections can be obtained and used from a connection pool in several
33
+ # ways:
34
+ #
35
+ # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
36
+ # earlier (pre-connection-pooling). Eventually, when you're done with
37
+ # the connection(s) and wish it to be returned to the pool, you call
38
+ # ActiveRecord::Base.clear_active_connections!. This will be the
39
+ # default behavior for Active Record when used in conjunction with
40
+ # Action Pack's request handling cycle.
41
+ # 2. Manually check out a connection from the pool with
42
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
43
+ # returning this connection to the pool when finished by calling
44
+ # ActiveRecord::Base.connection_pool.checkin(connection).
45
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
46
+ # obtains a connection, yields it as the sole argument to the block,
47
+ # and returns it to the pool after the block completes.
48
+ #
49
+ # Connections in the pool are actually AbstractAdapter objects (or objects
50
+ # compatible with AbstractAdapter's interface).
51
+ #
52
+ # == Options
53
+ #
54
+ # There are several connection-pooling-related options that you can add to
55
+ # your database connection configuration:
56
+ #
57
+ # * +pool+: number indicating size of connection pool (default 5)
58
+ # * +checkout_timeout+: number of seconds to block and wait for a connection
59
+ # before giving up and raising a timeout error (default 5 seconds).
60
+ # * +reaping_frequency+: frequency in seconds to periodically run the
61
+ # Reaper, which attempts to find and close dead connections, which can
62
+ # occur if a programmer forgets to close a connection at the end of a
63
+ # thread or a thread dies unexpectedly. (Default nil, which means don't
64
+ # run the Reaper).
65
+ # * +dead_connection_timeout+: number of seconds from last checkout
66
+ # after which the Reaper will consider a connection reapable. (default
67
+ # 5 seconds).
68
+ class ConnectionPool
69
+ # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
70
+ # with which it shares a Monitor. But could be a generic Queue.
71
+ #
72
+ # The Queue in stdlib's 'thread' could replace this class except
73
+ # stdlib's doesn't support waiting with a timeout.
74
+ class Queue
75
+ def initialize(lock = Monitor.new)
76
+ @lock = lock
77
+ @cond = @lock.new_cond
78
+ @num_waiting = 0
79
+ @queue = []
80
+ end
81
+
82
+ # Test if any threads are currently waiting on the queue.
83
+ def any_waiting?
84
+ synchronize do
85
+ @num_waiting > 0
86
+ end
87
+ end
88
+
89
+ # Return the number of threads currently waiting on this
90
+ # queue.
91
+ def num_waiting
92
+ synchronize do
93
+ @num_waiting
94
+ end
95
+ end
96
+
97
+ # Add +element+ to the queue. Never blocks.
98
+ def add(element)
99
+ synchronize do
100
+ @queue.push element
101
+ @cond.signal
102
+ end
103
+ end
104
+
105
+ # If +element+ is in the queue, remove and return it, or nil.
106
+ def delete(element)
107
+ synchronize do
108
+ @queue.delete(element)
109
+ end
110
+ end
111
+
112
+ # Remove all elements from the queue.
113
+ def clear
114
+ synchronize do
115
+ @queue.clear
116
+ end
117
+ end
118
+
119
+ # Remove the head of the queue.
120
+ #
121
+ # If +timeout+ is not given, remove and return the head the
122
+ # queue if the number of available elements is strictly
123
+ # greater than the number of threads currently waiting (that
124
+ # is, don't jump ahead in line). Otherwise, return nil.
125
+ #
126
+ # If +timeout+ is given, block if it there is no element
127
+ # available, waiting up to +timeout+ seconds for an element to
128
+ # become available.
129
+ #
130
+ # Raises:
131
+ # - ConnectionTimeoutError if +timeout+ is given and no element
132
+ # becomes available after +timeout+ seconds,
133
+ def poll(timeout = nil)
134
+ synchronize do
135
+ if timeout
136
+ no_wait_poll || wait_poll(timeout)
137
+ else
138
+ no_wait_poll
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def synchronize(&block)
146
+ @lock.synchronize(&block)
147
+ end
148
+
149
+ # Test if the queue currently contains any elements.
150
+ def any?
151
+ !@queue.empty?
152
+ end
153
+
154
+ # A thread can remove an element from the queue without
155
+ # waiting if an only if the number of currently available
156
+ # connections is strictly greater than the number of waiting
157
+ # threads.
158
+ def can_remove_no_wait?
159
+ @queue.size > @num_waiting
160
+ end
161
+
162
+ # Removes and returns the head of the queue if possible, or nil.
163
+ def remove
164
+ @queue.shift
165
+ end
166
+
167
+ # Remove and return the head the queue if the number of
168
+ # available elements is strictly greater than the number of
169
+ # threads currently waiting. Otherwise, return nil.
170
+ def no_wait_poll
171
+ remove if can_remove_no_wait?
172
+ end
173
+
174
+ # Waits on the queue up to +timeout+ seconds, then removes and
175
+ # returns the head of the queue.
176
+ def wait_poll(timeout)
177
+ @num_waiting += 1
178
+
179
+ t0 = Time.now
180
+ elapsed = 0
181
+ loop do
182
+ @cond.wait(timeout - elapsed)
183
+
184
+ return remove if any?
185
+
186
+ elapsed = Time.now - t0
187
+ if elapsed >= timeout
188
+ msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
189
+ [timeout, elapsed]
190
+ raise ConnectionTimeoutError, msg
191
+ end
192
+ end
193
+ ensure
194
+ @num_waiting -= 1
195
+ end
196
+ end
197
+
198
+ # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
199
+ # A reaper instantiated with a nil frequency will never reap the
200
+ # connection pool.
201
+ #
202
+ # Configure the frequency by setting "reaping_frequency" in your
203
+ # database yaml file.
204
+ class Reaper
205
+ attr_reader :pool, :frequency
206
+
207
+ def initialize(pool, frequency)
208
+ @pool = pool
209
+ @frequency = frequency
210
+ end
211
+
212
+ def run
213
+ return unless frequency
214
+ Thread.new(frequency, pool) { |t, p|
215
+ while true
216
+ sleep t
217
+ p.reap
218
+ end
219
+ }
220
+ end
221
+ end
222
+
223
+ include MonitorMixin
224
+
225
+ attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout
226
+ attr_reader :spec, :connections, :size, :reaper
227
+
228
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
229
+ # object which describes database connection information (e.g. adapter,
230
+ # host name, username, password, etc), as well as the maximum size for
231
+ # this ConnectionPool.
232
+ #
233
+ # The default ConnectionPool maximum size is 5.
234
+ def initialize(spec)
235
+ super()
236
+
237
+ @spec = spec
238
+
239
+ @checkout_timeout = spec.config[:checkout_timeout] || 5
240
+ @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
241
+ @reaper = Reaper.new self, spec.config[:reaping_frequency]
242
+ @reaper.run
243
+
244
+ # default max pool size to 5
245
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
246
+
247
+ # The cache of reserved connections mapped to threads
248
+ @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
249
+
250
+ @connections = []
251
+ @automatic_reconnect = true
252
+
253
+ @available = Queue.new self
254
+ end
255
+
256
+ # Retrieve the connection associated with the current thread, or call
257
+ # #checkout to obtain one if necessary.
258
+ #
259
+ # #connection can be called any number of times; the connection is
260
+ # held in a hash keyed by the thread id.
261
+ def connection
262
+ # this is correctly done double-checked locking
263
+ # (ThreadSafe::Cache's lookups have volatile semantics)
264
+ @reserved_connections[current_connection_id] || synchronize do
265
+ @reserved_connections[current_connection_id] ||= checkout
266
+ end
267
+ end
268
+
269
+ # Is there an open connection that is being used for the current thread?
270
+ def active_connection?
271
+ synchronize do
272
+ @reserved_connections.fetch(current_connection_id) {
273
+ return false
274
+ }.in_use?
275
+ end
276
+ end
277
+
278
+ # Signal that the thread is finished with the current connection.
279
+ # #release_connection releases the connection-thread association
280
+ # and returns the connection to the pool.
281
+ def release_connection(with_id = current_connection_id)
282
+ synchronize do
283
+ conn = @reserved_connections.delete(with_id)
284
+ checkin conn if conn
285
+ end
286
+ end
287
+
288
+ # If a connection already exists yield it to the block. If no connection
289
+ # exists checkout a connection, yield it to the block, and checkin the
290
+ # connection when finished.
291
+ def with_connection
292
+ connection_id = current_connection_id
293
+ fresh_connection = true unless active_connection?
294
+ yield connection
295
+ ensure
296
+ release_connection(connection_id) if fresh_connection
297
+ end
298
+
299
+ # Returns true if a connection has already been opened.
300
+ def connected?
301
+ synchronize { @connections.any? }
302
+ end
303
+
304
+ # Disconnects all connections in the pool, and clears the pool.
305
+ def disconnect!
306
+ synchronize do
307
+ @reserved_connections.clear
308
+ @connections.each do |conn|
309
+ checkin conn
310
+ conn.disconnect!
311
+ end
312
+ @connections = []
313
+ @available.clear
314
+ end
315
+ end
316
+
317
+ # Clears the cache which maps classes.
318
+ def clear_reloadable_connections!
319
+ synchronize do
320
+ @reserved_connections.clear
321
+ @connections.each do |conn|
322
+ checkin conn
323
+ conn.disconnect! if conn.requires_reloading?
324
+ end
325
+ @connections.delete_if do |conn|
326
+ conn.requires_reloading?
327
+ end
328
+ @available.clear
329
+ @connections.each do |conn|
330
+ @available.add conn
331
+ end
332
+ end
333
+ end
334
+
335
+ def clear_stale_cached_connections! # :nodoc:
336
+ reap
337
+ end
338
+ deprecate :clear_stale_cached_connections! => "Please use #reap instead"
339
+
340
+ # Check-out a database connection from the pool, indicating that you want
341
+ # to use it. You should call #checkin when you no longer need this.
342
+ #
343
+ # This is done by either returning and leasing existing connection, or by
344
+ # creating a new connection and leasing it.
345
+ #
346
+ # If all connections are leased and the pool is at capacity (meaning the
347
+ # number of currently leased connections is greater than or equal to the
348
+ # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
349
+ #
350
+ # Returns: an AbstractAdapter object.
351
+ #
352
+ # Raises:
353
+ # - ConnectionTimeoutError: no connection can be obtained from the pool.
354
+ def checkout
355
+ synchronize do
356
+ conn = acquire_connection
357
+ conn.lease
358
+ checkout_and_verify(conn)
359
+ end
360
+ end
361
+
362
+ # Check-in a database connection back into the pool, indicating that you
363
+ # no longer need this connection.
364
+ #
365
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
366
+ # calling +checkout+ on this pool.
367
+ def checkin(conn)
368
+ synchronize do
369
+ conn.run_callbacks :checkin do
370
+ conn.expire
371
+ end
372
+
373
+ release conn
374
+
375
+ @available.add conn
376
+ end
377
+ end
378
+
379
+ # Remove a connection from the connection pool. The connection will
380
+ # remain open and active but will no longer be managed by this pool.
381
+ def remove(conn)
382
+ synchronize do
383
+ @connections.delete conn
384
+ @available.delete conn
385
+
386
+ # FIXME: we might want to store the key on the connection so that removing
387
+ # from the reserved hash will be a little easier.
388
+ release conn
389
+
390
+ @available.add checkout_new_connection if @available.any_waiting?
391
+ end
392
+ end
393
+
394
+ # Removes dead connections from the pool. A dead connection can occur
395
+ # if a programmer forgets to close a connection at the end of a thread
396
+ # or a thread dies unexpectedly.
397
+ def reap
398
+ synchronize do
399
+ stale = Time.now - @dead_connection_timeout
400
+ connections.dup.each do |conn|
401
+ if conn.in_use? && stale > conn.last_use && !conn.active?
402
+ remove conn
403
+ end
404
+ end
405
+ end
406
+ end
407
+
408
+ private
409
+
410
+ # Acquire a connection by one of 1) immediately removing one
411
+ # from the queue of available connections, 2) creating a new
412
+ # connection if the pool is not at capacity, 3) waiting on the
413
+ # queue for a connection to become available.
414
+ #
415
+ # Raises:
416
+ # - ConnectionTimeoutError if a connection could not be acquired
417
+ def acquire_connection
418
+ if conn = @available.poll
419
+ conn
420
+ elsif @connections.size < @size
421
+ checkout_new_connection
422
+ else
423
+ @available.poll(@checkout_timeout)
424
+ end
425
+ end
426
+
427
+ def release(conn)
428
+ thread_id = if @reserved_connections[current_connection_id] == conn
429
+ current_connection_id
430
+ else
431
+ @reserved_connections.keys.find { |k|
432
+ @reserved_connections[k] == conn
433
+ }
434
+ end
435
+
436
+ @reserved_connections.delete thread_id if thread_id
437
+ end
438
+
439
+ def new_connection
440
+ Base.send(spec.adapter_method, spec.config)
441
+ end
442
+
443
+ def current_connection_id #:nodoc:
444
+ Base.connection_id ||= Thread.current.object_id
445
+ end
446
+
447
+ def checkout_new_connection
448
+ raise ConnectionNotEstablished unless @automatic_reconnect
449
+
450
+ c = new_connection
451
+ c.pool = self
452
+ @connections << c
453
+ c
454
+ end
455
+
456
+ def checkout_and_verify(c)
457
+ c.run_callbacks :checkout do
458
+ c.verify!
459
+ end
460
+ c
461
+ end
462
+ end
463
+
464
+ # ConnectionHandler is a collection of ConnectionPool objects. It is used
465
+ # for keeping separate connection pools for Active Record models that connect
466
+ # to different databases.
467
+ #
468
+ # For example, suppose that you have 5 models, with the following hierarchy:
469
+ #
470
+ # |
471
+ # +-- Book
472
+ # | |
473
+ # | +-- ScaryBook
474
+ # | +-- GoodBook
475
+ # +-- Author
476
+ # +-- BankAccount
477
+ #
478
+ # Suppose that Book is to connect to a separate database (i.e. one other
479
+ # than the default database). Then Book, ScaryBook and GoodBook will all use
480
+ # the same connection pool. Likewise, Author and BankAccount will use the
481
+ # same connection pool. However, the connection pool used by Author/BankAccount
482
+ # is not the same as the one used by Book/ScaryBook/GoodBook.
483
+ #
484
+ # Normally there is only a single ConnectionHandler instance, accessible via
485
+ # ActiveRecord::Base.connection_handler. Active Record models use this to
486
+ # determine the connection pool that they should use.
487
+ class ConnectionHandler
488
+ def initialize
489
+ # These caches are keyed by klass.name, NOT klass. Keying them by klass
490
+ # alone would lead to memory leaks in development mode as all previous
491
+ # instances of the class would stay in memory.
492
+ @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
493
+ h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
494
+ end
495
+ @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
496
+ h[k] = ThreadSafe::Cache.new
497
+ end
498
+ end
499
+
500
+ def connection_pool_list
501
+ owner_to_pool.values.compact
502
+ end
503
+
504
+ def connection_pools
505
+ ActiveSupport::Deprecation.warn(
506
+ "In the next release, this will return the same as #connection_pool_list. " \
507
+ "(An array of pools, rather than a hash mapping specs to pools.)"
508
+ )
509
+ Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
510
+ end
511
+
512
+ def establish_connection(owner, spec)
513
+ @class_to_pool.clear
514
+ raise RuntimeError, "Anonymous class is not allowed." unless owner.name
515
+ owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
516
+ end
517
+
518
+ # Returns true if there are any active connections among the connection
519
+ # pools that the ConnectionHandler is managing.
520
+ def active_connections?
521
+ connection_pool_list.any?(&:active_connection?)
522
+ end
523
+
524
+ # Returns any connections in use by the current thread back to the pool,
525
+ # and also returns connections to the pool cached by threads that are no
526
+ # longer alive.
527
+ def clear_active_connections!
528
+ connection_pool_list.each(&:release_connection)
529
+ end
530
+
531
+ # Clears the cache which maps classes.
532
+ def clear_reloadable_connections!
533
+ connection_pool_list.each(&:clear_reloadable_connections!)
534
+ end
535
+
536
+ def clear_all_connections!
537
+ connection_pool_list.each(&:disconnect!)
538
+ end
539
+
540
+ # Locate the connection of the nearest super class. This can be an
541
+ # active or defined connection: if it is the latter, it will be
542
+ # opened and set as the active connection for the class it was defined
543
+ # for (not necessarily the current class).
544
+ def retrieve_connection(klass) #:nodoc:
545
+ pool = retrieve_connection_pool(klass)
546
+ (pool && pool.connection) or raise ConnectionNotEstablished
547
+ end
548
+
549
+ # Returns true if a connection that's accessible to this class has
550
+ # already been opened.
551
+ def connected?(klass)
552
+ conn = retrieve_connection_pool(klass)
553
+ conn && conn.connected?
554
+ end
555
+
556
+ # Remove the connection for this class. This will close the active
557
+ # connection and the defined connection (if they exist). The result
558
+ # can be used as an argument for establish_connection, for easily
559
+ # re-establishing the connection.
560
+ def remove_connection(owner)
561
+ if pool = owner_to_pool.delete(owner.name)
562
+ @class_to_pool.clear
563
+ pool.automatic_reconnect = false
564
+ pool.disconnect!
565
+ pool.spec.config
566
+ end
567
+ end
568
+
569
+ # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
570
+ # This makes retrieving the connection pool O(1) once the process is warm.
571
+ # When a connection is established or removed, we invalidate the cache.
572
+ #
573
+ # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
574
+ # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
575
+ # #fetch is significantly slower than #[]. So in the nil case, no caching will
576
+ # take place, but that's ok since the nil case is not the common one that we wish
577
+ # to optimise for.
578
+ def retrieve_connection_pool(klass)
579
+ class_to_pool[klass.name] ||= begin
580
+ until pool = pool_for(klass)
581
+ klass = klass.superclass
582
+ break unless klass <= Base
583
+ end
584
+
585
+ class_to_pool[klass.name] = pool
586
+ end
587
+ end
588
+
589
+ private
590
+
591
+ def owner_to_pool
592
+ @owner_to_pool[Process.pid]
593
+ end
594
+
595
+ def class_to_pool
596
+ @class_to_pool[Process.pid]
597
+ end
598
+
599
+ def pool_for(owner)
600
+ owner_to_pool.fetch(owner.name) {
601
+ if ancestor_pool = pool_from_any_process_for(owner)
602
+ # A connection was established in an ancestor process that must have
603
+ # subsequently forked. We can't reuse the connection, but we can copy
604
+ # the specification and establish a new connection with it.
605
+ establish_connection owner, ancestor_pool.spec
606
+ else
607
+ owner_to_pool[owner.name] = nil
608
+ end
609
+ }
610
+ end
611
+
612
+ def pool_from_any_process_for(owner)
613
+ owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
614
+ owner_to_pool && owner_to_pool[owner.name]
615
+ end
616
+ end
617
+
618
+ class ConnectionManagement
619
+ def initialize(app)
620
+ @app = app
621
+ end
622
+
623
+ def call(env)
624
+ testing = env.key?('rack.test')
625
+
626
+ response = @app.call(env)
627
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
628
+ ActiveRecord::Base.clear_active_connections! unless testing
629
+ end
630
+
631
+ response
632
+ rescue
633
+ ActiveRecord::Base.clear_active_connections! unless testing
634
+ raise
635
+ end
636
+ end
637
+ end
638
+ end
@@ -0,0 +1,67 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseLimits
4
+
5
+ # Returns the maximum length of a table alias.
6
+ def table_alias_length
7
+ 255
8
+ end
9
+
10
+ # Returns the maximum length of a column name.
11
+ def column_name_length
12
+ 64
13
+ end
14
+
15
+ # Returns the maximum length of a table name.
16
+ def table_name_length
17
+ 64
18
+ end
19
+
20
+ # Returns the maximum allowed length for an index name. This
21
+ # limit is enforced by rails and Is less than or equal to
22
+ # <tt>index_name_length</tt>. The gap between
23
+ # <tt>index_name_length</tt> is to allow internal rails
24
+ # operations to use prefixes in temporary operations.
25
+ def allowed_index_name_length
26
+ index_name_length
27
+ end
28
+
29
+ # Returns the maximum length of an index name.
30
+ def index_name_length
31
+ 64
32
+ end
33
+
34
+ # Returns the maximum number of columns per table.
35
+ def columns_per_table
36
+ 1024
37
+ end
38
+
39
+ # Returns the maximum number of indexes per table.
40
+ def indexes_per_table
41
+ 16
42
+ end
43
+
44
+ # Returns the maximum number of columns in a multicolumn index.
45
+ def columns_per_multicolumn_index
46
+ 16
47
+ end
48
+
49
+ # Returns the maximum number of elements in an IN (x,y,z) clause.
50
+ # nil means no limit.
51
+ def in_clause_length
52
+ nil
53
+ end
54
+
55
+ # Returns the maximum length of an SQL query.
56
+ def sql_query_length
57
+ 1048575
58
+ end
59
+
60
+ # Returns maximum number of joins in a single query.
61
+ def joins_per_query
62
+ 256
63
+ end
64
+
65
+ end
66
+ end
67
+ end