activerecord 6.0.0 → 6.1.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1045 -575
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +5 -6
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations.rb +120 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +33 -9
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -77
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  95. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  97. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  99. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  100. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  103. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  104. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  106. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  107. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  108. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  109. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  110. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  111. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  112. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  113. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  114. data/lib/active_record/connection_handling.rb +219 -81
  115. data/lib/active_record/core.rb +253 -67
  116. data/lib/active_record/counter_cache.rb +4 -1
  117. data/lib/active_record/database_configurations.rb +124 -85
  118. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  119. data/lib/active_record/database_configurations/database_config.rb +52 -9
  120. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  121. data/lib/active_record/database_configurations/url_config.rb +15 -41
  122. data/lib/active_record/delegated_type.rb +209 -0
  123. data/lib/active_record/destroy_association_async_job.rb +36 -0
  124. data/lib/active_record/dynamic_matchers.rb +2 -3
  125. data/lib/active_record/enum.rb +82 -38
  126. data/lib/active_record/errors.rb +47 -12
  127. data/lib/active_record/explain.rb +9 -5
  128. data/lib/active_record/explain_subscriber.rb +1 -1
  129. data/lib/active_record/fixture_set/file.rb +10 -17
  130. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  131. data/lib/active_record/fixture_set/render_context.rb +1 -1
  132. data/lib/active_record/fixture_set/table_row.rb +2 -3
  133. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  134. data/lib/active_record/fixtures.rb +58 -12
  135. data/lib/active_record/gem_version.rb +2 -2
  136. data/lib/active_record/inheritance.rb +40 -21
  137. data/lib/active_record/insert_all.rb +39 -10
  138. data/lib/active_record/integration.rb +3 -5
  139. data/lib/active_record/internal_metadata.rb +16 -7
  140. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  141. data/lib/active_record/locking/optimistic.rb +33 -18
  142. data/lib/active_record/locking/pessimistic.rb +6 -2
  143. data/lib/active_record/log_subscriber.rb +28 -9
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  147. data/lib/active_record/migration.rb +115 -85
  148. data/lib/active_record/migration/command_recorder.rb +53 -45
  149. data/lib/active_record/migration/compatibility.rb +71 -20
  150. data/lib/active_record/migration/join_table.rb +0 -1
  151. data/lib/active_record/model_schema.rb +120 -15
  152. data/lib/active_record/nested_attributes.rb +2 -5
  153. data/lib/active_record/no_touching.rb +1 -1
  154. data/lib/active_record/null_relation.rb +0 -1
  155. data/lib/active_record/persistence.rb +50 -46
  156. data/lib/active_record/query_cache.rb +15 -5
  157. data/lib/active_record/querying.rb +12 -7
  158. data/lib/active_record/railtie.rb +65 -45
  159. data/lib/active_record/railties/console_sandbox.rb +2 -4
  160. data/lib/active_record/railties/databases.rake +277 -97
  161. data/lib/active_record/readonly_attributes.rb +4 -0
  162. data/lib/active_record/reflection.rb +77 -63
  163. data/lib/active_record/relation.rb +107 -67
  164. data/lib/active_record/relation/batches.rb +38 -32
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  166. data/lib/active_record/relation/calculations.rb +102 -45
  167. data/lib/active_record/relation/delegation.rb +9 -7
  168. data/lib/active_record/relation/finder_methods.rb +55 -17
  169. data/lib/active_record/relation/from_clause.rb +5 -1
  170. data/lib/active_record/relation/merger.rb +27 -26
  171. data/lib/active_record/relation/predicate_builder.rb +59 -40
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  175. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  176. data/lib/active_record/relation/query_methods.rb +343 -180
  177. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  178. data/lib/active_record/relation/spawn_methods.rb +8 -8
  179. data/lib/active_record/relation/where_clause.rb +107 -61
  180. data/lib/active_record/result.rb +41 -34
  181. data/lib/active_record/runtime_registry.rb +2 -2
  182. data/lib/active_record/sanitization.rb +6 -17
  183. data/lib/active_record/schema_dumper.rb +34 -4
  184. data/lib/active_record/schema_migration.rb +2 -8
  185. data/lib/active_record/scoping.rb +0 -1
  186. data/lib/active_record/scoping/default.rb +0 -1
  187. data/lib/active_record/scoping/named.rb +7 -18
  188. data/lib/active_record/secure_token.rb +16 -8
  189. data/lib/active_record/serialization.rb +5 -3
  190. data/lib/active_record/signed_id.rb +116 -0
  191. data/lib/active_record/statement_cache.rb +20 -4
  192. data/lib/active_record/store.rb +3 -3
  193. data/lib/active_record/suppressor.rb +2 -2
  194. data/lib/active_record/table_metadata.rb +42 -36
  195. data/lib/active_record/tasks/database_tasks.rb +140 -113
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  199. data/lib/active_record/test_databases.rb +5 -4
  200. data/lib/active_record/test_fixtures.rb +38 -16
  201. data/lib/active_record/timestamp.rb +4 -7
  202. data/lib/active_record/touch_later.rb +20 -21
  203. data/lib/active_record/transactions.rb +26 -73
  204. data/lib/active_record/type.rb +8 -2
  205. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  206. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  207. data/lib/active_record/type/serialized.rb +6 -3
  208. data/lib/active_record/type/time.rb +10 -0
  209. data/lib/active_record/type/type_map.rb +0 -1
  210. data/lib/active_record/type/unsigned_integer.rb +0 -1
  211. data/lib/active_record/type_caster/connection.rb +0 -1
  212. data/lib/active_record/type_caster/map.rb +8 -5
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record/validations/associated.rb +1 -2
  215. data/lib/active_record/validations/numericality.rb +35 -0
  216. data/lib/active_record/validations/uniqueness.rb +24 -4
  217. data/lib/arel.rb +15 -12
  218. data/lib/arel/attributes/attribute.rb +4 -0
  219. data/lib/arel/collectors/bind.rb +5 -0
  220. data/lib/arel/collectors/composite.rb +8 -0
  221. data/lib/arel/collectors/sql_string.rb +7 -0
  222. data/lib/arel/collectors/substitute_binds.rb +7 -0
  223. data/lib/arel/nodes.rb +3 -1
  224. data/lib/arel/nodes/binary.rb +82 -8
  225. data/lib/arel/nodes/bind_param.rb +8 -0
  226. data/lib/arel/nodes/casted.rb +21 -9
  227. data/lib/arel/nodes/equality.rb +6 -9
  228. data/lib/arel/nodes/grouping.rb +3 -0
  229. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  230. data/lib/arel/nodes/in.rb +8 -1
  231. data/lib/arel/nodes/infix_operation.rb +13 -1
  232. data/lib/arel/nodes/join_source.rb +1 -1
  233. data/lib/arel/nodes/node.rb +7 -6
  234. data/lib/arel/nodes/ordering.rb +27 -0
  235. data/lib/arel/nodes/sql_literal.rb +3 -0
  236. data/lib/arel/nodes/table_alias.rb +7 -3
  237. data/lib/arel/nodes/unary.rb +0 -1
  238. data/lib/arel/predications.rb +17 -24
  239. data/lib/arel/select_manager.rb +1 -2
  240. data/lib/arel/table.rb +13 -5
  241. data/lib/arel/visitors.rb +0 -7
  242. data/lib/arel/visitors/dot.rb +14 -3
  243. data/lib/arel/visitors/mysql.rb +11 -1
  244. data/lib/arel/visitors/postgresql.rb +15 -5
  245. data/lib/arel/visitors/sqlite.rb +0 -1
  246. data/lib/arel/visitors/to_sql.rb +89 -79
  247. data/lib/arel/visitors/visitor.rb +0 -1
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration.rb +6 -2
  250. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  251. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  252. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -8,6 +8,7 @@ module ActiveRecord
