activerecord 6.0.0.beta3 → 6.0.2.rc2

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +466 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +10 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +6 -2
  16. data/lib/active_record/associations/collection_proxy.rb +2 -2
  17. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  18. data/lib/active_record/associations/join_dependency.rb +14 -9
  19. data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
  20. data/lib/active_record/associations/preloader.rb +13 -8
  21. data/lib/active_record/associations/preloader/association.rb +34 -30
  22. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  23. data/lib/active_record/attribute_methods.rb +3 -53
  24. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  25. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  26. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  27. data/lib/active_record/attribute_methods/query.rb +2 -3
  28. data/lib/active_record/attribute_methods/read.rb +3 -9
  29. data/lib/active_record/attribute_methods/write.rb +6 -12
  30. data/lib/active_record/attributes.rb +13 -0
  31. data/lib/active_record/autosave_association.rb +21 -7
  32. data/lib/active_record/base.rb +0 -1
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  39. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
  41. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
  42. data/lib/active_record/connection_adapters/abstract_adapter.rb +111 -33
  43. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
  44. data/lib/active_record/connection_adapters/column.rb +17 -13
  45. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  46. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
  47. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  48. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  49. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
  50. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  51. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  52. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  53. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  54. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  55. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  56. data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
  57. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  58. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  59. data/lib/active_record/connection_adapters/postgresql_adapter.rb +67 -26
  60. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  61. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  62. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  63. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  64. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  65. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
  66. data/lib/active_record/connection_handling.rb +31 -13
  67. data/lib/active_record/core.rb +23 -24
  68. data/lib/active_record/database_configurations.rb +73 -44
  69. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  70. data/lib/active_record/database_configurations/url_config.rb +12 -12
  71. data/lib/active_record/dynamic_matchers.rb +1 -1
  72. data/lib/active_record/enum.rb +15 -0
  73. data/lib/active_record/errors.rb +1 -1
  74. data/lib/active_record/fixtures.rb +11 -6
  75. data/lib/active_record/gem_version.rb +2 -2
  76. data/lib/active_record/insert_all.rb +179 -0
  77. data/lib/active_record/integration.rb +13 -1
  78. data/lib/active_record/internal_metadata.rb +5 -1
  79. data/lib/active_record/locking/optimistic.rb +3 -4
  80. data/lib/active_record/log_subscriber.rb +1 -1
  81. data/lib/active_record/middleware/database_selector.rb +3 -3
  82. data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
  83. data/lib/active_record/migration.rb +62 -44
  84. data/lib/active_record/migration/command_recorder.rb +28 -14
  85. data/lib/active_record/migration/compatibility.rb +10 -0
  86. data/lib/active_record/model_schema.rb +3 -0
  87. data/lib/active_record/persistence.rb +206 -13
  88. data/lib/active_record/querying.rb +17 -12
  89. data/lib/active_record/railtie.rb +0 -1
  90. data/lib/active_record/railties/databases.rake +127 -25
  91. data/lib/active_record/reflection.rb +3 -3
  92. data/lib/active_record/relation.rb +99 -20
  93. data/lib/active_record/relation/calculations.rb +38 -40
  94. data/lib/active_record/relation/delegation.rb +22 -30
  95. data/lib/active_record/relation/finder_methods.rb +17 -12
  96. data/lib/active_record/relation/merger.rb +11 -16
  97. data/lib/active_record/relation/query_methods.rb +228 -76
  98. data/lib/active_record/relation/where_clause.rb +9 -5
  99. data/lib/active_record/sanitization.rb +33 -4
  100. data/lib/active_record/schema.rb +1 -1
  101. data/lib/active_record/schema_dumper.rb +10 -1
  102. data/lib/active_record/schema_migration.rb +1 -1
  103. data/lib/active_record/scoping/default.rb +6 -7
  104. data/lib/active_record/scoping/named.rb +3 -2
  105. data/lib/active_record/statement_cache.rb +2 -2
  106. data/lib/active_record/store.rb +48 -0
  107. data/lib/active_record/table_metadata.rb +9 -13
  108. data/lib/active_record/tasks/database_tasks.rb +109 -6
  109. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  110. data/lib/active_record/test_databases.rb +1 -16
  111. data/lib/active_record/test_fixtures.rb +1 -0
  112. data/lib/active_record/timestamp.rb +26 -16
  113. data/lib/active_record/touch_later.rb +4 -2
  114. data/lib/active_record/transactions.rb +56 -46
  115. data/lib/active_record/type_caster/connection.rb +16 -10
  116. data/lib/active_record/validations.rb +1 -0
  117. data/lib/active_record/validations/uniqueness.rb +3 -5
  118. data/lib/arel.rb +12 -5
  119. data/lib/arel/insert_manager.rb +3 -3
  120. data/lib/arel/nodes.rb +2 -1
  121. data/lib/arel/nodes/comment.rb +29 -0
  122. data/lib/arel/nodes/select_core.rb +16 -12
  123. data/lib/arel/nodes/unary.rb +1 -0
  124. data/lib/arel/nodes/values_list.rb +2 -17
  125. data/lib/arel/select_manager.rb +10 -10
  126. data/lib/arel/visitors/depth_first.rb +7 -2
  127. data/lib/arel/visitors/dot.rb +7 -2
  128. data/lib/arel/visitors/ibm_db.rb +13 -0
  129. data/lib/arel/visitors/informix.rb +6 -0
  130. data/lib/arel/visitors/mssql.rb +15 -1
  131. data/lib/arel/visitors/oracle12.rb +4 -5
  132. data/lib/arel/visitors/postgresql.rb +4 -10
  133. data/lib/arel/visitors/to_sql.rb +107 -131
  134. data/lib/arel/visitors/visitor.rb +9 -5
  135. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  136. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  137. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  138. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  139. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  140. metadata +16 -12
  141. data/lib/active_record/collection_cache_key.rb +0 -53
  142. data/lib/arel/nodes/values.rb +0 -16
