activerecord 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +218 -2
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +4 -2
  5. data/lib/active_record/association_relation.rb +4 -1
  6. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +4 -2
  7. data/lib/active_record/associations/builder/singular_association.rb +10 -1
  8. data/lib/active_record/associations/collection_association.rb +22 -17
  9. data/lib/active_record/associations/collection_proxy.rb +20 -7
  10. data/lib/active_record/associations/has_many_through_association.rb +4 -0
  11. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  12. data/lib/active_record/associations/join_dependency.rb +10 -4
  13. data/lib/active_record/associations/preloader/association.rb +18 -4
  14. data/lib/active_record/associations/preloader/collection_association.rb +0 -1
  15. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  16. data/lib/active_record/associations/singular_association.rb +8 -2
  17. data/lib/active_record/associations.rb +5 -0
  18. data/lib/active_record/attribute.rb +3 -3
  19. data/lib/active_record/attribute_methods/primary_key.rb +14 -1
  20. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  21. data/lib/active_record/attribute_methods.rb +3 -7
  22. data/lib/active_record/attribute_set/builder.rb +29 -7
  23. data/lib/active_record/attribute_set.rb +2 -0
  24. data/lib/active_record/attributes.rb +3 -3
  25. data/lib/active_record/autosave_association.rb +15 -11
  26. data/lib/active_record/base.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +40 -32
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +4 -4
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -7
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +38 -33
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -5
  34. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -45
  35. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  36. data/lib/active_record/connection_adapters/mysql/database_statements.rb +6 -23
  37. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  38. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -5
  39. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
  40. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -1
  41. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  42. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  43. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +7 -1
  44. data/lib/active_record/connection_adapters/postgresql_adapter.rb +6 -2
  45. data/lib/active_record/core.rb +3 -1
  46. data/lib/active_record/enum.rb +6 -5
  47. data/lib/active_record/gem_version.rb +1 -1
  48. data/lib/active_record/integration.rb +13 -10
  49. data/lib/active_record/migration.rb +6 -5
  50. data/lib/active_record/model_schema.rb +134 -47
  51. data/lib/active_record/no_touching.rb +4 -0
  52. data/lib/active_record/persistence.rb +10 -4
  53. data/lib/active_record/query_cache.rb +13 -15
  54. data/lib/active_record/querying.rb +3 -3
  55. data/lib/active_record/railties/controller_runtime.rb +1 -1
  56. data/lib/active_record/reflection.rb +12 -0
  57. data/lib/active_record/relation/calculations.rb +11 -11
  58. data/lib/active_record/relation/delegation.rb +1 -1
  59. data/lib/active_record/relation/finder_methods.rb +11 -9
  60. data/lib/active_record/relation/query_methods.rb +3 -3
  61. data/lib/active_record/relation.rb +7 -4
  62. data/lib/active_record/result.rb +7 -1
  63. data/lib/active_record/sanitization.rb +11 -1
  64. data/lib/active_record/schema_dumper.rb +10 -17
  65. data/lib/active_record/scoping/named.rb +1 -1
  66. data/lib/active_record/statement_cache.rb +2 -2
  67. data/lib/active_record/table_metadata.rb +4 -3
  68. data/lib/active_record/touch_later.rb +6 -1
  69. data/lib/active_record/type/internal/abstract_json.rb +5 -1
  70. data/lib/active_record/validations/uniqueness.rb +3 -4
  71. data/lib/active_record.rb +1 -1
  72. metadata +8 -7
@@ -95,7 +95,8 @@ module ActiveRecord
95
95
  base_name.foreign_key
96
96
  else
97
97
  if ActiveRecord::Base != self && table_exists?
98
- connection.schema_cache.primary_keys(table_name)
98
+ pk = connection.schema_cache.primary_keys(table_name)
99
+ suppress_composite_primary_key(pk)
99
100
  else
100
101
  'id'
101
102
  end