8
8
  #
9
9
  # * add_column
10
10
  # * add_foreign_key
11
+ # * add_check_constraint
11
12
  # * add_index
12
13
  # * add_reference
13
14
  # * add_timestamps
@@ -25,6 +26,7 @@ module ActiveRecord
25
26
  # * remove_column (must supply a type)
26
27
  # * remove_columns (must specify at least one column name or more)
27
28
  # * remove_foreign_key (must supply a second table)
29
+ # * remove_check_constraint
28
30
  # * remove_index
29
31
  # * remove_reference
30
32
  # * remove_timestamps
@@ -32,13 +34,15 @@ module ActiveRecord
32
34
  # * rename_index
33
35
  # * rename_table
34
36
  class CommandRecorder
35
- ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
37
+ ReversibleAndIrreversibleMethods = [
38
+ :create_table, :create_join_table, :rename_table, :add_column, :remove_column,
36
39
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
37
40
  :change_column_default, :add_reference, :remove_reference, :transaction,
38
41
  :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
39
42
  :change_column, :execute, :remove_columns, :change_column_null,
40
43
  :add_foreign_key, :remove_foreign_key,
41
- :change_column_comment, :change_table_comment
44
+ :change_column_comment, :change_table_comment,
45
+ :add_check_constraint, :remove_check_constraint
42
46
  ]