@@ -5,20 +5,24 @@ require "active_support/deprecation"
5
5
  module ActiveRecord
6
6
  module ConnectionAdapters # :nodoc:
7
7
  module DatabaseLimits
8
+ def max_identifier_length # :nodoc:
9
+ 64
10
+ end
11
+
8
12
  # Returns the maximum length of a table alias.
9
13
  def table_alias_length
10
- 255
14
+ max_identifier_length
11
15
  end
12
16
 
13
17
  # Returns the maximum length of a column name.
14
18
  def column_name_length
15
- 64
19
+ max_identifier_length
16
20
  end
17
21
  deprecate :column_name_length
18
22
 
19
23
  # Returns the maximum length of a table name.
20
24
  def table_name_length
21
- 64
25
+ max_identifier_length
22
26
  end
23
27
  deprecate :table_name_length
24
28
 
@@ -33,7 +37,7 @@ module ActiveRecord
33
37
 
34
38
  # Returns the maximum length of an index name.
35
39
  def index_name_length
36
- 64
40
+ max_identifier_length
37
41
  end
38
42
 
39
43
  # Returns the maximum number of columns per table.
@@ -131,7 +131,7 @@ module ActiveRecord
131
131
  # +binds+ as the bind substitutes. +name+ is logged along with
132
132
  # the executed +sql+ statement.
133
133
  def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
134
- sql, binds = sql_for_insert(sql, pk, sequence_name, binds)
134
+ sql, binds = sql_for_insert(sql, pk, binds)
135
135
  exec_query(sql, name, binds)
136
136
  end
137
137
 
@@ -142,11 +142,6 @@ module ActiveRecord
142
142
  exec_query(sql, name, binds)
143
143
  end
144
144
 
145
- # Executes the truncate statement.
146
- def truncate(table_name, name = nil)
147
- raise NotImplementedError
148
- end
149
-
150
145
  # Executes update +sql+ statement in the context of this connection using
151
146
  # +binds+ as the bind substitutes. +name+ is logged along with
152
147
  # the executed +sql+ statement.
@@ -154,6 +149,10 @@ module ActiveRecord
154
149
  exec_query(sql, name, binds)
155
150
  end
156
151
 
152
+ def exec_insert_all(sql, name) # :nodoc:
153
+ exec_query(sql, name)
154
+ end
155
+
157
156
  # Executes an INSERT query and returns the new record's ID
158
157
  #
159
158
  # +id_value+ will be returned unless the value is +nil+, in