@@ -122,6 +123,18 @@ module ActiveRecord
122
123
  @quoted_primary_key = nil
123
124
  @attributes_builder = nil
124
125
  end
126
+
127
+ private
128
+
129
+ def suppress_composite_primary_key(pk)
130
+ return pk unless pk.is_a?(Array)
131
+
132
+ warn <<-WARNING.strip_heredoc
133
+ WARNING: Active Record does not support composite primary key.
134
+
135
+ #{table_name} has composite primary key. Composite primary key is ignored.
136
+ WARNING
137
+ end
125
138
  end
126
139
  end
127
140
  end
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def set_time_zone_without_conversion(value)
42
- ::Time.zone.local_to_utc(value).in_time_zone
42
+ ::Time.zone.local_to_utc(value).in_time_zone if value
43
43
  end
44
44
 
45
45
  def map_avoiding_infinite_recursion(value)
@@ -279,9 +279,8 @@ module ActiveRecord
279
279
  # Returns an <tt>#inspect</tt>-like string for the value of the
280
280
  # attribute +attr_name+. String attributes are truncated up to 50
281
281
  # characters, Date and Time attributes are returned in the
282
- # <tt>:db</tt> format, Array attributes are truncated up to 10 values.
283
- # Other attributes return the value of <tt>#inspect</tt> without
284
- # modification.
282
+ # <tt>:db</tt> format. Other attributes return the value of
283
+ # <tt>#inspect</tt> without modification.
285
284
  #
286
285
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
287
286
  #
@@ -292,7 +291,7 @@ module ActiveRecord
292
291
  # # => "\"2012-10-22 00:15:07\""
293
292
  #
294
293
  # person.attribute_for_inspect(:tag_ids)
295
- # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
294
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
296
295
  def attribute_for_inspect(attr_name)
297
296
  value = read_attribute(attr_name)
298
297
 
@@ -300,9 +299,6 @@ module ActiveRecord
300
299
  "#{value[0, 50]}...".inspect
301
300
  elsif value.is_a?(Date) || value.is_a?(Time)
302
301
  %("#{value.to_s(:db)}")
