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.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +804 -338
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +20 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +3 -6
  7. data/lib/active_record/associations/association.rb +13 -45
  8. data/lib/active_record/associations/association_scope.rb +3 -15
  9. data/lib/active_record/associations/belongs_to_association.rb +1 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
  11. data/lib/active_record/associations/builder/association.rb +6 -4
  12. data/lib/active_record/associations/builder/belongs_to.rb +7 -4
  13. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +4 -4
  15. data/lib/active_record/associations/builder/has_one.rb +5 -6
  16. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  17. data/lib/active_record/associations/collection_association.rb +65 -32
  18. data/lib/active_record/associations/collection_proxy.rb +8 -41
  19. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
  20. data/lib/active_record/associations/has_many_association.rb +11 -7
  21. data/lib/active_record/associations/has_many_through_association.rb +19 -9
  22. data/lib/active_record/associations/has_one_association.rb +23 -13
  23. data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
  24. data/lib/active_record/associations/join_dependency.rb +3 -3
  25. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  26. data/lib/active_record/associations/preloader.rb +14 -10
  27. data/lib/active_record/associations/through_association.rb +8 -4
  28. data/lib/active_record/associations.rb +92 -76
  29. data/lib/active_record/attribute_assignment.rb +221 -0
  30. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  31. data/lib/active_record/attribute_methods/dirty.rb +21 -11
  32. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  33. data/lib/active_record/attribute_methods/read.rb +73 -83
  34. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  36. data/lib/active_record/attribute_methods/write.rb +32 -6
  37. data/lib/active_record/attribute_methods.rb +231 -30
  38. data/lib/active_record/autosave_association.rb +44 -26
  39. data/lib/active_record/base.rb +227 -1708
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
  41. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -34
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  45. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +39 -28
  46. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  49. data/lib/active_record/connection_adapters/column.rb +37 -11
  50. data/lib/active_record/connection_adapters/mysql2_adapter.rb +133 -581
  51. data/lib/active_record/connection_adapters/mysql_adapter.rb +136 -693
  52. data/lib/active_record/connection_adapters/postgresql_adapter.rb +209 -97
  53. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  54. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  55. data/lib/active_record/connection_adapters/sqlite_adapter.rb +62 -35
  56. data/lib/active_record/counter_cache.rb +9 -4
  57. data/lib/active_record/dynamic_finder_match.rb +12 -0
  58. data/lib/active_record/dynamic_matchers.rb +84 -0
  59. data/lib/active_record/errors.rb +11 -1
  60. data/lib/active_record/explain.rb +86 -0
  61. data/lib/active_record/explain_subscriber.rb +25 -0
  62. data/lib/active_record/fixtures/file.rb +65 -0
  63. data/lib/active_record/fixtures.rb +57 -86
  64. data/lib/active_record/identity_map.rb +3 -4
  65. data/lib/active_record/inheritance.rb +174 -0
  66. data/lib/active_record/integration.rb +60 -0
  67. data/lib/active_record/locking/optimistic.rb +33 -26
  68. data/lib/active_record/locking/pessimistic.rb +23 -1
  69. data/lib/active_record/log_subscriber.rb +8 -4
  70. data/lib/active_record/migration/command_recorder.rb +8 -8
  71. data/lib/active_record/migration.rb +68 -35
  72. data/lib/active_record/model_schema.rb +368 -0
  73. data/lib/active_record/nested_attributes.rb +60 -24
  74. data/lib/active_record/persistence.rb +57 -11
  75. data/lib/active_record/query_cache.rb +6 -6
  76. data/lib/active_record/querying.rb +58 -0
  77. data/lib/active_record/railtie.rb +37 -29
  78. data/lib/active_record/railties/controller_runtime.rb +3 -1
  79. data/lib/active_record/railties/databases.rake +213 -117
  80. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  81. data/lib/active_record/readonly_attributes.rb +26 -0
  82. data/lib/active_record/reflection.rb +7 -15
  83. data/lib/active_record/relation/batches.rb +7 -4
  84. data/lib/active_record/relation/calculations.rb +55 -16
  85. data/lib/active_record/relation/delegation.rb +49 -0
  86. data/lib/active_record/relation/finder_methods.rb +16 -11
  87. data/lib/active_record/relation/predicate_builder.rb +8 -6
  88. data/lib/active_record/relation/query_methods.rb +75 -9
  89. data/lib/active_record/relation/spawn_methods.rb +48 -7
  90. data/lib/active_record/relation.rb +78 -32
  91. data/lib/active_record/result.rb +10 -4
  92. data/lib/active_record/sanitization.rb +194 -0
  93. data/lib/active_record/schema_dumper.rb +12 -5
  94. data/lib/active_record/scoping/default.rb +142 -0
  95. data/lib/active_record/scoping/named.rb +200 -0
  96. data/lib/active_record/scoping.rb +152 -0
  97. data/lib/active_record/serialization.rb +1 -43
  98. data/lib/active_record/serializers/xml_serializer.rb +4 -45
  99. data/lib/active_record/session_store.rb +18 -16
  100. data/lib/active_record/store.rb +52 -0
  101. data/lib/active_record/test_case.rb +11 -7
  102. data/lib/active_record/timestamp.rb +17 -3
  103. data/lib/active_record/transactions.rb +27 -6
  104. data/lib/active_record/translation.rb +22 -0
  105. data/lib/active_record/validations/associated.rb +5 -4
  106. data/lib/active_record/validations/uniqueness.rb +8 -8
  107. data/lib/active_record/validations.rb +1 -1
  108. data/lib/active_record/version.rb +3 -3
  109. data/lib/active_record.rb +38 -3
  110. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  111. data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
  112. data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
  113. data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
  114. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  115. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  116. metadata +49 -28
  117. 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/synchronization'
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
- # * +wait_timeout+: number of seconds to block and wait for a connection
58
- # before giving up and raising a timeout error (default 5 seconds).
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
- # The mutex used to synchronize pool access
78
- @connection_mutex = Monitor.new
79
- @queue = @connection_mutex.new_cond
80
- @timeout = spec.config[:wait_timeout] || 5
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
- @reserved_connections[current_connection_id] ||= checkout
101
+ synchronize do
102
+ @reserved_connections[current_connection_id] ||= checkout
103
+ end
163
104
  end