@@ -181,6 +180,23 @@ module ActiveRecord
181
180
  exec_delete(sql, name, binds)
182
181
  end
183
182
 
183
+ # Executes the truncate statement.
184
+ def truncate(table_name, name = nil)
185
+ execute(build_truncate_statements(table_name), name)
186
+ end
187
+
188
+ def truncate_tables(*table_names) # :nodoc:
189
+ return if table_names.empty?
190
+
191
+ with_multi_statements do
192
+ disable_referential_integrity do
193
+ Array(build_truncate_statements(*table_names)).each do |sql|
194
+ execute_batch(sql, "Truncate Tables")
195
+ end
196
+ end
197
+ end
198
+ end
199
+
184
200
  # Runs the given block in a database transaction, and returns the result
185
201
  # of the block.
186
202
  #
@@ -341,46 +357,20 @@ module ActiveRecord
341
357
  # We keep this method to provide fallback
342
358
  # for databases like sqlite that do not support bulk inserts.
343
359
  def insert_fixture(fixture, table_name)
344
- fixture = fixture.stringify_keys
345
-
346
- columns = schema_cache.columns_hash(table_name)
347
- binds = fixture.map do |name, value|
348
- if column = columns[name]
349
- type = lookup_cast_type_from_column(column)
350
- Relation::QueryAttribute.new(name, value, type)
351
- else
352
- raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
353
- end
354
- end
355
-
356
- table = Arel::Table.new(table_name)
357
-
358
- values = binds.map do |bind|
359
- value = with_yaml_fallback(bind.value_for_database)
360
- [table[bind.name], value]
361
- end
362
-
363
- manager = Arel::InsertManager.new
364
- manager.into(table)
365
- manager.insert(values)
366
- execute manager.to_sql, "Fixture Insert"
360
+ execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
367
361
  end
368
362
 
369
363
  def insert_fixtures_set(fixture_set, tables_to_delete = [])
370
- fixture_inserts = fixture_set.map do |table_name, fixtures|
371
- next if fixtures.empty?
372
-
373
- build_fixture_sql(fixtures, table_name)
374
- end.compact
375
-
376
- table_deletes = tables_to_delete.map { |table| +"DELETE FROM #{quote_table_name table}" }
377
- total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
378
-
379
- disable_referential_integrity do
380
- transaction(requires_new: true) do
381
- total_sql.each do |sql|
382
- execute sql, "Fixtures Load"
383
- yield if block_given?
364
+ fixture_inserts = build_fixture_statements(fixture_set)
365
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
366
+ total_sql = Array(combine_multi_statements(table_deletes + fixture_inserts))
367
+
368
+ with_multi_statements do
369
+ disable_referential_integrity do
370
+ transaction(requires_new: true) do
371
+ total_sql.each do |sql|
372
+ execute_batch(sql, "Fixtures Load")
373
+ end
384
374
  end
385
375
  end
386
376
  end
@@ -404,15 +394,33 @@ module ActiveRecord
404
394
  end
405
395
  end
406
396
 
397
+ # Fixture value is quoted by Arel, however scalar values
398
+ # are not quotable. In this case we want to convert
399
+ # the column value to YAML.
400
+ def with_yaml_fallback(value) # :nodoc:
401
+ if value.is_a?(Hash) || value.is_a?(Array)
402
+ YAML.dump(value)
403
+ else
404
+ value
405
+ end
406
+ end
407
+
407
408
  private
409
+ def execute_batch(sql, name = nil)
410
+ execute(sql, name)
411
+ end
412
+
413
+ DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
414
+ private_constant :DEFAULT_INSERT_VALUE
415
+
408
416
  def default_insert_value(column)
409
- Arel.sql("DEFAULT")
417
+ DEFAULT_INSERT_VALUE
410
418
  end
411
419
 
412
420
  def build_fixture_sql(fixtures, table_name)
413
421
  columns = schema_cache.columns_hash(table_name)
414
422
 
415
- values = fixtures.map do |fixture|
423
+ values_list = fixtures.map do |fixture|
416
424
  fixture = fixture.stringify_keys
417
425
 
418
426
  unknown_columns = fixture.keys - columns.keys