303
- elsif value.is_a?(Array) && value.size > 10
304
- inspected = value.first(10).inspect
305
- %(#{inspected[0...-1]}, ...])
306
302
  else
307
303
  value.inspect
308
304
  end
@@ -3,11 +3,12 @@ require 'active_record/attribute'
3
3
  module ActiveRecord
4
4
  class AttributeSet # :nodoc:
5
5
  class Builder # :nodoc:
6
- attr_reader :types, :always_initialized
6
+ attr_reader :types, :always_initialized, :default
7
7
 
8
- def initialize(types, always_initialized = nil)
8
+ def initialize(types, always_initialized = nil, &default)
9
9
  @types = types
10
10
  @always_initialized = always_initialized
11
+ @default = default
11
12
  end
12
13
 
13
14
  def build_from_database(values = {}, additional_types = {})
@@ -15,21 +16,22 @@ module ActiveRecord
15
16
  values[always_initialized] = nil
16
17
  end
17
18
 
18
- attributes = LazyAttributeHash.new(types, values, additional_types)
19
+ attributes = LazyAttributeHash.new(types, values, additional_types, &default)
19
20
  AttributeSet.new(attributes)
20
21
  end
21
22
  end
22
23
  end
23
24
 
24
25
  class LazyAttributeHash # :nodoc:
25
- delegate :transform_values, :each_key, to: :materialize
26
+ delegate :transform_values, :each_key, :fetch, to: :materialize
26
27
 
27
- def initialize(types, values, additional_types)
28
+ def initialize(types, values, additional_types, &default)
28
29
  @types = types
29
30
  @values = values
30
31
  @additional_types = additional_types
31
32
  @materialized = false
32
33
  @delegate_hash = {}
34
+ @default = default || proc {}
33
35
  end
34
36
 
35
37
  def key?(key)
@@ -76,9 +78,29 @@ module ActiveRecord
76
78
  end
77
79
  end
78
80
 
81
+ def marshal_dump
82
+ materialize
83
+ end
84
+
85
+ def marshal_load(delegate_hash)
86
+ @delegate_hash = delegate_hash
87
+ @types = {}
88
+ @values = {}
89
+ @additional_types = {}
90
+ @materialized = true
91
+ end
92
+
93
+ def encode_with(coder)
94
+ coder["delegate_hash"] = materialize
95
+ end
96
+
97
+ def init_with(coder)
98
+ marshal_load(coder["delegate_hash"])
99
+ end
100
+
79
101
  protected
80
102
 
81
- attr_reader :types, :values, :additional_types, :delegate_hash
103
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default
82
104
 
83
105
  def materialize
84
106
  unless @materialized
@@ -101,7 +123,7 @@ module ActiveRecord
101
123
  if value_present
102
124
  delegate_hash[name] = Attribute.from_database(name, value, type)
103
125
  elsif types.key?(name)
104
- delegate_hash[name] = Attribute.uninitialized(name, type)
126
+ delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
105
127
  end
106
128
  end
107
129
  end
@@ -2,6 +2,8 @@ require 'active_record/attribute_set/builder'
2
2
 
3
3
  module ActiveRecord
4
4
  class AttributeSet # :nodoc:
5
+ delegate :fetch, to: :attributes
6
+
5
7
  def initialize(attributes)
6
8
  @attributes = attributes
7
9
  end
@@ -34,10 +34,10 @@ module ActiveRecord
34
34
  # is not passed, the previous default value (if any) will be used.
35
35
  # Otherwise, the default will be +nil+.
36
36
  #
37
- # +array+ (PG only) specifies that the type should be an array (see the
37
+ # +array+ (PostgreSQL only) specifies that the type should be an array (see the
38
38
  # examples below).
39
39
  #
40
- # +range+ (PG only) specifies that the type should be a range (see the
40
+ # +range+ (PostgreSQL only) specifies that the type should be a range (see the
41
41
  # examples below).
42
42
  #
43
43
  # ==== Examples
@@ -253,7 +253,7 @@ module ActiveRecord
253
253
  name,
254
254
  value,
255
255
  type,
256
- _default_attributes[name],
256
+ _default_attributes.fetch(name.to_s) { nil },
257
257
  )
258
258
  else
259
259
  default_attribute = Attribute.from_database(name, value, type)
@@ -329,26 +329,20 @@ module ActiveRecord
329
329
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
330
330
 
331
331
  validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
332
+
332
333
  unless valid = record.valid?(validation_context)
333
334
  if reflection.options[:autosave]
334
335
  indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
335
336
 
336
337
  record.errors.each do |attribute, message|
337
- if indexed_attribute
338
- attribute = "#{reflection.name}[#{index}].#{attribute}"
339
- else
340
- attribute = "#{reflection.name}.#{attribute}"
341
- end
338
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
342
339
  errors[attribute] << message
343
340
  errors[attribute].uniq!
344
341
  end
345
342
 
346
343
  record.errors.details.each_key do |attribute|
347
- if indexed_attribute
348
- reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
349
- else
350
- reflection_attribute = "#{reflection.name}.#{attribute}"
351
- end
344
+ reflection_attribute =
345
+ normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
352
346
 
353
347
  record.errors.details[attribute].each do |error|
354
348
  errors.details[reflection_attribute] << error
@@ -362,6 +356,14 @@ module ActiveRecord
362
356
  valid
363
357
  end
364
358
 
359
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
360
+ if indexed_attribute
361
+ "#{reflection.name}[#{index}].#{attribute}"
362
+ else
363
+ "#{reflection.name}.#{attribute}"
364
+ end
365
+ end
366
+
365
367
  # Is used as a before_save callback to check while saving a collection