43
47
  include JoinTable
44
48
 
@@ -84,6 +88,11 @@ module ActiveRecord
84
88
  # recorder.inverse_of(:rename_table, [:old, :new])
85
89
  # # => [:rename_table, [:new, :old]]
86
90
  #
91
+ # If the inverse of a command requires several commands, returns array of commands.
92
+ #
93
+ # recorder.inverse_of(:remove_columns, [:some_table, :foo, :bar, type: :string])
94
+ # # => [[:add_column, :some_table, :foo, :string], [:add_column, :some_table, :bar, :string]]
95
+ #
87
96
  # This method will raise an +IrreversibleMigration+ exception if it cannot
88
97
  # invert the +command+.
89
98
  def inverse_of(command, args, &block)
@@ -103,11 +112,12 @@ module ActiveRecord
103
112
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
104
113
  end # end
105
114
  EOV
115
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
106
116
  end
107
117
  alias :add_belongs_to :add_reference
108
118
  alias :remove_belongs_to :remove_reference
109
119
 
110
- def change_table(table_name, options = {}) # :nodoc:
120
+ def change_table(table_name, **options) # :nodoc:
111
121
  yield delegate.update_table_definition(table_name, self)
112
122
  end
113
123
 
@@ -118,7 +128,6 @@ module ActiveRecord
118
128
  end
119
129
 
120
130
  private
121
-
122
131
  module StraightReversions # :nodoc:
123
132
  private
124
133
  {
@@ -126,8 +135,11 @@ module ActiveRecord
126
135
  create_table: :drop_table,
127
136
  create_join_table: :drop_join_table,
128
137
  add_column: :remove_column,
138
+ add_index: :remove_index,
129
139
  add_timestamps: :remove_timestamps,
130
140
  add_reference: :remove_reference,
141
+ add_foreign_key: :remove_foreign_key,
142
+ add_check_constraint: :remove_check_constraint,
131
143
  enable_extension: :disable_extension
132
144
  }.each do |cmd, inv|
133
145
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
@@ -169,44 +181,49 @@ module ActiveRecord
169
181
  super
170
182
  end
171
183
 
184
+ def invert_remove_columns(args)
185
+ unless args[-1].is_a?(Hash) && args[-1].has_key?(:type)
186
+ raise ActiveRecord::IrreversibleMigration, "remove_columns is only reversible if given a type."
187
+ end
188
+
189
+ [:add_columns, args]
190
+ end
191
+
172
192
  def invert_rename_index(args)
173
- [:rename_index, [args.first] + args.last(2).reverse]
193
+ table_name, old_name, new_name = args
194
+ [:rename_index, [table_name, new_name, old_name]]
174
195
  end
175
196
 
176
197
  def invert_rename_column(args)
177
- [:rename_column, [args.first] + args.last(2).reverse]
198
+ table_name, old_name, new_name = args
199
+ [:rename_column, [table_name, new_name, old_name]]
178
200
  end
179
201
 
180
- def invert_add_index(args)
181
- table, columns, options = *args
182
- options ||= {}
183
-
184
- options_hash = options.slice(:name, :algorithm)
185
- options_hash[:column] = columns if !options_hash[:name]
202
+ def invert_remove_index(args)
203
+ options = args.extract_options!
204
+ table, columns = args
186
205
 
187
- [:remove_index, [table, options_hash]]
188
- end
206
+ columns ||= options.delete(:column)
189
207
 
190
- def invert_remove_index(args)
191
- table, options_or_column = *args
192
- if (options = options_or_column).is_a?(Hash)
193
- unless options[:column]
194
- raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
195
- end
196
- options = options.dup
197
- [:add_index, [table, options.delete(:column), options]]
198
- elsif (column = options_or_column).present?
199
- [:add_index, [table, column]]
208
+ unless columns
209
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
200
210
  end