@@ -423,8 +431,7 @@ module ActiveRecord
423
431
  columns.map do |name, column|
424
432
  if fixture.key?(name)
425
433
  type = lookup_cast_type_from_column(column)
426
- bind = Relation::QueryAttribute.new(name, fixture[name], type)
427
- with_yaml_fallback(bind.value_for_database)
434
+ with_yaml_fallback(type.serialize(fixture[name]))
428
435
  else
429
436
  default_insert_value(column)
430
437
  end
@@ -434,12 +441,43 @@ module ActiveRecord
434
441
  table = Arel::Table.new(table_name)
435
442
  manager = Arel::InsertManager.new
436
443
  manager.into(table)
437
- columns.each_key { |column| manager.columns << table[column] }
438
- manager.values = manager.create_values_list(values)
439
444
 
445
+ if values_list.size == 1
446
+ values = values_list.shift
447
+ new_values = []
448
+ columns.each_key.with_index { |column, i|
449
+ unless values[i].equal?(DEFAULT_INSERT_VALUE)
450
+ new_values << values[i]
451
+ manager.columns << table[column]
452
+ end
453
+ }
454
+ values_list << new_values
455
+ else
456
+ columns.each_key { |column| manager.columns << table[column] }
457
+ end
458
+
459
+ manager.values = manager.create_values_list(values_list)
440
460
  manager.to_sql
441
461
  end
442
462
 
463
+ def build_fixture_statements(fixture_set)
464
+ fixture_set.map do |table_name, fixtures|
465
+ next if fixtures.empty?
466
+ build_fixture_sql(fixtures, table_name)
467
+ end.compact
468
+ end
469
+
470
+ def build_truncate_statements(*table_names)
471
+ truncate_tables = table_names.map do |table_name|
472
+ "TRUNCATE TABLE #{quote_table_name(table_name)}"
473
+ end
474
+ combine_multi_statements(truncate_tables)
475
+ end
476
+
477
+ def with_multi_statements
478
+ yield
479
+ end
480
+
443
481
  def combine_multi_statements(total_sql)
444
482
  total_sql.join(";\n")
445
483
  end
@@ -453,7 +491,7 @@ module ActiveRecord
453
491
  exec_query(sql, name, binds, prepare: true)
454
492
  end
455
493
 
456
- def sql_for_insert(sql, pk, sequence_name, binds)
494
+ def sql_for_insert(sql, pk, binds)
457
495
  [sql, binds]
458
496
  end
459
497
 
@@ -473,17 +511,6 @@ module ActiveRecord
473
511
  relation
474
512
  end
475
513
  end
476
-
477
- # Fixture value is quoted by Arel, however scalar values
478
- # are not quotable. In this case we want to convert
479
- # the column value to YAML.
480
- def with_yaml_fallback(value)
481
- if value.is_a?(Hash) || value.is_a?(Array)
482
- YAML.dump(value)
483
- else
484
- value
485
- end
486
- end
487
514
  end
488
515
  end
489
516
  end
@@ -7,7 +7,8 @@ module ActiveRecord
7
7
  module QueryCache
8
8
  class << self
9
9
  def included(base) #:nodoc:
10
- dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
10
+ dirties_query_cache base, :insert, :update, :delete, :truncate, :truncate_tables,
11
+ :rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
11
12
 
12
13
  base.set_callback :checkout, :after, :configure_query_cache!
13
14
  base.set_callback :checkin, :after, :disable_query_cache!
@@ -32,17 +33,17 @@ module ActiveRecord
32
33
  end
33
34
 
34
35
  def enable_query_cache!
35
- @query_cache_enabled[connection_cache_key(Thread.current)] = true
36
+ @query_cache_enabled[connection_cache_key(current_thread)] = true
36
37
  connection.enable_query_cache! if active_connection?
37
38
  end
38
39
 
39
40
  def disable_query_cache!
40
- @query_cache_enabled.delete connection_cache_key(Thread.current)
41
+ @query_cache_enabled.delete connection_cache_key(current_thread)
41
42
  connection.disable_query_cache! if active_connection?
42
43
  end
43
44
 
44
45
  def query_cache_enabled
45
- @query_cache_enabled[connection_cache_key(Thread.current)]
46
+ @query_cache_enabled[connection_cache_key(current_thread)]
46
47
  end
