activerecord 3.1.12 → 3.2.22.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +804 -338
- data/README.rdoc +3 -3
- data/examples/performance.rb +20 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +3 -6
- data/lib/active_record/associations/association.rb +13 -45
- data/lib/active_record/associations/association_scope.rb +3 -15
- data/lib/active_record/associations/belongs_to_association.rb +1 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -4
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +65 -32
- data/lib/active_record/associations/collection_proxy.rb +8 -41
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_association.rb +11 -7
- data/lib/active_record/associations/has_many_through_association.rb +19 -9
- data/lib/active_record/associations/has_one_association.rb +23 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
- data/lib/active_record/associations/join_dependency.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +14 -10
- data/lib/active_record/associations/through_association.rb +8 -4
- data/lib/active_record/associations.rb +92 -76
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +21 -11
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +73 -83
- data/lib/active_record/attribute_methods/serialization.rb +120 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods/write.rb +32 -6
- data/lib/active_record/attribute_methods.rb +231 -30
- data/lib/active_record/autosave_association.rb +44 -26
- data/lib/active_record/base.rb +227 -1708
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +39 -28
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
- data/lib/active_record/connection_adapters/column.rb +37 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +133 -581
- data/lib/active_record/connection_adapters/mysql_adapter.rb +136 -693
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +209 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +62 -35
- data/lib/active_record/counter_cache.rb +9 -4
- data/lib/active_record/dynamic_finder_match.rb +12 -0
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +86 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +57 -86
- data/lib/active_record/identity_map.rb +3 -4
- data/lib/active_record/inheritance.rb +174 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locking/optimistic.rb +33 -26
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +8 -4
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +68 -35
- data/lib/active_record/model_schema.rb +368 -0
- data/lib/active_record/nested_attributes.rb +60 -24
- data/lib/active_record/persistence.rb +57 -11
- data/lib/active_record/query_cache.rb +6 -6
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +37 -29
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +213 -117
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +7 -4
- data/lib/active_record/relation/calculations.rb +55 -16
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +16 -11
- data/lib/active_record/relation/predicate_builder.rb +8 -6
- data/lib/active_record/relation/query_methods.rb +75 -9
- data/lib/active_record/relation/spawn_methods.rb +48 -7
- data/lib/active_record/relation.rb +78 -32
- data/lib/active_record/result.rb +10 -4
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +12 -5
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +200 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +4 -45
- data/lib/active_record/session_store.rb +18 -16
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +17 -3
- data/lib/active_record/transactions.rb +27 -6
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +3 -3
- data/lib/active_record.rb +38 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +49 -28
- data/lib/active_record/named_scope.rb +0 -200
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'monitor'
|
3
3
|
require 'set'
|
4
|
-
require 'active_support/core_ext/module/
|
4
|
+
require 'active_support/core_ext/module/deprecation'
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
# Raised when a connection could not be obtained within the connection
|
@@ -54,13 +54,16 @@ module ActiveRecord
|
|
54
54
|
# your database connection configuration:
|
55
55
|
#
|
56
56
|
# * +pool+: number indicating size of connection pool (default 5)
|
57
|
-
# * +
|
58
|
-
# before giving up and raising a timeout error
|
57
|
+
# * +checkout _timeout+: number of seconds to block and wait for a
|
58
|
+
# connection before giving up and raising a timeout error
|
59
|
+
# (default 5 seconds). ('wait_timeout' supported for backwards
|
60
|
+
# compatibility, but conflicts with key used for different purpose
|
61
|
+
# by mysql2 adapter).
|
59
62
|
class ConnectionPool
|
63
|
+
include MonitorMixin
|
64
|
+
|
60
65
|
attr_accessor :automatic_reconnect
|
61
66
|
attr_reader :spec, :connections
|
62
|
-
attr_reader :columns, :columns_hash, :primary_keys, :tables
|
63
|
-
attr_reader :column_defaults
|
64
67
|
|
65
68
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
66
69
|
# object which describes database connection information (e.g. adapter,
|
@@ -69,88 +72,24 @@ module ActiveRecord
|
|
69
72
|
#
|
70
73
|
# The default ConnectionPool maximum size is 5.
|
71
74
|
def initialize(spec)
|
75
|
+
super()
|
76
|
+
|
72
77
|
@spec = spec
|
73
78
|
|
74
79
|
# The cache of reserved connections mapped to threads
|
75
80
|
@reserved_connections = {}
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
82
|
+
@queue = new_cond
|
83
|
+
# 'wait_timeout', the backward-compatible key, conflicts with spec key
|
84
|
+
# used by mysql2 for something entirely different, checkout_timeout
|
85
|
+
# preferred to avoid conflict and allow independent values.
|
86
|
+
@timeout = spec.config[:checkout_timeout] || spec.config[:wait_timeout] || 5
|
81
87
|
|
82
88
|
# default max pool size to 5
|
83
89
|
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
|
84
90
|
|
85
91
|
@connections = []
|
86
|
-
@checked_out = []
|
87
92
|
@automatic_reconnect = true
|
88
|
-
@tables = {}
|
89
|
-
@visitor = nil
|
90
|
-
|
91
|
-
@columns = Hash.new do |h, table_name|
|
92
|
-
h[table_name] = with_connection do |conn|
|
93
|
-
|
94
|
-
# Fetch a list of columns
|
95
|
-
conn.columns(table_name, "#{table_name} Columns").tap do |columns|
|
96
|
-
|
97
|
-
# set primary key information
|
98
|
-
columns.each do |column|
|
99
|
-
column.primary = column.name == primary_keys[table_name]
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
@columns_hash = Hash.new do |h, table_name|
|
106
|
-
h[table_name] = Hash[columns[table_name].map { |col|
|
107
|
-
[col.name, col]
|
108
|
-
}]
|
109
|
-
end
|
110
|
-
|
111
|
-
@column_defaults = Hash.new do |h, table_name|
|
112
|
-
h[table_name] = Hash[columns[table_name].map { |col|
|
113
|
-
[col.name, col.default]
|
114
|
-
}]
|
115
|
-
end
|
116
|
-
|
117
|
-
@primary_keys = Hash.new do |h, table_name|
|
118
|
-
h[table_name] = with_connection do |conn|
|
119
|
-
table_exists?(table_name) ? conn.primary_key(table_name) : 'id'
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# A cached lookup for table existence.
|
125
|
-
def table_exists?(name)
|
126
|
-
return true if @tables.key? name
|
127
|
-
|
128
|
-
with_connection do |conn|
|
129
|
-
conn.tables.each { |table| @tables[table] = true }
|
130
|
-
@tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
|
131
|
-
end
|
132
|
-
|
133
|
-
@tables.key? name
|
134
|
-
end
|
135
|
-
|
136
|
-
# Clears out internal caches:
|
137
|
-
#
|
138
|
-
# * columns
|
139
|
-
# * columns_hash
|
140
|
-
# * tables
|
141
|
-
def clear_cache!
|
142
|
-
@columns.clear
|
143
|
-
@columns_hash.clear
|
144
|
-
@column_defaults.clear
|
145
|
-
@tables.clear
|
146
|
-
end
|
147
|
-
|
148
|
-
# Clear out internal caches for table with +table_name+.
|
149
|
-
def clear_table_cache!(table_name)
|
150
|
-
@columns.delete table_name
|
151
|
-
@columns_hash.delete table_name
|
152
|
-
@column_defaults.delete table_name
|
153
|
-
@primary_keys.delete table_name
|
154
93
|
end
|
155
94
|
|
156
95
|
# Retrieve the connection associated with the current thread, or call
|
@@ -159,29 +98,34 @@ module ActiveRecord
|
|
159
98
|
# #connection can be called any number of times; the connection is
|
160
99
|
# held in a hash keyed by the thread id.
|
161
100
|
def connection
|
162
|
-
|
101
|
+
synchronize do
|
102
|
+
@reserved_connections[current_connection_id] ||= checkout
|
103
|
+
end
|
163
104
|
end
|
164
105
|
|
165
|
-
#
|
166
|
-
# pool.
|
106
|
+
# Is there an open connection that is being used for the current thread?
|
167
107
|
def active_connection?
|
168
|
-
|
108
|
+
synchronize do
|
109
|
+
@reserved_connections.fetch(current_connection_id) {
|
110
|
+
return false
|
111
|
+
}.in_use?
|
112
|
+
end
|
169
113
|
end
|
170
114
|
|
171
115
|
# Signal that the thread is finished with the current connection.
|
172
116
|
# #release_connection releases the connection-thread association
|
173
117
|
# and returns the connection to the pool.
|
174
118
|
def release_connection(with_id = current_connection_id)
|
175
|
-
conn = @reserved_connections.delete(with_id)
|
119
|
+
conn = synchronize { @reserved_connections.delete(with_id) }
|
176
120
|
checkin conn if conn
|
177
121
|
end
|
178
122
|
|
179
|
-
# If a connection already exists yield it to the block.
|
123
|
+
# If a connection already exists yield it to the block. If no connection
|
180
124
|
# exists checkout a connection, yield it to the block, and checkin the
|
181
125
|
# connection when finished.
|
182
126
|
def with_connection
|
183
127
|
connection_id = current_connection_id
|
184
|
-
fresh_connection = true unless
|
128
|
+
fresh_connection = true unless active_connection?
|
185
129
|
yield connection
|
186
130
|
ensure
|
187
131
|
release_connection(connection_id) if fresh_connection
|
@@ -189,43 +133,73 @@ module ActiveRecord
|
|
189
133
|
|
190
134
|
# Returns true if a connection has already been opened.
|
191
135
|
def connected?
|
192
|
-
|
136
|
+
synchronize { @connections.any? }
|
193
137
|
end
|
194
138
|
|
195
139
|
# Disconnects all connections in the pool, and clears the pool.
|
196
140
|
def disconnect!
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
141
|
+
synchronize do
|
142
|
+
@reserved_connections = {}
|
143
|
+
@connections.each do |conn|
|
144
|
+
checkin conn
|
145
|
+
conn.disconnect!
|
146
|
+
end
|
147
|
+
@connections = []
|
203
148
|
end
|
204
|
-
@connections = []
|
205
149
|
end
|
206
150
|
|
207
151
|
# Clears the cache which maps classes.
|
208
152
|
def clear_reloadable_connections!
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
153
|
+
synchronize do
|
154
|
+
@reserved_connections = {}
|
155
|
+
@connections.each do |conn|
|
156
|
+
checkin conn
|
157
|
+
conn.disconnect! if conn.requires_reloading?
|
158
|
+
end
|
159
|
+
@connections.delete_if do |conn|
|
160
|
+
conn.requires_reloading?
|
161
|
+
end
|
218
162
|
end
|
219
163
|
end
|
220
164
|
|
221
165
|
# Verify active connections and remove and disconnect connections
|
222
166
|
# associated with stale threads.
|
223
167
|
def verify_active_connections! #:nodoc:
|
224
|
-
|
225
|
-
|
226
|
-
connection
|
168
|
+
synchronize do
|
169
|
+
clear_stale_cached_connections!
|
170
|
+
@connections.each do |connection|
|
171
|
+
connection.verify!
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def columns
|
177
|
+
with_connection do |c|
|
178
|
+
c.schema_cache.columns
|
227
179
|
end
|
228
180
|
end
|
181
|
+
deprecate :columns
|
182
|
+
|
183
|
+
def columns_hash
|
184
|
+
with_connection do |c|
|
185
|
+
c.schema_cache.columns_hash
|
186
|
+
end
|
187
|
+
end
|
188
|
+
deprecate :columns_hash
|
189
|
+
|
190
|
+
def primary_keys
|
191
|
+
with_connection do |c|
|
192
|
+
c.schema_cache.primary_keys
|
193
|
+
end
|
194
|
+
end
|
195
|
+
deprecate :primary_keys
|
196
|
+
|
197
|
+
def clear_cache!
|
198
|
+
with_connection do |c|
|
199
|
+
c.schema_cache.clear!
|
200
|
+
end
|
201
|
+
end
|
202
|
+
deprecate :clear_cache!
|
229
203
|
|
230
204
|
# Return any checked-out connections back to the pool by threads that
|
231
205
|
# are no longer alive.
|
@@ -234,7 +208,13 @@ module ActiveRecord
|
|
234
208
|
t.alive?
|
235
209
|
}.map { |thread| thread.object_id }
|
236
210
|
keys.each do |key|
|
237
|
-
|
211
|
+
conn = @reserved_connections[key]
|
212
|
+
ActiveSupport::Deprecation.warn(<<-eowarn) if conn.in_use?
|
213
|
+
Database connections will not be closed automatically, please close your
|
214
|
+
database connection at the end of the thread by calling `close` on your
|
215
|
+
connection. For example: ActiveRecord::Base.connection.close
|
216
|
+
eowarn
|
217
|
+
checkin conn
|
238
218
|
@reserved_connections.delete(key)
|
239
219
|
end
|
240
220
|
end
|
@@ -256,27 +236,43 @@ module ActiveRecord
|
|
256
236
|
# - ConnectionTimeoutError: no connection can be obtained from the pool
|
257
237
|
# within the timeout period.
|
258
238
|
def checkout
|
259
|
-
|
260
|
-
|
239
|
+
synchronize do
|
240
|
+
waited_time = 0
|
241
|
+
|
261
242
|
loop do
|
262
|
-
conn =
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
@queue.wait(@timeout)
|
270
|
-
|
271
|
-
if(@checked_out.size < @connections.size)
|
272
|
-
next
|
273
|
-
else
|
274
|
-
clear_stale_cached_connections!
|
275
|
-
if @size == @checked_out.size
|
276
|
-
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
|
243
|
+
conn = @connections.find { |c| c.lease }
|
244
|
+
|
245
|
+
unless conn
|
246
|
+
if @connections.size < @size
|
247
|
+
conn = checkout_new_connection
|
248
|
+
conn.lease
|
277
249
|
end
|
278
250
|
end
|
279
251
|
|
252
|
+
if conn
|
253
|
+
checkout_and_verify conn
|
254
|
+
return conn
|
255
|
+
end
|
256
|
+
|
257
|
+
if waited_time >= @timeout
|
258
|
+
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout} (waited #{waited_time} seconds). The max pool size is currently #{@size}; consider increasing it."
|
259
|
+
end
|
260
|
+
|
261
|
+
# Sometimes our wait can end because a connection is available,
|
262
|
+
# but another thread can snatch it up first. If timeout hasn't
|
263
|
+
# passed but no connection is avail, looks like that happened --
|
264
|
+
# loop and wait again, for the time remaining on our timeout.
|
265
|
+
before_wait = Time.now
|
266
|
+
@queue.wait( [@timeout - waited_time, 0].max )
|
267
|
+
waited_time += (Time.now - before_wait)
|
268
|
+
|
269
|
+
# Will go away in Rails 4, when we don't clean up
|
270
|
+
# after leaked connections automatically anymore. Right now, clean
|
271
|
+
# up after we've returned from a 'wait' if it looks like it's
|
272
|
+
# needed, then loop and try again.
|
273
|
+
if(active_connections.size >= @connections.size)
|
274
|
+
clear_stale_cached_connections!
|
275
|
+
end
|
280
276
|
end
|
281
277
|
end
|
282
278
|
end
|
@@ -287,30 +283,36 @@ module ActiveRecord
|
|
287
283
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
288
284
|
# calling +checkout+ on this pool.
|
289
285
|
def checkin(conn)
|
290
|
-
|
286
|
+
synchronize do
|
291
287
|
conn.run_callbacks :checkin do
|
292
|
-
|
288
|
+
conn.expire
|
293
289
|
@queue.signal
|
294
290
|
end
|
291
|
+
|
292
|
+
release conn
|
295
293
|
end
|
296
294
|
end
|
297
295
|
|
298
|
-
synchronize :clear_reloadable_connections!, :verify_active_connections!,
|
299
|
-
:connected?, :disconnect!, :with => :@connection_mutex
|
300
|
-
|
301
296
|
private
|
302
297
|
|
303
|
-
def
|
304
|
-
|
298
|
+
def release(conn)
|
299
|
+
synchronize do
|
300
|
+
thread_id = nil
|
305
301
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
302
|
+
if @reserved_connections[current_connection_id] == conn
|
303
|
+
thread_id = current_connection_id
|
304
|
+
else
|
305
|
+
thread_id = @reserved_connections.keys.find { |k|
|
306
|
+
@reserved_connections[k] == conn
|
307
|
+
}
|
308
|
+
end
|
312
309
|
|
313
|
-
|
310
|
+
@reserved_connections.delete thread_id if thread_id
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def new_connection
|
315
|
+
ActiveRecord::Base.send(spec.adapter_method, spec.config)
|
314
316
|
end
|
315
317
|
|
316
318
|
def current_connection_id #:nodoc:
|
@@ -321,22 +323,21 @@ module ActiveRecord
|
|
321
323
|
raise ConnectionNotEstablished unless @automatic_reconnect
|
322
324
|
|
323
325
|
c = new_connection
|
326
|
+
c.pool = self
|
324
327
|
@connections << c
|
325
|
-
|
326
|
-
end
|
327
|
-
|
328
|
-
def checkout_existing_connection
|
329
|
-
c = (@connections - @checked_out).first
|
330
|
-
checkout_and_verify(c)
|
328
|
+
c
|
331
329
|
end
|
332
330
|
|
333
331
|
def checkout_and_verify(c)
|
334
332
|
c.run_callbacks :checkout do
|
335
333
|
c.verify!
|
336
|
-
@checked_out << c
|
337
334
|
end
|
338
335
|
c
|
339
336
|
end
|
337
|
+
|
338
|
+
def active_connections
|
339
|
+
@connections.find_all { |c| c.in_use? }
|
340
|
+
end
|
340
341
|
end
|
341
342
|
|
342
343
|
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
@@ -367,10 +368,12 @@ module ActiveRecord
|
|
367
368
|
|
368
369
|
def initialize(pools = {})
|
369
370
|
@connection_pools = pools
|
371
|
+
@class_to_pool = {}
|
370
372
|
end
|
371
373
|
|
372
374
|
def establish_connection(name, spec)
|
373
|
-
@connection_pools[
|
375
|
+
@connection_pools[spec] ||= ConnectionAdapters::ConnectionPool.new(spec)
|
376
|
+
@class_to_pool[name] = @connection_pools[spec]
|
374
377
|
end
|
375
378
|
|
376
379
|
# Returns true if there are any active connections among the connection
|
@@ -379,9 +382,7 @@ module ActiveRecord
|
|
379
382
|
connection_pools.values.any? { |pool| pool.active_connection? }
|
380
383
|
end
|
381
384
|
|
382
|
-
# Returns any connections in use by the current thread back to the pool
|
383
|
-
# and also returns connections to the pool cached by threads that are no
|
384
|
-
# longer alive.
|
385
|
+
# Returns any connections in use by the current thread back to the pool.
|
385
386
|
def clear_active_connections!
|
386
387
|
@connection_pools.each_value {|pool| pool.release_connection }
|
387
388
|
end
|
@@ -421,16 +422,17 @@ module ActiveRecord
|
|
421
422
|
# can be used as an argument for establish_connection, for easily
|
422
423
|
# re-establishing the connection.
|
423
424
|
def remove_connection(klass)
|
424
|
-
pool = @
|
425
|
+
pool = @class_to_pool.delete(klass.name)
|
425
426
|
return nil unless pool
|
426
427
|
|
428
|
+
@connection_pools.delete pool.spec
|
427
429
|
pool.automatic_reconnect = false
|
428
430
|
pool.disconnect!
|
429
431
|
pool.spec.config
|
430
432
|
end
|
431
433
|
|
432
434
|
def retrieve_connection_pool(klass)
|
433
|
-
pool = @
|
435
|
+
pool = @class_to_pool[klass.name]
|
434
436
|
return pool if pool
|
435
437
|
return nil if ActiveRecord::Base == klass
|
436
438
|
retrieve_connection_pool klass.superclass
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class Base
|
3
5
|
class ConnectionSpecification #:nodoc:
|
@@ -5,6 +7,75 @@ module ActiveRecord
|
|
5
7
|
def initialize (config, adapter_method)
|
6
8
|
@config, @adapter_method = config, adapter_method
|
7
9
|
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Builds a ConnectionSpecification from user input
|
13
|
+
class Resolver # :nodoc:
|
14
|
+
attr_reader :config, :klass, :configurations
|
15
|
+
|
16
|
+
def initialize(config, configurations)
|
17
|
+
@config = config
|
18
|
+
@configurations = configurations
|
19
|
+
end
|
20
|
+
|
21
|
+
def spec
|
22
|
+
case config
|
23
|
+
when nil
|
24
|
+
raise AdapterNotSpecified unless defined?(Rails.env)
|
25
|
+
resolve_string_connection Rails.env
|
26
|
+
when Symbol, String
|
27
|
+
resolve_string_connection config.to_s
|
28
|
+
when Hash
|
29
|
+
resolve_hash_connection config
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def resolve_string_connection(spec) # :nodoc:
|
35
|
+
hash = configurations.fetch(spec) do |k|
|
36
|
+
connection_url_to_hash(k)
|
37
|
+
end
|
38
|
+
|
39
|
+
raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
|
40
|
+
|
41
|
+
resolve_hash_connection hash
|
42
|
+
end
|
43
|
+
|
44
|
+
def resolve_hash_connection(spec) # :nodoc:
|
45
|
+
spec = spec.symbolize_keys
|
46
|
+
|
47
|
+
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
|
48
|
+
|
49
|
+
begin
|
50
|
+
require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
|
51
|
+
rescue LoadError => e
|
52
|
+
raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace
|
53
|
+
end
|
54
|
+
|
55
|
+
adapter_method = "#{spec[:adapter]}_connection"
|
56
|
+
|
57
|
+
ConnectionSpecification.new(spec, adapter_method)
|
58
|
+
end
|
59
|
+
|
60
|
+
def connection_url_to_hash(url) # :nodoc:
|
61
|
+
config = URI.parse url
|
62
|
+
adapter = config.scheme
|
63
|
+
adapter = "postgresql" if adapter == "postgres"
|
64
|
+
spec = { :adapter => adapter,
|
65
|
+
:username => config.user,
|
66
|
+
:password => config.password,
|
67
|
+
:port => config.port,
|
68
|
+
:database => config.path.sub(%r{^/},""),
|
69
|
+
:host => config.host }
|
70
|
+
spec.reject!{ |_,value| value.blank? }
|
71
|
+
spec.map { |key,value| spec[key] = URI.unescape(value) if value.is_a?(String) }
|
72
|
+
if config.query
|
73
|
+
options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
|
74
|
+
spec.merge!(options)
|
75
|
+
end
|
76
|
+
spec
|
77
|
+
end
|
78
|
+
end
|
8
79
|
end
|
9
80
|
|
10
81
|
##
|
@@ -46,39 +117,24 @@ module ActiveRecord
|
|
46
117
|
# "database" => "path/to/dbfile"
|
47
118
|
# )
|
48
119
|
#
|
120
|
+
# Or a URL:
|
121
|
+
#
|
122
|
+
# ActiveRecord::Base.establish_connection(
|
123
|
+
# "postgres://myuser:mypass@localhost/somedatabase"
|
124
|
+
# )
|
125
|
+
#
|
49
126
|
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
|
50
127
|
# may be returned on an error.
|
51
|
-
def self.establish_connection(spec =
|
52
|
-
|
53
|
-
|
54
|
-
raise AdapterNotSpecified unless defined?(Rails.env)
|
55
|
-
establish_connection(Rails.env)
|
56
|
-
when ConnectionSpecification
|
57
|
-
self.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
|
128
|
+
def self.establish_connection(spec = ENV["DATABASE_URL"])
|
129
|
+
resolver = ConnectionSpecification::Resolver.new spec, configurations
|
130
|
+
spec = resolver.spec
|
67
131
|
|
68
|
-
|
69
|
-
|
70
|
-
rescue LoadError => e
|
71
|
-
raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e})"
|
72
|
-
end
|
73
|
-
|
74
|
-
adapter_method = "#{spec[:adapter]}_connection"
|
75
|
-
unless respond_to?(adapter_method)
|
76
|
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
|
77
|
-
end
|
78
|
-
|
79
|
-
remove_connection
|
80
|
-
establish_connection(ConnectionSpecification.new(spec, adapter_method))
|
132
|
+
unless respond_to?(spec.adapter_method)
|
133
|
+
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
|
81
134
|
end
|
135
|
+
|
136
|
+
remove_connection
|
137
|
+
connection_handler.establish_connection name, spec
|
82
138
|
end
|
83
139
|
|
84
140
|
class << self
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/module/deprecation'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module ConnectionAdapters # :nodoc:
|
5
3
|
module DatabaseStatements
|
@@ -53,7 +51,7 @@ module ActiveRecord
|
|
53
51
|
undef_method :execute
|
54
52
|
|
55
53
|
# Executes +sql+ statement in the context of this connection using
|
56
|
-
# +binds+ as the bind substitutes.
|
54
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
57
55
|
# the executed +sql+ statement.
|
58
56
|
def exec_query(sql, name = 'SQL', binds = [])
|
59
57
|
end
|
@@ -256,44 +254,19 @@ module ActiveRecord
|
|
256
254
|
# done if the transaction block raises an exception or returns false.
|
257
255
|
def rollback_db_transaction() end
|
258
256
|
|
259
|
-
# Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
|
260
|
-
# fragment that has the same semantics as LIMIT and OFFSET.
|
261
|
-
#
|
262
|
-
# +options+ must be a Hash which contains a +:limit+ option
|
263
|
-
# and an +:offset+ option.
|
264
|
-
#
|
265
|
-
# This method *modifies* the +sql+ parameter.
|
266
|
-
#
|
267
|
-
# This method is deprecated!! Stop using it!
|
268
|
-
#
|
269
|
-
# ===== Examples
|
270
|
-
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
|
271
|
-
# generates
|
272
|
-
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
|
273
|
-
def add_limit_offset!(sql, options)
|
274
|
-
if limit = options[:limit]
|
275
|
-
sql << " LIMIT #{sanitize_limit(limit)}"
|
276
|
-
end
|
277
|
-
if offset = options[:offset]
|
278
|
-
sql << " OFFSET #{offset.to_i}"
|
279
|
-
end
|
280
|
-
sql
|
281
|
-
end
|
282
|
-
deprecate :add_limit_offset!
|
283
|
-
|
284
257
|
def default_sequence_name(table, column)
|
285
258
|
nil
|
286
259
|
end
|
287
260
|
|
288
261
|
# Set the sequence to the max value of the table's column.
|
289
262
|
def reset_sequence!(table, column, sequence = nil)
|
290
|
-
# Do nothing by default.
|
263
|
+
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
291
264
|
end
|
292
265
|
|
293
266
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
294
267
|
# something beyond a simple insert (eg. Oracle).
|
295
268
|
def insert_fixture(fixture, table_name)
|
296
|
-
columns =
|
269
|
+
columns = schema_cache.columns_hash(table_name)
|
297
270
|
|
298
271
|
key_list = []
|
299
272
|
value_list = fixture.map do |name, value|
|
@@ -319,10 +292,10 @@ module ActiveRecord
|
|
319
292
|
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
320
293
|
#
|
321
294
|
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
322
|
-
# should look like an integer, or a comma-delimited list of integers, or
|
295
|
+
# should look like an integer, or a comma-delimited list of integers, or
|
323
296
|
# an Arel SQL literal.
|
324
297
|
#
|
325
|
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
298
|
+
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
326
299
|
# Returns the sanitized limit parameter, either as an integer, or as a
|
327
300
|
# string which contains a comma-delimited list of integers.
|
328
301
|
def sanitize_limit(limit)
|
@@ -370,7 +343,7 @@ module ActiveRecord
|
|
370
343
|
|
371
344
|
# Send a rollback message to all records after they have been rolled back. If rollback
|
372
345
|
# is false, only rollback records since the last save point.
|
373
|
-
def rollback_transaction_records(rollback)
|
346
|
+
def rollback_transaction_records(rollback)
|
374
347
|
if rollback
|
375
348
|
records = @_current_transaction_records.flatten
|
376
349
|
@_current_transaction_records.clear
|
@@ -390,7 +363,7 @@ module ActiveRecord
|
|
390
363
|
end
|
391
364
|
|
392
365
|
# Send a commit message to all records after they have been committed.
|
393
|
-
def commit_transaction_records
|
366
|
+
def commit_transaction_records
|
394
367
|
records = @_current_transaction_records.flatten
|
395
368
|
@_current_transaction_records.clear
|
396
369
|
unless records.blank?
|