211
+
212
+ options.delete(:if_exists)
213
+
214
+ args = [table, columns]
215
+ args << options unless options.empty?
216
+
217
+ [:add_index, args]
201
218
  end
202
219
 
203
220
  alias :invert_add_belongs_to :invert_add_reference
204
221
  alias :invert_remove_belongs_to :invert_remove_reference
205
222
 
206
223
  def invert_change_column_default(args)
207
- table, column, options = *args
224
+ table, column, options = args
208
225
 
209
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
226
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
210
227
  raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
211
228
  end
212
229
 
@@ -218,21 +235,6 @@ module ActiveRecord
218
235
  [:change_column_null, args]
219
236
  end
220
237
 
221
- def invert_add_foreign_key(args)
222
- from_table, to_table, add_options = args
223
- add_options ||= {}
224
-
225
- if add_options[:name]
226
- options = { name: add_options[:name] }
227
- elsif add_options[:column]
228
- options = { column: add_options[:column] }
229
- else
230
- options = to_table
231
- end
232
-
233
- [:remove_foreign_key, [from_table, options]]
234
- end
235
-
236
238
  def invert_remove_foreign_key(args)
237
239
  options = args.extract_options!
238
240
  from_table, to_table = args
@@ -248,9 +250,9 @@ module ActiveRecord
248
250
  end
249
251
 
250
252
  def invert_change_column_comment(args)
251
- table, column, options = *args
253
+ table, column, options = args
252
254
 
253
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
255
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
254
256
  raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
255
257
  end
256
258
 
@@ -258,15 +260,20 @@ module ActiveRecord
258
260
  end
259
261
 
260
262
  def invert_change_table_comment(args)
261
- table, options = *args
263
+ table, options = args
262
264
 
263
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
265
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
264
266
  raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
265
267
  end
266
268
 
267
269
  [:change_table_comment, [table, from: options[:to], to: options[:from]]]
268
270
  end
269
271
 
272
+ def invert_remove_check_constraint(args)
273
+ raise ActiveRecord::IrreversibleMigration, "remove_check_constraint is only reversible if given an expression." if args.size < 2
274
+ super
275
+ end
276
+
270
277
  def respond_to_missing?(method, _)
271
278
  super || delegate.respond_to?(method)
272
279
  end
@@ -279,6 +286,7 @@ module ActiveRecord
279
286
  super
280
287
  end
281
288
  end
289
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
282
290
  end
283
291
  end
284
292
  end
@@ -13,7 +13,62 @@ module ActiveRecord
13
13
  const_get(name)
14
14
  end
15
15
 
16
- V6_0 = Current
16
+ V6_1 = Current
17
+
18
+ class V6_0 < V6_1
19
+ class ReferenceDefinition < ConnectionAdapters::ReferenceDefinition
20
+ def index_options(table_name)
21
+ as_options(index)
22
+ end
23
+ end
24
+
25
+ module TableDefinition
26
+ def references(*args, **options)
27
+ args.each do |ref_name|
28
+ ReferenceDefinition.new(ref_name, **options).add_to(self)
29
+ end
30
+ end
31
+ alias :belongs_to :references
32
+ end
33
+
34
+ def create_table(table_name, **options)
35
+ if block_given?
36
+ super { |t| yield compatible_table_definition(t) }
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def change_table(table_name, **options)
43
+ if block_given?
44
+ super { |t| yield compatible_table_definition(t) }
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ def create_join_table(table_1, table_2, **options)
51
+ if block_given?
52
+ super { |t| yield compatible_table_definition(t) }
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def add_reference(table_name, ref_name, **options)
59
+ ReferenceDefinition.new(ref_name, **options)
60
+ .add_to(connection.update_table_definition(table_name, self))
61
+ end
62
+ alias :add_belongs_to :add_reference
63
+
64
+ private
65
+ def compatible_table_definition(t)
66
+ class << t
67
+ prepend TableDefinition
68
+ end
69
+ t
70
+ end
71
+ end
17
72
 
18
73
  class V5_2 < V6_0
19
74
  module TableDefinition
@@ -29,13 +84,11 @@ module ActiveRecord
29
84
  end
30
85
 
31
86
  def invert_change_column_comment(args)
32
- table_name, column_name, comment = args
33
- [:change_column_comment, [table_name, column_name, from: comment, to: comment]]
87
+ [:change_column_comment, args]
34
88
  end