47
48
  end
48
49
 
@@ -134,6 +135,7 @@ module ActiveRecord
134
135
  type_casted_binds: -> { type_casted_binds(binds) },
135
136
  name: name,
136
137
  connection_id: object_id,
138
+ connection: self,
137
139
  cached: true
138
140
  }
139
141
  end
@@ -138,15 +138,72 @@ module ActiveRecord
138
138
  "'#{quote_string(value.to_s)}'"
139
139
  end
140
140
 
141
- def type_casted_binds(binds) # :nodoc:
142
- if binds.first.is_a?(Array)
143
- binds.map { |column, value| type_cast(value, column) }
144
- else
145
- binds.map { |attr| type_cast(attr.value_for_database) }
146
- end
141
+ def sanitize_as_sql_comment(value) # :nodoc:
142
+ value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
143
+ end
144
+
145
+ def column_name_matcher # :nodoc:
146
+ COLUMN_NAME
147
+ end
148
+
149
+ def column_name_with_order_matcher # :nodoc:
150
+ COLUMN_NAME_WITH_ORDER
147
151
  end
148
152
 
153
+ # Regexp for column names (with or without a table name prefix).
154
+ # Matches the following:
155
+ #
156
+ # "#{table_name}.#{column_name}"
157
+ # "#{column_name}"
158
+ COLUMN_NAME = /
159
+ \A
160
+ (
161
+ (?:
162
+ # table_name.column_name | function(one or no argument)
163
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
164
+ )
165
+ (?:\s+AS\s+\w+)?
166
+ )
167
+ (?:\s*,\s*\g<1>)*
168
+ \z
169
+ /ix
170
+
171
+ # Regexp for column names with order (with or without a table name prefix,
172
+ # with or without various order modifiers). Matches the following:
173
+ #
174
+ # "#{table_name}.#{column_name}"
175
+ # "#{table_name}.#{column_name} #{direction}"
176
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
177
+ # "#{table_name}.#{column_name} NULLS LAST"
178
+ # "#{column_name}"
179
+ # "#{column_name} #{direction}"
180
+ # "#{column_name} #{direction} NULLS FIRST"
181
+ # "#{column_name} NULLS LAST"
182
+ COLUMN_NAME_WITH_ORDER = /
183
+ \A
184
+ (
185
+ (?:
186
+ # table_name.column_name | function(one or no argument)
187
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
188
+ )
189
+ (?:\s+ASC|\s+DESC)?
190
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
191
+ )
192
+ (?:\s*,\s*\g<1>)*
193
+ \z
194
+ /ix
195
+
196
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
197
+
149
198
  private
199
+ def type_casted_binds(binds)
200
+ if binds.first.is_a?(Array)
201
+ binds.map { |column, value| type_cast(value, column) }
202
+ else
203
+ binds.map { |attr| type_cast(attr.value_for_database) }
204
+ end
205
+ end
206
+
150
207
  def lookup_cast_type(sql_type)
151
208
  type_map.lookup(sql_type)
152
209
  end
@@ -105,13 +105,9 @@ module ActiveRecord
105
105
  !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
106
106
  end
107
107
 
108
- def defined_for?(to_table_ord = nil, to_table: nil, **options)
109
- if to_table_ord
110
- self.to_table == to_table_ord.to_s
111
- else
112
- (to_table.nil? || to_table.to_s == self.to_table) &&
113
- options.all? { |k, v| self.options[k].to_s == v.to_s }
114
- end
108
+ def defined_for?(to_table: nil, **options)
109
+ (to_table.nil? || to_table.to_s == self.to_table) &&
110
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
115
111
  end
116
112
 
117
113
  private
@@ -420,6 +416,7 @@ module ActiveRecord
420
416
  #
421
417
  # t.references(:user)
422
418
  # t.belongs_to(:supplier, foreign_key: true)
419
+ # t.belongs_to(:supplier, foreign_key: true, type: :integer)
423
420
  #
424
421
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
425
422
  def references(*args, **options)
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  def column_spec_for_primary_key(column)
16
16
  return {} if default_primary_key?(column)
17
17
  spec = { id: schema_type(column).inspect }
