activerecord 4.2.0 → 4.2.11
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 +5 -5
- data/CHANGELOG.md +657 -1
- data/lib/active_record.rb +3 -0
- data/lib/active_record/aggregations.rb +6 -3
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +5 -4
- data/lib/active_record/associations/association.rb +15 -3
- data/lib/active_record/associations/association_scope.rb +1 -0
- data/lib/active_record/associations/belongs_to_association.rb +13 -5
- data/lib/active_record/associations/builder/association.rb +1 -1
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -4
- data/lib/active_record/associations/collection_association.rb +35 -15
- data/lib/active_record/associations/collection_proxy.rb +15 -9
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +30 -15
- data/lib/active_record/associations/has_many_through_association.rb +11 -2
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency.rb +8 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
- data/lib/active_record/associations/preloader.rb +4 -4
- data/lib/active_record/associations/preloader/association.rb +5 -1
- data/lib/active_record/associations/singular_association.rb +2 -8
- data/lib/active_record/associations/through_association.rb +11 -6
- data/lib/active_record/attribute.rb +15 -1
- data/lib/active_record/attribute_assignment.rb +2 -2
- data/lib/active_record/attribute_methods.rb +4 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +5 -0
- data/lib/active_record/attribute_methods/dirty.rb +14 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +5 -1
- data/lib/active_record/attribute_methods/write.rb +1 -1
- data/lib/active_record/attribute_set.rb +4 -0
- data/lib/active_record/attribute_set/builder.rb +32 -12
- data/lib/active_record/attributes.rb +8 -0
- data/lib/active_record/autosave_association.rb +24 -9
- data/lib/active_record/base.rb +4 -5
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +12 -6
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -16
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +87 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +2 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +25 -7
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -10
- data/lib/active_record/connection_adapters/column.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -21
- data/lib/active_record/connection_adapters/mysql_adapter.rb +10 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +21 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +12 -28
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +28 -15
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/enum.rb +2 -3
- data/lib/active_record/errors.rb +6 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +9 -7
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +16 -14
- data/lib/active_record/migration.rb +38 -10
- data/lib/active_record/model_schema.rb +4 -2
- data/lib/active_record/nested_attributes.rb +13 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +7 -4
- data/lib/active_record/railtie.rb +5 -3
- data/lib/active_record/railties/databases.rake +17 -24
- data/lib/active_record/reflection.rb +40 -28
- data/lib/active_record/relation.rb +3 -2
- data/lib/active_record/relation/calculations.rb +10 -3
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +4 -16
- data/lib/active_record/relation/merger.rb +24 -1
- data/lib/active_record/relation/predicate_builder.rb +32 -3
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +29 -27
- data/lib/active_record/relation/spawn_methods.rb +7 -3
- data/lib/active_record/schema_dumper.rb +1 -1
- data/lib/active_record/schema_migration.rb +1 -4
- data/lib/active_record/scoping/default.rb +1 -0
- data/lib/active_record/tasks/database_tasks.rb +5 -2
- data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
- data/lib/active_record/transactions.rb +21 -11
- data/lib/active_record/type/boolean.rb +1 -0
- data/lib/active_record/type/date.rb +4 -0
- data/lib/active_record/type/date_time.rb +14 -3
- data/lib/active_record/type/decimal.rb +27 -3
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/integer.rb +9 -5
- data/lib/active_record/type/numeric.rb +1 -1
- data/lib/active_record/type/serialized.rb +7 -1
- data/lib/active_record/type/string.rb +4 -0
- data/lib/active_record/type/value.rb +9 -0
- data/lib/active_record/validations/uniqueness.rb +16 -6
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -6
- metadata +9 -7
@@ -9,6 +9,8 @@ module ActiveRecord
|
|
9
9
|
class_attribute :user_provided_defaults, instance_accessor: false # :internal:
|
10
10
|
self.user_provided_columns = {}
|
11
11
|
self.user_provided_defaults = {}
|
12
|
+
|
13
|
+
delegate :persistable_attribute_names, to: :class
|
12
14
|
end
|
13
15
|
|
14
16
|
module ClassMethods # :nodoc:
|
@@ -96,6 +98,10 @@ module ActiveRecord
|
|
96
98
|
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
97
99
|
end
|
98
100
|
|
101
|
+
def persistable_attribute_names # :nodoc:
|
102
|
+
@persistable_attribute_names ||= connection.schema_cache.columns_hash(table_name).keys
|
103
|
+
end
|
104
|
+
|
99
105
|
def reset_column_information # :nodoc:
|
100
106
|
super
|
101
107
|
clear_caches_calculated_from_columns
|
@@ -129,6 +135,8 @@ module ActiveRecord
|
|
129
135
|
@columns_hash = nil
|
130
136
|
@content_columns = nil
|
131
137
|
@default_attributes = nil
|
138
|
+
@persistable_attribute_names = nil
|
139
|
+
@attribute_names = nil
|
132
140
|
end
|
133
141
|
|
134
142
|
def raw_default_values
|
@@ -177,10 +177,8 @@ module ActiveRecord
|
|
177
177
|
# before actually defining them.
|
178
178
|
def add_autosave_association_callbacks(reflection)
|
179
179
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
180
|
-
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
181
|
-
collection = reflection.collection?
|
182
180
|
|
183
|
-
if collection
|
181
|
+
if reflection.collection?
|
184
182
|
before_save :before_save_collection_association
|
185
183
|
|
186
184
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
@@ -204,8 +202,18 @@ module ActiveRecord
|
|
204
202
|
before_save save_method
|
205
203
|
end
|
206
204
|
|
205
|
+
define_autosave_validation_callbacks(reflection)
|
206
|
+
end
|
207
|
+
|
208
|
+
def define_autosave_validation_callbacks(reflection)
|
209
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
207
210
|
if reflection.validate? && !method_defined?(validation_method)
|
208
|
-
|
211
|
+
if reflection.collection?
|
212
|
+
method = :validate_collection_association
|
213
|
+
else
|
214
|
+
method = :validate_single_association
|
215
|
+
end
|
216
|
+
|
209
217
|
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
210
218
|
validate validation_method
|
211
219
|
end
|
@@ -272,11 +280,18 @@ module ActiveRecord
|
|
272
280
|
# go through nested autosave associations that are loaded in memory (without loading
|
273
281
|
# any new ones), and return true if is changed for autosave
|
274
282
|
def nested_records_changed_for_autosave?
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
283
|
+
@_nested_records_changed_for_autosave_already_called ||= false
|
284
|
+
return false if @_nested_records_changed_for_autosave_already_called
|
285
|
+
begin
|
286
|
+
@_nested_records_changed_for_autosave_already_called = true
|
287
|
+
self.class._reflections.values.any? do |reflection|
|
288
|
+
if reflection.options[:autosave]
|
289
|
+
association = association_instance_get(reflection.name)
|
290
|
+
association && Array.wrap(association.target).any?(&:changed_for_autosave?)
|
291
|
+
end
|
279
292
|
end
|
293
|
+
ensure
|
294
|
+
@_nested_records_changed_for_autosave_already_called = false
|
280
295
|
end
|
281
296
|
end
|
282
297
|
|
@@ -303,7 +318,7 @@ module ActiveRecord
|
|
303
318
|
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
304
319
|
# enabled records if they're marked_for_destruction? or destroyed.
|
305
320
|
def association_valid?(reflection, record)
|
306
|
-
return true if record.destroyed? || record.marked_for_destruction?
|
321
|
+
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
307
322
|
|
308
323
|
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
|
309
324
|
unless valid = record.valid?(validation_context)
|
data/lib/active_record/base.rb
CHANGED
@@ -119,23 +119,22 @@ module ActiveRecord #:nodoc:
|
|
119
119
|
# All column values are automatically available through basic accessors on the Active Record
|
120
120
|
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
|
121
121
|
# the default accessors (using the same name as the attribute) and calling
|
122
|
-
#
|
123
|
-
# change things.
|
122
|
+
# +super+ to actually change things.
|
124
123
|
#
|
125
124
|
# class Song < ActiveRecord::Base
|
126
125
|
# # Uses an integer of seconds to hold the length of the song
|
127
126
|
#
|
128
127
|
# def length=(minutes)
|
129
|
-
#
|
128
|
+
# super(minutes.to_i * 60)
|
130
129
|
# end
|
131
130
|
#
|
132
131
|
# def length
|
133
|
-
#
|
132
|
+
# super / 60
|
134
133
|
# end
|
135
134
|
# end
|
136
135
|
#
|
137
136
|
# You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
|
138
|
-
#
|
137
|
+
# or <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
|
139
138
|
#
|
140
139
|
# == Attribute query methods
|
141
140
|
#
|
@@ -199,7 +199,7 @@ module ActiveRecord
|
|
199
199
|
# == Canceling callbacks
|
200
200
|
#
|
201
201
|
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
|
202
|
-
# cancelled.
|
202
|
+
# cancelled.
|
203
203
|
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
|
204
204
|
# methods on the model, which are called last.
|
205
205
|
#
|
@@ -236,7 +236,7 @@ module ActiveRecord
|
|
236
236
|
@spec = spec
|
237
237
|
|
238
238
|
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
239
|
-
@reaper
|
239
|
+
@reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
|
240
240
|
@reaper.run
|
241
241
|
|
242
242
|
# default max pool size to 5
|
@@ -365,7 +365,7 @@ module ActiveRecord
|
|
365
365
|
conn.expire
|
366
366
|
end
|
367
367
|
|
368
|
-
release owner
|
368
|
+
release conn, owner
|
369
369
|
|
370
370
|
@available.add conn
|
371
371
|
end
|
@@ -378,7 +378,7 @@ module ActiveRecord
|
|
378
378
|
@connections.delete conn
|
379
379
|
@available.delete conn
|
380
380
|
|
381
|
-
release conn.owner
|
381
|
+
release conn, conn.owner
|
382
382
|
|
383
383
|
@available.add checkout_new_connection if @available.any_waiting?
|
384
384
|
end
|
@@ -426,10 +426,12 @@ module ActiveRecord
|
|
426
426
|
end
|
427
427
|
end
|
428
428
|
|
429
|
-
def release(owner)
|
429
|
+
def release(conn, owner)
|
430
430
|
thread_id = owner.object_id
|
431
431
|
|
432
|
-
@reserved_connections
|
432
|
+
if @reserved_connections[thread_id] == conn
|
433
|
+
@reserved_connections.delete thread_id
|
434
|
+
end
|
433
435
|
end
|
434
436
|
|
435
437
|
def new_connection
|
@@ -454,6 +456,10 @@ module ActiveRecord
|
|
454
456
|
c.verify!
|
455
457
|
end
|
456
458
|
c
|
459
|
+
rescue
|
460
|
+
remove c
|
461
|
+
c.disconnect!
|
462
|
+
raise
|
457
463
|
end
|
458
464
|
end
|
459
465
|
|
@@ -631,7 +637,7 @@ module ActiveRecord
|
|
631
637
|
end
|
632
638
|
|
633
639
|
def pool_from_any_process_for(owner)
|
634
|
-
owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
|
640
|
+
owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[owner.name] }
|
635
641
|
owner_to_pool && owner_to_pool[owner.name]
|
636
642
|
end
|
637
643
|
end
|
@@ -234,6 +234,10 @@ module ActiveRecord
|
|
234
234
|
current_transaction.add_record(record)
|
235
235
|
end
|
236
236
|
|
237
|
+
def transaction_state
|
238
|
+
current_transaction.state
|
239
|
+
end
|
240
|
+
|
237
241
|
# Begins the transaction (and turns off auto-committing).
|
238
242
|
def begin_db_transaction() end
|
239
243
|
|
@@ -258,7 +262,18 @@ module ActiveRecord
|
|
258
262
|
|
259
263
|
# Rolls back the transaction (and turns on auto-committing). Must be
|
260
264
|
# done if the transaction block raises an exception or returns false.
|
261
|
-
def rollback_db_transaction
|
265
|
+
def rollback_db_transaction
|
266
|
+
exec_rollback_db_transaction
|
267
|
+
end
|
268
|
+
|
269
|
+
def exec_rollback_db_transaction() end #:nodoc:
|
270
|
+
|
271
|
+
def rollback_to_savepoint(name = nil)
|
272
|
+
exec_rollback_to_savepoint(name)
|
273
|
+
end
|
274
|
+
|
275
|
+
def exec_rollback_to_savepoint(name = nil) #:nodoc:
|
276
|
+
end
|
262
277
|
|
263
278
|
def default_sequence_name(table, column)
|
264
279
|
nil
|
@@ -272,12 +287,17 @@ module ActiveRecord
|
|
272
287
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
273
288
|
# something beyond a simple insert (eg. Oracle).
|
274
289
|
def insert_fixture(fixture, table_name)
|
290
|
+
fixture = fixture.stringify_keys
|
275
291
|
columns = schema_cache.columns_hash(table_name)
|
276
292
|
|
277
293
|
key_list = []
|
278
294
|
value_list = fixture.map do |name, value|
|
279
|
-
|
280
|
-
|
295
|
+
if column = columns[name]
|
296
|
+
key_list << quote_column_name(name)
|
297
|
+
quote(value, column)
|
298
|
+
else
|
299
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
300
|
+
end
|
281
301
|
end
|
282
302
|
|
283
303
|
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
|
@@ -3,7 +3,7 @@ module ActiveRecord
|
|
3
3
|
module QueryCache
|
4
4
|
class << self
|
5
5
|
def included(base) #:nodoc:
|
6
|
-
dirties_query_cache base, :insert, :update, :delete
|
6
|
+
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
|
7
7
|
end
|
8
8
|
|
9
9
|
def dirties_query_cache(base, *method_names)
|
@@ -57,11 +57,11 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
|
59
59
|
module TimestampDefaultDeprecation # :nodoc:
|
60
|
-
def emit_warning_if_null_unspecified(options)
|
60
|
+
def emit_warning_if_null_unspecified(sym, options)
|
61
61
|
return if options.key?(:null)
|
62
62
|
|
63
63
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
64
|
-
|
64
|
+
`##{sym}` was called without specifying an option for `null`. In Rails 5,
|
65
65
|
this behavior will change to `null: false`. You should manually specify
|
66
66
|
`null: true` to prevent the behavior of your existing migrations from changing.
|
67
67
|
MSG
|
@@ -94,11 +94,12 @@ module ActiveRecord
|
|
94
94
|
# An array of ColumnDefinition objects, representing the column changes
|
95
95
|
# that have been defined.
|
96
96
|
attr_accessor :indexes
|
97
|
-
attr_reader :name, :temporary, :options, :as
|
97
|
+
attr_reader :name, :temporary, :options, :as, :foreign_keys
|
98
98
|
|
99
99
|
def initialize(types, name, temporary, options, as = nil)
|
100
100
|
@columns_hash = {}
|
101
101
|
@indexes = {}
|
102
|
+
@foreign_keys = []
|
102
103
|
@native = types
|
103
104
|
@temporary = temporary
|
104
105
|
@options = options
|
@@ -255,6 +256,7 @@ module ActiveRecord
|
|
255
256
|
def column(name, type, options = {})
|
256
257
|
name = name.to_s
|
257
258
|
type = type.to_sym
|
259
|
+
options = options.dup
|
258
260
|
|
259
261
|
if @columns_hash[name] && @columns_hash[name].primary_key?
|
260
262
|
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
|
@@ -286,35 +288,46 @@ module ActiveRecord
|
|
286
288
|
indexes[column_name] = options
|
287
289
|
end
|
288
290
|
|
291
|
+
def foreign_key(table_name, options = {}) # :nodoc:
|
292
|
+
foreign_keys.push([table_name, options])
|
293
|
+
end
|
294
|
+
|
289
295
|
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
290
296
|
# <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
|
291
297
|
#
|
292
298
|
# t.timestamps null: false
|
293
299
|
def timestamps(*args)
|
294
300
|
options = args.extract_options!
|
295
|
-
emit_warning_if_null_unspecified(options)
|
301
|
+
emit_warning_if_null_unspecified(:timestamps, options)
|
296
302
|
column(:created_at, :datetime, options)
|
297
303
|
column(:updated_at, :datetime, options)
|
298
304
|
end
|
299
305
|
|
300
|
-
# Adds a reference.
|
301
|
-
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
|
302
|
-
# by default, the <tt>:type</tt> option can be used to specify a different type.
|
306
|
+
# Adds a reference.
|
303
307
|
#
|
304
308
|
# t.references(:user)
|
305
|
-
# t.
|
306
|
-
# t.belongs_to(:supplier, polymorphic: true)
|
309
|
+
# t.belongs_to(:supplier, foreign_key: true)
|
307
310
|
#
|
308
|
-
# See SchemaStatements#add_reference
|
311
|
+
# See SchemaStatements#add_reference for details of the options you can use.
|
309
312
|
def references(*args)
|
310
313
|
options = args.extract_options!
|
311
314
|
polymorphic = options.delete(:polymorphic)
|
312
315
|
index_options = options.delete(:index)
|
316
|
+
foreign_key_options = options.delete(:foreign_key)
|
313
317
|
type = options.delete(:type) || :integer
|
318
|
+
|
319
|
+
if polymorphic && foreign_key_options
|
320
|
+
raise ArgumentError, "Cannot add a foreign key on a polymorphic relation"
|
321
|
+
end
|
322
|
+
|
314
323
|
args.each do |col|
|
315
324
|
column("#{col}_id", type, options)
|
316
325
|
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
|
317
326
|
index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
327
|
+
if foreign_key_options
|
328
|
+
to_table = Base.pluralize_table_names ? col.to_s.pluralize : col.to_s
|
329
|
+
foreign_key(to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
|
330
|
+
end
|
318
331
|
end
|
319
332
|
end
|
320
333
|
alias :belongs_to :references
|
@@ -520,15 +533,12 @@ module ActiveRecord
|
|
520
533
|
@base.rename_column(name, column_name, new_column_name)
|
521
534
|
end
|
522
535
|
|
523
|
-
# Adds a reference.
|
524
|
-
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
|
525
|
-
# by default, the <tt>:type</tt> option can be used to specify a different type.
|
536
|
+
# Adds a reference.
|
526
537
|
#
|
527
538
|
# t.references(:user)
|
528
|
-
# t.
|
529
|
-
# t.belongs_to(:supplier, polymorphic: true)
|
539
|
+
# t.belongs_to(:supplier, foreign_key: true)
|
530
540
|
#
|
531
|
-
# See SchemaStatements#add_reference
|
541
|
+
# See SchemaStatements#add_reference for details of the options you can use.
|
532
542
|
def references(*args)
|
533
543
|
options = args.extract_options!
|
534
544
|
args.each do |ref_name|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'active_record/migration/join_table'
|
2
|
+
require 'active_support/core_ext/string/access'
|
3
|
+
require 'digest'
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters # :nodoc:
|
@@ -17,6 +19,20 @@ module ActiveRecord
|
|
17
19
|
table_name[0...table_alias_length].tr('.', '_')
|
18
20
|
end
|
19
21
|
|
22
|
+
# Returns the relation names useable to back Active Record models.
|
23
|
+
# For most adapters this means all tables and views.
|
24
|
+
def data_sources
|
25
|
+
tables
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks to see if the data source +name+ exists on the database.
|
29
|
+
#
|
30
|
+
# data_source_exists?(:ebooks)
|
31
|
+
#
|
32
|
+
def data_source_exists?(name)
|
33
|
+
data_sources.include?(name.to_s)
|
34
|
+
end
|
35
|
+
|
20
36
|
# Checks to see if the table +table_name+ exists on the database.
|
21
37
|
#
|
22
38
|
# table_exists?(:developers)
|
@@ -204,7 +220,17 @@ module ActiveRecord
|
|
204
220
|
end
|
205
221
|
|
206
222
|
result = execute schema_creation.accept td
|
207
|
-
|
223
|
+
|
224
|
+
unless supports_indexes_in_create?
|
225
|
+
td.indexes.each_pair do |column_name, index_options|
|
226
|
+
add_index(table_name, column_name, index_options)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
td.foreign_keys.each do |other_table_name, foreign_key_options|
|
231
|
+
add_foreign_key(table_name, other_table_name, foreign_key_options)
|
232
|
+
end
|
233
|
+
|
208
234
|
result
|
209
235
|
end
|
210
236
|
|
@@ -518,6 +544,8 @@ module ActiveRecord
|
|
518
544
|
#
|
519
545
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
520
546
|
#
|
547
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
|
548
|
+
#
|
521
549
|
# ====== Creating an index with a specific method
|
522
550
|
#
|
523
551
|
# add_index(:developers, :name, using: 'btree')
|
@@ -576,9 +604,8 @@ module ActiveRecord
|
|
576
604
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
577
605
|
#
|
578
606
|
def rename_index(table_name, old_name, new_name)
|
579
|
-
|
580
|
-
|
581
|
-
end
|
607
|
+
validate_index_length!(table_name, new_name)
|
608
|
+
|
582
609
|
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
583
610
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
584
611
|
return unless old_index_def
|
@@ -610,11 +637,21 @@ module ActiveRecord
|
|
610
637
|
indexes(table_name).detect { |i| i.name == index_name }
|
611
638
|
end
|
612
639
|
|
613
|
-
# Adds a reference.
|
614
|
-
#
|
615
|
-
# a
|
640
|
+
# Adds a reference. The reference column is an integer by default,
|
641
|
+
# the <tt>:type</tt> option can be used to specify a different type.
|
642
|
+
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
616
643
|
# <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
|
617
644
|
#
|
645
|
+
# The +options+ hash can include the following keys:
|
646
|
+
# [<tt>:type</tt>]
|
647
|
+
# The reference column type. Defaults to +:integer+.
|
648
|
+
# [<tt>:index</tt>]
|
649
|
+
# Add an appropriate index. Defaults to false.
|
650
|
+
# [<tt>:foreign_key</tt>]
|
651
|
+
# Add an appropriate foreign key. Defaults to false.
|
652
|
+
# [<tt>:polymorphic</tt>]
|
653
|
+
# Wether an additional +_type+ column should be added. Defaults to false.
|
654
|
+
#
|
618
655
|
# ====== Create a user_id integer column
|
619
656
|
#
|
620
657
|
# add_reference(:products, :user)
|
@@ -623,11 +660,7 @@ module ActiveRecord
|
|
623
660
|
#
|
624
661
|
# add_reference(:products, :user, type: :string)
|
625
662
|
#
|
626
|
-
# ====== Create
|
627
|
-
#
|
628
|
-
# add_belongs_to(:products, :supplier, polymorphic: true)
|
629
|
-
#
|
630
|
-
# ====== Create a supplier_id, supplier_type columns and appropriate index
|
663
|
+
# ====== Create supplier_id, supplier_type columns and appropriate index
|
631
664
|
#
|
632
665
|
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
633
666
|
#
|
@@ -635,9 +668,19 @@ module ActiveRecord
|
|
635
668
|
polymorphic = options.delete(:polymorphic)
|
636
669
|
index_options = options.delete(:index)
|
637
670
|
type = options.delete(:type) || :integer
|
671
|
+
foreign_key_options = options.delete(:foreign_key)
|
672
|
+
|
673
|
+
if polymorphic && foreign_key_options
|
674
|
+
raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
|
675
|
+
end
|
676
|
+
|
638
677
|
add_column(table_name, "#{ref_name}_id", type, options)
|
639
678
|
add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
|
640
679
|
add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
680
|
+
if foreign_key_options
|
681
|
+
to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
682
|
+
add_foreign_key(table_name, to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
|
683
|
+
end
|
641
684
|
end
|
642
685
|
alias :add_belongs_to :add_reference
|
643
686
|
|
@@ -652,7 +695,16 @@ module ActiveRecord
|
|
652
695
|
#
|
653
696
|
# remove_reference(:products, :supplier, polymorphic: true)
|
654
697
|
#
|
698
|
+
# ====== Remove the reference with a foreign key
|
699
|
+
#
|
700
|
+
# remove_reference(:products, :user, index: true, foreign_key: true)
|
701
|
+
#
|
655
702
|
def remove_reference(table_name, ref_name, options = {})
|
703
|
+
if options[:foreign_key]
|
704
|
+
to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
705
|
+
remove_foreign_key(table_name, to_table)
|
706
|
+
end
|
707
|
+
|
656
708
|
remove_column(table_name, "#{ref_name}_id")
|
657
709
|
remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
|
658
710
|
end
|
@@ -668,8 +720,8 @@ module ActiveRecord
|
|
668
720
|
# +to_table+ contains the referenced primary key.
|
669
721
|
#
|
670
722
|
# The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
|
671
|
-
# +identifier+ is a 10 character long
|
672
|
-
# the <tt>:name</tt> option.
|
723
|
+
# +identifier+ is a 10 character long string which is deterministically generated from the
|
724
|
+
# +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
|
673
725
|
#
|
674
726
|
# ====== Creating a simple foreign key
|
675
727
|
#
|
@@ -681,7 +733,7 @@ module ActiveRecord
|
|
681
733
|
#
|
682
734
|
# ====== Creating a foreign key on a specific column
|
683
735
|
#
|
684
|
-
# add_foreign_key :articles, :users, column: :author_id, primary_key:
|
736
|
+
# add_foreign_key :articles, :users, column: :author_id, primary_key: :lng_id
|
685
737
|
#
|
686
738
|
# generates:
|
687
739
|
#
|
@@ -764,7 +816,10 @@ module ActiveRecord
|
|
764
816
|
end
|
765
817
|
|
766
818
|
def foreign_key_column_for(table_name) # :nodoc:
|
767
|
-
|
819
|
+
prefix = Base.table_name_prefix
|
820
|
+
suffix = Base.table_name_suffix
|
821
|
+
name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
822
|
+
"#{name.singularize}_id"
|
768
823
|
end
|
769
824
|
|
770
825
|
def dump_schema_information #:nodoc:
|
@@ -786,10 +841,9 @@ module ActiveRecord
|
|
786
841
|
version = version.to_i
|
787
842
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
788
843
|
|
789
|
-
migrated = select_values("SELECT version FROM #{sm_table}").map
|
790
|
-
|
791
|
-
|
792
|
-
filename.split('/').last.split('_').first.to_i
|
844
|
+
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
845
|
+
versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
|
846
|
+
ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
|
793
847
|
end
|
794
848
|
|
795
849
|
unless migrated.include?(version)
|
@@ -835,11 +889,12 @@ module ActiveRecord
|
|
835
889
|
end
|
836
890
|
|
837
891
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
838
|
-
#
|
892
|
+
# PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
|
839
893
|
# require the order columns appear in the SELECT.
|
840
894
|
#
|
841
895
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
842
|
-
|
896
|
+
#
|
897
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
843
898
|
columns
|
844
899
|
end
|
845
900
|
|
@@ -850,7 +905,7 @@ module ActiveRecord
|
|
850
905
|
# add_timestamps(:suppliers, null: false)
|
851
906
|
#
|
852
907
|
def add_timestamps(table_name, options = {})
|
853
|
-
emit_warning_if_null_unspecified(options)
|
908
|
+
emit_warning_if_null_unspecified(:add_timestamps, options)
|
854
909
|
add_column table_name, :created_at, :datetime, options
|
855
910
|
add_column table_name, :updated_at, :datetime, options
|
856
911
|
end
|
@@ -982,8 +1037,16 @@ module ActiveRecord
|
|
982
1037
|
end
|
983
1038
|
|
984
1039
|
def foreign_key_name(table_name, options) # :nodoc:
|
1040
|
+
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1041
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
985
1042
|
options.fetch(:name) do
|
986
|
-
"fk_rails_#{
|
1043
|
+
"fk_rails_#{hashed_identifier}"
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def validate_index_length!(table_name, new_name)
|
1048
|
+
if new_name.length > allowed_index_name_length
|
1049
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
987
1050
|
end
|
988
1051
|
end
|
989
1052
|
end
|