164
105
 
165
- # Check to see if there is an active connection in this connection
166
- # pool.
106
+ # Is there an open connection that is being used for the current thread?
167
107
  def active_connection?
168
- @reserved_connections.key? current_connection_id
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. If no connection
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 @reserved_connections[connection_id]
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
- !@connections.empty?
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
- @reserved_connections.each do |name,conn|
198
- checkin conn
199
- end
200
- @reserved_connections = {}
201
- @connections.each do |conn|
202
- conn.disconnect!
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
- @reserved_connections.each do |name, conn|
210
- checkin conn
211
- end
212
- @reserved_connections = {}
213
- @connections.each do |conn|
214
- conn.disconnect! if conn.requires_reloading?
215
- end
216
- @connections.delete_if do |conn|
217
- conn.requires_reloading?
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
- clear_stale_cached_connections!
225
- @connections.each do |connection|
226
- connection.verify!
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
- checkin @reserved_connections[key]
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
- # Checkout an available connection
260
- @connection_mutex.synchronize do
239
+ synchronize do
240
+ waited_time = 0
241
+
261
242
  loop do
262
- conn = if @checked_out.size < @connections.size
263
- checkout_existing_connection
264
- elsif @connections.size < @size
265
- checkout_new_connection
266
- end
267
- return conn if conn
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
- @connection_mutex.synchronize do
286
+ synchronize do
291
287
  conn.run_callbacks :checkin do
292
- @checked_out.delete conn
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 new_connection
304
- connection = ActiveRecord::Base.send(spec.adapter_method, spec.config)
298
+ def release(conn)
299
+ synchronize do
300
+ thread_id = nil
305
301
 
306
- # TODO: This is a bit icky, and in the long term we may want to change the method
307
- # signature for connections. Also, if we switch to have one visitor per
308
- # connection (and therefore per thread), we can get rid of the thread-local
309
- # variable in Arel::Visitors::ToSql.
310
- @visitor ||= connection.class.visitor_for(self)
311
- connection.visitor = @visitor
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
- connection
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
- checkout_and_verify(c)
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[name] = ConnectionAdapters::ConnectionPool.new(spec)
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 = @connection_pools.delete(klass.name)
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 = @connection_pools[klass.name]
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 = nil)
52
- case spec
53
- when nil
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
- begin
69
- require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
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. +name+ is logged along with
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. Implement for PostgreSQL, Oracle, ...
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 = Hash[columns(table_name).map { |c| [c.name, c] }]
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) #:nodoc
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 #:nodoc
366
+ def commit_transaction_records
394
367
  records = @_current_transaction_records.flatten
395
368
  @_current_transaction_records.clear
396
369
  unless records.blank?