18
- spec.merge!(prepare_column_options(column).except!(:null))
18
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
19
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
20
  spec
21
21
  end
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  def index_exists?(table_name, column_name, options = {})
102
102
  column_names = Array(column_name).map(&:to_s)
103
103
  checks = []
104
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
105
105
  checks << lambda { |i| i.unique } if options[:unique]
106
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
107
 
@@ -302,7 +302,7 @@ module ActiveRecord
302
302
  if pk.is_a?(Array)
303
303
  td.primary_keys pk
304
304
  else
305
- td.primary_key pk, options.fetch(:id, :primary_key), options
305
+ td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
306
306
  end
307
307
  end
308
308
 
@@ -518,14 +518,15 @@ module ActiveRecord
518
518
  # Available options are (none of these exists by default):
519
519
  # * <tt>:limit</tt> -
520
520
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
521
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
521
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
522
522
  # This option is ignored by some backends.
523
523
  # * <tt>:default</tt> -
524
524
  # The column's default value. Use +nil+ for +NULL+.
525
525
  # * <tt>:null</tt> -
526
526
  # Allows or disallows +NULL+ values in the column.
527
527
  # * <tt>:precision</tt> -
528
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
528
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
529
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
529
530
  # * <tt>:scale</tt> -
530
531
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
531
532
  # * <tt>:collation</tt> -
@@ -770,6 +771,17 @@ module ActiveRecord
770
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
771
772
  #
772
773
  # Note: only supported by MySQL.
774
+ #
775
+ # ====== Creating an index with a specific algorithm
776
+ #
777
+ # add_index(:developers, :name, algorithm: :concurrently)
778
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
779
+ #
780
+ # Note: only supported by PostgreSQL.
781
+ #
782
+ # Concurrently adding an index is not supported in a transaction.
783
+ #
784
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
773
785
  def add_index(table_name, column_name, options = {})
774
786
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
775
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -793,6 +805,15 @@ module ActiveRecord
793
805
  #
794
806
  # remove_index :accounts, name: :by_branch_party
795
807
  #
808
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
+ #
810
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
811
+ #
812
+ # Note: only supported by PostgreSQL.
813
+ #
814
+ # Concurrently removing an index is not supported in a transaction.
815
+ #
816
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
796
817
  def remove_index(table_name, options = {})
797
818
  index_name = index_name_for_remove(table_name, options)
798
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
@@ -966,7 +987,7 @@ module ActiveRecord
966
987
  # [<tt>:on_update</tt>]
967
988
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
968
989
  # [<tt>:validate</tt>]
969
- # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
990
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
970
991
  def add_foreign_key(from_table, to_table, options = {})
971
992
  return unless supports_foreign_keys?
972
993
 
@@ -1002,10 +1023,10 @@ module ActiveRecord
1002
1023
  # with an addition of
1003
1024
  # [<tt>:to_table</tt>]
1004
1025
  # The name of the table that contains the referenced primary key.
1005
- def remove_foreign_key(from_table, options_or_to_table = {})
1026
+ def remove_foreign_key(from_table, to_table = nil, **options)
1006
1027
  return unless supports_foreign_keys?
1007
1028
 
1008
- fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1009
1030
 
1010
1031
  at = create_alter_table from_table
1011
1032
  at.drop_foreign_key fk_name_to_delete
@@ -1024,8 +1045,8 @@ module ActiveRecord
1024
1045
  # # Checks to see if a foreign key with a custom name exists.
1025
1046
  # foreign_key_exists?(:accounts, name: "special_fk_name")
1026
1047
  #
1027
- def foreign_key_exists?(from_table, options_or_to_table = {})
1028
- foreign_key_for(from_table, options_or_to_table).present?
1048
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1049
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1029
1050
  end
1030
1051
 
1031
1052
  def foreign_key_column_for(table_name) # :nodoc:
@@ -1040,8 +1061,8 @@ module ActiveRecord
1040
1061
  options
1041
1062
  end
1042
1063
 
1043
- def dump_schema_information #:nodoc:
1044
- versions = ActiveRecord::SchemaMigration.all_versions
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1045
1066
  insert_versions_sql(versions) if versions.any?
1046
1067
  end
1047
1068
 