366
368
  # association whether or not the parent was a new record before saving.
367
369
  def before_save_collection_association
@@ -457,7 +459,9 @@ module ActiveRecord
457
459
  # In addition, it will destroy the association if it was marked for destruction.
458
460
  def save_belongs_to_association(reflection)
459
461
  association = association_instance_get(reflection.name)
460
- record = association && association.load_target
462
+ return unless association && association.loaded? && !association.stale_target?
463
+
464
+ record = association.load_target
461
465
  if record && !record.destroyed?
462
466
  autosave = reflection.options[:autosave]
463
467
 
@@ -312,8 +312,8 @@ module ActiveRecord #:nodoc:
312
312
  include NestedAttributes
313
313
  include Aggregations
314
314
  include Transactions
315
- include NoTouching
316
315
  include TouchLater
316
+ include NoTouching
317
317
  include Reflection
318
318
  include Serialization
319
319
  include Store
@@ -307,6 +307,7 @@ module ActiveRecord
307
307
  end
308
308
 
309
309
  include MonitorMixin
310
+ include QueryCache::ConnectionPoolConfiguration
310
311
 
311
312
  attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
312
313
  attr_reader :spec, :connections, :size, :reaper
@@ -349,8 +350,7 @@ module ActiveRecord
349
350
  # currently in the process of independently establishing connections to the DB.
350
351
  @now_connecting = 0
351
352
 
352
- # A boolean toggle that allows/disallows new connections.
353
- @new_cons_enabled = true
353
+ @threads_blocking_new_connections = 0
354
354
 
355
355
  @available = ConnectionLeasingQueue.new self
356
356
  end
@@ -415,7 +415,10 @@ module ActiveRecord
415
415
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
416
416
  synchronize do
417
417
  @connections.each do |conn|
418
- checkin conn
418
+ if conn.in_use?
419
+ conn.steal!
420
+ checkin conn
421
+ end
419
422
  conn.disconnect!
420
423
  end
421
424
  @connections = []
@@ -442,33 +445,19 @@ module ActiveRecord
442
445
  # connections in the pool within a timeout interval (default duration is
443
446
  # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
444
447
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
445
- num_new_conns_required = 0
446
-
447
448
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
448
449
  synchronize do
449
450
  @connections.each do |conn|
450
- checkin conn
451
+ if conn.in_use?
452
+ conn.steal!
453
+ checkin conn
454
+ end
451
455
  conn.disconnect! if conn.requires_reloading?
452
456
  end
453
457
  @connections.delete_if(&:requires_reloading?)
454
-
455
458
  @available.clear
456
-
457
- if @connections.size < @size
458
- # because of the pruning done by this method, we might be running
459
- # low on connections, while threads stuck in queue are helpless
460
- # (not being able to establish new connections for themselves),
461
- # see also more detailed explanation in +remove+
462
- num_new_conns_required = num_waiting_in_queue - @connections.size
463
- end
464
-
465
- @connections.each do |conn|
466
- @available.add conn
467
- end
468
459
  end
469
460
  end
470
-
471
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
472
461
  end
473
462
 
474
463
  # Clears the cache which maps classes and re-connects connections that
@@ -556,17 +545,17 @@ module ActiveRecord
556
545
  stale_connections = synchronize do
557
546
  @connections.select do |conn|
558
547
  conn.in_use? && !conn.owner.alive?
548
+ end.each do |conn|
549
+ conn.steal!
559
550
  end
560
551
  end
561
552
 
562
553
  stale_connections.each do |conn|
563
- synchronize do
564
- if conn.active?
565
- conn.reset!
566
- checkin conn
567
- else
568
- remove conn
569
- end
554
+ if conn.active?
555
+ conn.reset!
556
+ checkin conn
557
+ else
558
+ remove conn
570
559
  end
571
560
  end
572
561
  end
@@ -675,13 +664,32 @@ module ActiveRecord
675
664
  end