35
89
 
36
90
  def invert_change_table_comment(args)
37
- table_name, comment = args
38
- [:change_table_comment, [table_name, from: comment, to: comment]]
91
+ [:change_table_comment, args]
39
92
  end
40
93
  end
41
94
 
@@ -86,9 +139,9 @@ module ActiveRecord
86
139
  end
87
140
 
88
141
  class V5_1 < V5_2
89
- def change_column(table_name, column_name, type, options = {})
142
+ def change_column(table_name, column_name, type, **options)
90
143
  if connection.adapter_name == "PostgreSQL"
91
- super(table_name, column_name, type, options.except(:default, :null, :comment))
144
+ super(table_name, column_name, type, **options.except(:default, :null, :comment))
92
145
  connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
93
146
  connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
94
147
  connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
@@ -97,7 +150,7 @@ module ActiveRecord
97
150
  end
98
151
  end
99
152
 
100
- def create_table(table_name, options = {})
153
+ def create_table(table_name, **options)
101
154
  if connection.adapter_name == "Mysql2"
102
155
  super(table_name, options: "ENGINE=InnoDB", **options)
103
156
  else
@@ -119,7 +172,7 @@ module ActiveRecord
119
172
  alias :belongs_to :references
120
173
  end
121
174
 
122
- def create_table(table_name, options = {})
175
+ def create_table(table_name, **options)
123
176
  if connection.adapter_name == "PostgreSQL"
124
177
  if options[:id] == :uuid && !options.key?(:default)
125
178
  options[:default] = "uuid_generate_v4()"
@@ -147,7 +200,7 @@ module ActiveRecord
147
200
  super
148
201
  end
149
202
 
150
- def add_column(table_name, column_name, type, options = {})
203
+ def add_column(table_name, column_name, type, **options)
151
204
  if type == :primary_key
152
205
  type = :integer
153
206
  options[:primary_key] = true
@@ -194,7 +247,7 @@ module ActiveRecord
194
247
  super
195
248
  end
196
249
 
197
- def index_exists?(table_name, column_name, options = {})
250
+ def index_exists?(table_name, column_name, **options)
198
251
  column_names = Array(column_name).map(&:to_s)
199
252
  options[:name] =
200
253
  if options[:name].present?
@@ -205,10 +258,9 @@ module ActiveRecord
205
258
  super
206
259
  end
207
260
 
208
- def remove_index(table_name, options = {})
209
- options = { column: options } unless options.is_a?(Hash)
210
- options[:name] = index_name_for_remove(table_name, options)
211
- super(table_name, options)
261
+ def remove_index(table_name, column_name = nil, **options)
262
+ options[:name] = index_name_for_remove(table_name, column_name, options)
263
+ super
212
264
  end
213
265
 
214
266
  private
@@ -219,13 +271,12 @@ module ActiveRecord
219
271
  super
220
272
  end
221
273
 
222
- def index_name_for_remove(table_name, options = {})
223
- index_name = connection.index_name(table_name, options)
274
+ def index_name_for_remove(table_name, column_name, options)
275
+ index_name = connection.index_name(table_name, column_name || options)
224
276
 
225
277
  unless connection.index_name_exists?(table_name, index_name)
226
- if options.is_a?(Hash) && options.has_key?(:name)
227
- options_without_column = options.dup
228
- options_without_column.delete :column
278
+ if options.key?(:name)
279
+ options_without_column = options.except(:column)
229
280
  index_name_without_column = connection.index_name(table_name, options_without_column)
230
281
 
231
282
  if connection.index_name_exists?(table_name, index_name_without_column)
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  class Migration
5
5
  module JoinTable #:nodoc:
6
6
  private
7
-
8
7
  def find_join_table_name(table_1, table_2, options = {})
9
8
  options.delete(:table_name) || join_table_name(table_1, table_2)
10
9
  end
@@ -115,8 +115,17 @@ module ActiveRecord
115
115
  #
116
116
  # Sets the column to sort records by when no explicit order clause is used
117
117
  # during an ordered finder call. Useful when the primary key is not an