@@ -1051,13 +1072,13 @@ module ActiveRecord
1051
1072
 
1052
1073
  def assume_migrated_upto_version(version, migrations_paths = nil)
1053
1074
  unless migrations_paths.nil?
1054
- ActiveSupport::Deprecation.warn(<<~MSG)
1075
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
1055
1076
  Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1056
1077
  MSG
1057
1078
  end
1058
1079
 
1059
1080
  version = version.to_i
1060
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
1061
1082
 
1062
1083
  migrated = migration_context.get_all_versions
1063
1084
  versions = migration_context.migrations.map(&:version)
@@ -1097,7 +1118,7 @@ module ActiveRecord
1097
1118
  if (0..6) === precision
1098
1119
  column_type_sql << "(#{precision})"
1099
1120
  else
1100
- raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1121
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1101
1122
  end
1102
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1103
1124
  column_type_sql << "(#{limit})"
@@ -1185,12 +1206,22 @@ module ActiveRecord
1185
1206
  end
1186
1207
 
1187
1208
  # Changes the comment for a table or removes it if +nil+.
1188
- def change_table_comment(table_name, comment)
1209
+ #
1210
+ # Passing a hash containing +:from+ and +:to+ will make this change
1211
+ # reversible in migration:
1212
+ #
1213
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1214
+ def change_table_comment(table_name, comment_or_changes)
1189
1215
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1190
1216
  end
1191
1217
 
1192
1218
  # Changes the comment for a column or removes it if +nil+.
1193
- def change_column_comment(table_name, column_name, comment)
1219
+ #
1220
+ # Passing a hash containing +:from+ and +:to+ will make this change
1221
+ # reversible in migration:
1222
+ #
1223
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1224
+ def change_column_comment(table_name, column_name, comment_or_changes)
1194
1225
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1195
1226
  end
1196
1227
 
@@ -1341,14 +1372,14 @@ module ActiveRecord
1341
1372
  end
1342
1373
  end
1343
1374
 
1344
- def foreign_key_for(from_table, options_or_to_table = {})
1375
+ def foreign_key_for(from_table, **options)
1345
1376
  return unless supports_foreign_keys?
1346
- foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
1377
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1347
1378
  end
1348
1379
 
1349
- def foreign_key_for!(from_table, options_or_to_table = {})
1350
- foreign_key_for(from_table, options_or_to_table) || \
1351
- raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
1380
+ def foreign_key_for!(from_table, to_table: nil, **options)
1381
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1382
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1352
1383
  end
1353
1384
 
1354
1385
  def extract_foreign_key_action(specifier)
@@ -1374,11 +1405,37 @@ module ActiveRecord
1374
1405
  default_or_changes
1375
1406
  end
1376
1407
  end
1408
+ alias :extract_new_comment_value :extract_new_default_value
1377
1409
 
1378
1410
  def can_remove_index_by_name?(options)
1379
1411
  options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1380
1412
  end
1381
1413
 
1414
+ def bulk_change_table(table_name, operations)
1415
+ sql_fragments = []
1416
+ non_combinable_operations = []
1417
+
1418
+ operations.each do |command, args|
1419
+ table, arguments = args.shift, args
1420
+ method = :"#{command}_for_alter"
1421
+
1422
+ if respond_to?(method, true)
1423
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1424
+ sql_fragments << sqls
1425
+ non_combinable_operations.concat(procs)
1426
+ else
1427
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1428
+ non_combinable_operations.each(&:call)
1429
+ sql_fragments = []
1430
+ non_combinable_operations = []
1431
+ send(command, table, *arguments)
1432
+ end
1433
+ end
1434
+
1435
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1436
+ non_combinable_operations.each(&:call)
1437
+ end
1438
+
1382
1439
  def add_column_for_alter(table_name, column_name, type, options = {})
1383
1440
  td = create_table_definition(table_name)
1384
1441
  cd = td.new_column_definition(column_name, type, options)
@@ -1394,7 +1451,7 @@ module ActiveRecord
1394
1451
  end
1395
1452
 
1396
1453
  def insert_versions_sql(versions)
1397
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1454
+ sm_table = quote_table_name(schema_migration.table_name)
1398
1455
 
1399
1456
  if versions.is_a?(Array)
1400
1457
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"