676
665
 
677
666
  def with_new_connections_blocked
678
- previous_value = nil
679
667
  synchronize do
680
- previous_value, @new_cons_enabled = @new_cons_enabled, false
668
+ @threads_blocking_new_connections += 1
681
669
  end
670
+
682
671
  yield
683
672
  ensure
684
- synchronize { @new_cons_enabled = previous_value }
673
+ num_new_conns_required = 0
674
+
675
+ synchronize do
676
+ @threads_blocking_new_connections -= 1
677
+
678
+ if @threads_blocking_new_connections.zero?
679
+ @available.clear
680
+
681
+ num_new_conns_required = num_waiting_in_queue
682
+
683
+ @connections.each do |conn|
684
+ next if conn.in_use?
685
+
686
+ @available.add conn
687
+ num_new_conns_required -= 1
688
+ end
689
+ end
690
+ end
691
+
692
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
685
693
  end
686
694
 
687
695
  # Acquire a connection by one of 1) immediately removing one
@@ -733,7 +741,7 @@ module ActiveRecord
733
741
  # and increment @now_connecting, to prevent overstepping this pool's @size
734
742
  # constraint
735
743
  do_checkout = synchronize do
736
- if @new_cons_enabled && (@connections.size + @now_connecting) < @size
744
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
737
745
  @now_connecting += 1
738
746
  end
739
747
  end
@@ -244,7 +244,7 @@ module ActiveRecord
244
244
  end
245
245
 
246
246
  def reset_transaction #:nodoc:
247
- @transaction_manager = TransactionManager.new(self)
247
+ @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
248
248
  end
249
249
 
250
250
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  class << self
5
5
  def included(base) #:nodoc:
6
6
  dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
7
+
8
+ base.set_callback :checkout, :after, :configure_query_cache!
9
+ base.set_callback :checkin, :after, :disable_query_cache!
7
10
  end
8
11
 
9
12
  def dirties_query_cache(base, *method_names)
@@ -18,6 +21,27 @@ module ActiveRecord
18
21
  end
19
22
  end
20
23
 
24
+ module ConnectionPoolConfiguration
25
+ def initialize(*)
26
+ super
27
+ @query_cache_enabled = Concurrent::Map.new { false }
28
+ end
29
+
30
+ def enable_query_cache!
31
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
32
+ connection.enable_query_cache! if active_connection?
33
+ end
34
+
35
+ def disable_query_cache!
36
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
37
+ connection.disable_query_cache! if active_connection?
38
+ end
39
+
40
+ def query_cache_enabled
41
+ @query_cache_enabled[connection_cache_key(Thread.current)]
42
+ end
43
+ end
44
+
21
45
  attr_reader :query_cache, :query_cache_enabled
22
46
 
23
47
  def initialize(*)
@@ -41,6 +65,7 @@ module ActiveRecord
41
65
 
42
66
  def disable_query_cache!
43
67
  @query_cache_enabled = false
68
+ clear_query_cache
44
69
  end
45
70
 
46
71
  # Disable the query cache within the block.
@@ -90,6 +115,10 @@ module ActiveRecord
90
115
  def locked?(arel)
91
116
  arel.respond_to?(:locked) && arel.locked
92
117
  end
118
+
119
+ def configure_query_cache!
120
+ enable_query_cache! if pool.query_cache_enabled
121
+ end
93
122
  end
94
123
  end
95
124
  end
@@ -112,19 +112,19 @@ module ActiveRecord
112
112
  end
113
113
 
114
114
  def quoted_true
115
- "'t'"
115
+ "'t'".freeze
116
116
  end
117
117
 
118
118
  def unquoted_true
119
- 't'
119
+ 't'.freeze
120
120
  end
121
121
 
122
122
  def quoted_false
123
- "'f'"
123
+ "'f'".freeze
124
124
  end
125
125
 
126
126
  def unquoted_false