118
- # auto-incrementing integer, for example when it's a UUID. Note that using
119
- # a non-unique column can result in non-deterministic results.
118
+ # auto-incrementing integer, for example when it's a UUID. Records are subsorted
119
+ # by the primary key if it exists to ensure deterministic results.
120
+
121
+ ##
122
+ # :singleton-method: immutable_strings_by_default=
123
+ # :call-seq: immutable_strings_by_default=(bool)
124
+ #
125
+ # Determines whether columns should infer their type as +:string+ or
126
+ # +:immutable_string+. This setting does not affect the behavior of
127
+ # <tt>attribute :foo, :string</tt>. Defaults to false.
128
+
120
129
  included do
121
130
  mattr_accessor :primary_key_prefix_type, instance_writer: false
122
131
 
@@ -126,12 +135,13 @@ module ActiveRecord
126
135
  class_attribute :internal_metadata_table_name, instance_accessor: false, default: "ar_internal_metadata"
127
136
  class_attribute :pluralize_table_names, instance_writer: false, default: true
128
137
  class_attribute :implicit_order_column, instance_accessor: false
138
+ class_attribute :immutable_strings_by_default, instance_accessor: false
129
139
 
130
140
  self.protected_environments = ["production"]
131
141
  self.inheritance_column = "type"
132
142
  self.ignored_columns = [].freeze
133
143
 
134
- delegate :type_for_attribute, to: :class
144
+ delegate :type_for_attribute, :column_for_attribute, to: :class
135
145
 
136
146
  initialize_load_schema_monitor
137
147
  end
@@ -287,8 +297,38 @@ module ActiveRecord
287
297
 
288
298
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
289
299
  # accessors defined, and won't be referenced in SQL queries.
300
+ #
301
+ # A common usage pattern for this method is to ensure all references to an attribute
302
+ # have been removed and deployed, before a migration to drop the column from the database
303
+ # has been deployed and run. Using this two step approach to dropping columns ensures there
304
+ # is no code that raises errors due to having a cached schema in memory at the time the
305
+ # schema migration is run.
306
+ #
307
+ # For example, given a model where you want to drop the "category" attribute, first mark it
308
+ # as ignored:
309
+ #
310
+ # class Project < ActiveRecord::Base
311
+ # # schema:
312
+ # # id :bigint
313
+ # # name :string, limit: 255
314
+ # # category :string, limit: 255
315
+ #
316
+ # self.ignored_columns = [:category]
317
+ # end
318
+ #
319
+ # The schema still contains "category", but now the model omits it, so any meta-driven code or
320
+ # schema caching will not attempt to use the column:
321
+ #
322
+ # Project.columns_hash["category"] => nil
323
+ #
324
+ # You will get an error if accessing that attribute directly, so ensure all usages of the
325
+ # column are removed (automated tests can help you find any usages).
326
+ #
327
+ # user = Project.create!(name: "First Project")
328
+ # user.category # => raises NoMethodError
290
329
  def ignored_columns=(columns)
291
- @ignored_columns = columns.map(&:to_s)
330
+ reload_schema_from_cache
331
+ @ignored_columns = columns.map(&:to_s).freeze
292
332
  end
293
333
 
294
334
  def sequence_name
@@ -355,7 +395,7 @@ module ActiveRecord
355
395
 
356
396
  def columns
357
397
  load_schema
358
- @columns ||= columns_hash.values
398
+ @columns ||= columns_hash.values.freeze
359
399
  end
360
400
 
361
401
  def attribute_types # :nodoc:
@@ -380,6 +420,8 @@ module ActiveRecord
380
420
  # a string or a symbol.
381
421
  def type_for_attribute(attr_name, &block)
382
422
  attr_name = attr_name.to_s
423
+ attr_name = attribute_aliases[attr_name] || attr_name
424
+
383
425
  if block
384
426
  attribute_types.fetch(attr_name, &block)
385
427
  else
@@ -387,11 +429,31 @@ module ActiveRecord
387
429
  end
388
430
  end
389
431
 
432
+ # Returns the column object for the named attribute.
433
+ # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
434
+ # named attribute does not exist.
435
+ #
436
+ # class Person < ActiveRecord::Base
437
+ # end
438
+ #
439
+ # person = Person.new
440
+ # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
441
+ # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
442
+ #
443
+ # person.column_for_attribute(:nothing)
444
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
445
+ def column_for_attribute(name)
446
+ name = name.to_s
447
+ columns_hash.fetch(name) do
448
+ ConnectionAdapters::NullColumn.new(name)
449
+ end
450
+ end
451
+
390
452
  # Returns a hash where the keys are column names and the values are