127
- 'f'
127
+ 'f'.freeze
128
128
  end
129
129
 
130
130
  # Quote date/time values for use in SQL input. Includes microseconds
@@ -51,11 +51,12 @@ module ActiveRecord
51
51
  options[:primary_key] != default_primary_key
52
52
  end
53
53
 
54
- def defined_for?(options_or_to_table = {})
55
- if options_or_to_table.is_a?(Hash)
56
- options_or_to_table.all? {|key, value| options[key].to_s == value.to_s }
54
+ def defined_for?(to_table_ord = nil, to_table: nil, **options)
55
+ if to_table_ord
56
+ self.to_table == to_table_ord.to_s
57
57
  else
58
- to_table == options_or_to_table.to_s
58
+ (to_table.nil? || to_table.to_s == self.to_table) &&
59
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
59
60
  end
60
61
  end
61
62
 
@@ -211,7 +212,7 @@ module ActiveRecord
211
212
 
212
213
  def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
213
214
  @columns_hash = {}
214
- @indexes = {}
215
+ @indexes = []
215
216
  @foreign_keys = []
216
217
  @primary_keys = nil
217
218
  @temporary = temporary
@@ -303,7 +304,7 @@ module ActiveRecord
303
304
  # end
304
305
  def column(name, type, options = {})
305
306
  name = name.to_s
306
- type = type.to_sym
307
+ type = type.to_sym if type
307
308
  options = options.dup
308
309
 
309
310
  if @columns_hash[name] && @columns_hash[name].primary_key?
@@ -327,7 +328,7 @@ module ActiveRecord
327
328
  #
328
329
  # index(:account_id, name: 'index_projects_on_account_id')
329
330
  def index(column_name, options = {})
330
- indexes[column_name] = options
331
+ indexes << [column_name, options]
331
332
  end
332
333
 
333
334
  def foreign_key(table_name, options = {}) # :nodoc:
@@ -129,14 +129,9 @@ module ActiveRecord
129
129
 
130
130
  # Returns just a table's primary key
131
131
  def primary_key(table_name)
132
- pks = primary_keys(table_name)
133
- warn <<-WARNING.strip_heredoc if pks.count > 1
134
- WARNING: Rails does not support composite primary key.
135
-
136
- #{table_name} has composite primary key. Composite primary key is ignored.
137
- WARNING
138
-
139
- pks.first if pks.one?
132
+ pk = primary_keys(table_name)
133
+ pk = pk.first unless pk.size > 1
134
+ pk
140
135
  end
141
136
 
142
137
  # Creates a new table with the name +table_name+. +table_name+ may either
@@ -283,16 +278,16 @@ module ActiveRecord
283
278
  result = execute schema_creation.accept td
284
279
 
285
280
  unless supports_indexes_in_create?
286
- td.indexes.each_pair do |column_name, index_options|
281
+ td.indexes.each do |column_name, index_options|
287
282
  add_index(table_name, column_name, index_options)
288
283
  end
289
284
  end
290
285
 
291
286
  if supports_comments? && !supports_comments_in_create?
292
- change_table_comment(table_name, comment) if comment
287
+ change_table_comment(table_name, comment) if comment.present?
293
288
 
294
289
  td.columns.each do |column|
295
- change_column_comment(table_name, column.name, column.comment) if column.comment
290
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
296
291
  end
297
292
  end
298
293
 
@@ -790,7 +785,7 @@ module ActiveRecord
790
785
  # [<tt>:type</tt>]
791
786
  # The reference column type. Defaults to +:integer+.
792
787
  # [<tt>:index</tt>]
793
- # Add an appropriate index. Defaults to false.
788
+ # Add an appropriate index. Defaults to true.
794
789
  # See #add_index for usage of this option.
795
790
  # [<tt>:foreign_key</tt>]
796
791
  # Add an appropriate foreign key constraint. Defaults to false.