391
453
  # default values when instantiating the Active Record object for this table.
392
454
  def column_defaults
393
455
  load_schema
394
- @column_defaults ||= _default_attributes.deep_dup.to_hash
456
+ @column_defaults ||= _default_attributes.deep_dup.to_hash.freeze
395
457
  end
396
458
 
397
459
  def _default_attributes # :nodoc:
@@ -401,7 +463,7 @@ module ActiveRecord
401
463
 
402
464
  # Returns an array of column names as strings.
403
465
  def column_names
404
- @column_names ||= columns.map(&:name)
466
+ @column_names ||= columns.map(&:name).freeze
405
467
  end
406
468
 
407
469
  def symbol_column_to_string(name_symbol) # :nodoc:
@@ -415,9 +477,8 @@ module ActiveRecord
415
477
  @content_columns ||= columns.reject do |c|
416
478
  c.name == primary_key ||
417
479
  c.name == inheritance_column ||
418
- c.name.end_with?("_id") ||
419
- c.name.end_with?("_count")
420
- end
480
+ c.name.end_with?("_id", "_count")
481
+ end.freeze
421
482
  end
422
483
 
423
484
  # Resets all the cached information about columns, which will cause them
@@ -427,7 +488,7 @@ module ActiveRecord
427
488
  # when just after creating a table you want to populate it with some default
428
489
  # values, eg:
429
490
  #
430
- # class CreateJobLevels < ActiveRecord::Migration[5.0]
491
+ # class CreateJobLevels < ActiveRecord::Migration[6.0]
431
492
  # def up
432
493
  # create_table :job_levels do |t|
433
494
  # t.integer :id
@@ -456,13 +517,11 @@ module ActiveRecord
456
517
  end
457
518
 
458
519
  protected
459
-
460
520
  def initialize_load_schema_monitor
461
521
  @load_schema_monitor = Monitor.new
462
522
  end
463
523
 
464
524
  private
465
-
466
525
  def inherited(child_class)
467
526
  super
468
527
  child_class.initialize_load_schema_monitor
@@ -480,15 +539,27 @@ module ActiveRecord
480
539
  load_schema!
481
540
 
482
541
  @schema_loaded = true
542
+ rescue
543
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
544
+ raise
483
545
  end
484
546
  end
485
547
 
486
548
  def load_schema!
487
- @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
549
+ unless table_name
550
+ raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
551
+ end
552
+
553
+ columns_hash = connection.schema_cache.columns_hash(table_name)
554
+ columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
555
+ @columns_hash = columns_hash.freeze
488
556
  @columns_hash.each do |name, column|
557
+ type = connection.lookup_cast_type_from_column(column)
558
+ type = _convert_type_from_options(type)
559
+ warn_if_deprecated_type(column)
489
560
  define_attribute(
490
561
  name,
491
- connection.lookup_cast_type_from_column(column),
562
+ type,
492
563
  default: column.default,
493
564
  user_provided_default: false
494
565
  )
@@ -537,6 +608,40 @@ module ActiveRecord
537
608
  base_class.table_name
538
609
  end
539
610
  end
611
+
612
+ def _convert_type_from_options(type)
613
+ if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
614
+ type.to_immutable_string
615
+ else
616
+ type
617
+ end
618
+ end
619
+
620
+ def warn_if_deprecated_type(column)
621
+ return if attributes_to_define_after_schema_loads.key?(column.name)
622
+ return unless column.respond_to?(:oid)
623
+
624
+ if column.array?
625
+ array_arguments = ", array: true"
626
+ else
627
+ array_arguments = ""
628
+ end
629
+
630
+ if column.sql_type.start_with?("interval")
631
+ precision_arguments = column.precision.presence && ", precision: #{column.precision}"
632
+ ActiveSupport::Deprecation.warn(<<~WARNING)
633
+ The behavior of the `:interval` type will be changing in Rails 6.2
634
+ to return an `ActiveSupport::Duration` object. If you'd like to keep
635
+ the old behavior, you can add this line to #{self.name} model:
636
+
637
+ attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
638
+
639
+ If you'd like the new behavior today, you can add this line:
640
+
641
+ attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
642
+ WARNING
643
+ end
644
+ end
540
645
  end
541
646
  end
542
647
  end