@@ -847,14 +842,19 @@ module ActiveRecord
847
842
  #
848
843
  # remove_reference(:products, :user, index: true, foreign_key: true)
849
844
  #
850
- def remove_reference(table_name, ref_name, options = {})
851
- if options[:foreign_key]
845
+ def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
846
+ if foreign_key
852
847
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
853
- remove_foreign_key(table_name, reference_name)
848
+ if foreign_key.is_a?(Hash)
849
+ foreign_key_options = foreign_key
850
+ else
851
+ foreign_key_options = { to_table: reference_name }
852
+ end
853
+ remove_foreign_key(table_name, **foreign_key_options)
854
854
  end
855
855
 
856
856
  remove_column(table_name, "#{ref_name}_id")
857
- remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
857
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
858
858
  end
859
859
  alias :remove_belongs_to :remove_reference
860
860
 
@@ -993,8 +993,8 @@ module ActiveRecord
993
993
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
994
994
 
995
995
  if supports_multi_insert?
996
- sql = "INSERT INTO #{sm_table} (version) VALUES "
997
- sql << versions.map {|v| "('#{v}')" }.join(', ')
996
+ sql = "INSERT INTO #{sm_table} (version) VALUES\n"
997
+ sql << versions.map {|v| "('#{v}')" }.join(",\n")
998
998
  sql << ";\n\n"
999
999
  sql
1000
1000
  else
@@ -1043,7 +1043,8 @@ module ActiveRecord
1043
1043
  end
1044
1044
 
1045
1045
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
1046
- if native = native_database_types[type.to_sym]
1046
+ type = type.to_sym if type
1047
+ if native = native_database_types[type]
1047
1048
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
1048
1049
 
1049
1050
  if type == :decimal # ignore limit, use precision and scale
@@ -1163,31 +1164,35 @@ module ActiveRecord
1163
1164
  end
1164
1165
 
1165
1166
  protected
1166
- def add_index_sort_order(option_strings, column_names, options = {})
1167
- if options.is_a?(Hash) && order = options[:order]
1167
+
1168
+ def add_index_sort_order(quoted_columns, **options)
1169
+ if order = options[:order]
1168
1170
  case order
1169
1171
  when Hash
1170
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
1172
+ order = order.symbolize_keys
1173
+ quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
1171
1174
  when String
1172
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
1175
+ quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
1173
1176
  end
1174
1177
  end
1175
1178
 
1176
- return option_strings
1179
+ quoted_columns
1177
1180
  end
1178
1181
 
1179
1182
  # Overridden by the MySQL adapter for supporting index lengths
1180
- def quoted_columns_for_index(column_names, options = {})
1181
- return [column_names] if column_names.is_a?(String)
1182
-
1183
- option_strings = Hash[column_names.map {|name| [name, '']}]
1184
-
1185
- # add index sort order if supported
1183
+ def add_options_for_index_columns(quoted_columns, **options)
1186
1184
  if supports_index_sort_order?
1187
- option_strings = add_index_sort_order(option_strings, column_names, options)
1185
+ quoted_columns = add_index_sort_order(quoted_columns, options)
1188
1186
  end
1189
1187
 
1190
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
1188
+ quoted_columns
1189
+ end
1190
+
1191
+ def quoted_columns_for_index(column_names, **options)
1192
+ return [column_names] if column_names.is_a?(String)
1193
+
1194
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1195
+ add_options_for_index_columns(quoted_columns, options).values
1191
1196
  end
1192
1197
 
1193
1198
  def index_name_for_remove(table_name, options = {})
@@ -1210,7 +1215,7 @@ module ActiveRecord
1210
1215
  checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
1211
1216
  end
1212
1217
 
1213
- raise ArgumentError "No name or columns specified" if checks.none?
1218
+ raise ArgumentError, "No name or columns specified" if checks.none?
1214
1219
 
1215
1220
  matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1216
1221