activerecord 5.2.4.3 → 6.0.0

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +614 -588
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +19 -14
  9. data/lib/active_record/associations/association.rb +52 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/association.rb +14 -18
  14. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  15. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  17. data/lib/active_record/associations/builder/has_many.rb +2 -0
  18. data/lib/active_record/associations/builder/has_one.rb +35 -1
  19. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  20. data/lib/active_record/associations/collection_association.rb +6 -21
  21. data/lib/active_record/associations/collection_proxy.rb +12 -15
  22. data/lib/active_record/associations/foreign_association.rb +7 -0
  23. data/lib/active_record/associations/has_many_association.rb +2 -10
  24. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  25. data/lib/active_record/associations/has_one_association.rb +28 -30
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/preloader/association.rb +38 -36
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/singular_association.rb +2 -16
  34. data/lib/active_record/attribute_assignment.rb +7 -10
  35. data/lib/active_record/attribute_methods.rb +28 -100
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  37. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  38. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  39. data/lib/active_record/attribute_methods/query.rb +2 -3
  40. data/lib/active_record/attribute_methods/read.rb +15 -53
  41. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  43. data/lib/active_record/attribute_methods/write.rb +17 -24
  44. data/lib/active_record/attributes.rb +13 -0
  45. data/lib/active_record/autosave_association.rb +5 -9
  46. data/lib/active_record/base.rb +2 -3
  47. data/lib/active_record/callbacks.rb +5 -19
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  60. data/lib/active_record/connection_adapters/column.rb +17 -13
  61. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  62. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  69. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  70. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  75. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  83. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  86. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  87. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  88. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  89. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  90. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  91. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  94. data/lib/active_record/connection_handling.rb +149 -27
  95. data/lib/active_record/core.rb +100 -60
  96. data/lib/active_record/counter_cache.rb +4 -29
  97. data/lib/active_record/database_configurations.rb +233 -0
  98. data/lib/active_record/database_configurations/database_config.rb +37 -0
  99. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  100. data/lib/active_record/database_configurations/url_config.rb +79 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/enum.rb +37 -7
  103. data/lib/active_record/errors.rb +15 -7
  104. data/lib/active_record/explain.rb +1 -1
  105. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  106. data/lib/active_record/fixture_set/render_context.rb +17 -0
  107. data/lib/active_record/fixture_set/table_row.rb +153 -0
  108. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  109. data/lib/active_record/fixtures.rb +145 -472
  110. data/lib/active_record/gem_version.rb +4 -4
  111. data/lib/active_record/inheritance.rb +13 -3
  112. data/lib/active_record/insert_all.rb +179 -0
  113. data/lib/active_record/integration.rb +68 -16
  114. data/lib/active_record/internal_metadata.rb +10 -2
  115. data/lib/active_record/locking/optimistic.rb +5 -6
  116. data/lib/active_record/locking/pessimistic.rb +3 -3
  117. data/lib/active_record/log_subscriber.rb +7 -26
  118. data/lib/active_record/middleware/database_selector.rb +75 -0
  119. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  120. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  121. data/lib/active_record/migration.rb +100 -81
  122. data/lib/active_record/migration/command_recorder.rb +50 -6
  123. data/lib/active_record/migration/compatibility.rb +76 -49
  124. data/lib/active_record/model_schema.rb +30 -9
  125. data/lib/active_record/nested_attributes.rb +2 -2
  126. data/lib/active_record/no_touching.rb +7 -0
  127. data/lib/active_record/persistence.rb +228 -24
  128. data/lib/active_record/query_cache.rb +11 -4
  129. data/lib/active_record/querying.rb +32 -20
  130. data/lib/active_record/railtie.rb +80 -43
  131. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  132. data/lib/active_record/railties/controller_runtime.rb +30 -35
  133. data/lib/active_record/railties/databases.rake +196 -46
  134. data/lib/active_record/reflection.rb +32 -30
  135. data/lib/active_record/relation.rb +310 -80
  136. data/lib/active_record/relation/batches.rb +13 -10
  137. data/lib/active_record/relation/calculations.rb +53 -47
  138. data/lib/active_record/relation/delegation.rb +26 -43
  139. data/lib/active_record/relation/finder_methods.rb +13 -26
  140. data/lib/active_record/relation/merger.rb +11 -20
  141. data/lib/active_record/relation/predicate_builder.rb +4 -6
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  147. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  148. data/lib/active_record/relation/query_attribute.rb +13 -8
  149. data/lib/active_record/relation/query_methods.rb +189 -63
  150. data/lib/active_record/relation/spawn_methods.rb +1 -1
  151. data/lib/active_record/relation/where_clause.rb +14 -10
  152. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  153. data/lib/active_record/result.rb +30 -11
  154. data/lib/active_record/sanitization.rb +32 -40
  155. data/lib/active_record/schema.rb +2 -11
  156. data/lib/active_record/schema_dumper.rb +22 -7
  157. data/lib/active_record/schema_migration.rb +5 -1
  158. data/lib/active_record/scoping.rb +8 -8
  159. data/lib/active_record/scoping/default.rb +4 -5
  160. data/lib/active_record/scoping/named.rb +19 -15
  161. data/lib/active_record/statement_cache.rb +30 -3
  162. data/lib/active_record/store.rb +87 -8
  163. data/lib/active_record/table_metadata.rb +10 -17
  164. data/lib/active_record/tasks/database_tasks.rb +194 -25
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  168. data/lib/active_record/test_databases.rb +23 -0
  169. data/lib/active_record/test_fixtures.rb +224 -0
  170. data/lib/active_record/timestamp.rb +39 -25
  171. data/lib/active_record/touch_later.rb +4 -2
  172. data/lib/active_record/transactions.rb +57 -66
  173. data/lib/active_record/translation.rb +1 -1
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  176. data/lib/active_record/type_caster/connection.rb +15 -14
  177. data/lib/active_record/type_caster/map.rb +1 -4
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record/validations/uniqueness.rb +15 -27
  180. data/lib/arel.rb +51 -0
  181. data/lib/arel/alias_predication.rb +9 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/attributes/attribute.rb +37 -0
  184. data/lib/arel/collectors/bind.rb +24 -0
  185. data/lib/arel/collectors/composite.rb +31 -0
  186. data/lib/arel/collectors/plain_string.rb +20 -0
  187. data/lib/arel/collectors/sql_string.rb +20 -0
  188. data/lib/arel/collectors/substitute_binds.rb +28 -0
  189. data/lib/arel/crud.rb +42 -0
  190. data/lib/arel/delete_manager.rb +18 -0
  191. data/lib/arel/errors.rb +9 -0
  192. data/lib/arel/expressions.rb +29 -0
  193. data/lib/arel/factory_methods.rb +49 -0
  194. data/lib/arel/insert_manager.rb +49 -0
  195. data/lib/arel/math.rb +45 -0
  196. data/lib/arel/nodes.rb +68 -0
  197. data/lib/arel/nodes/and.rb +32 -0
  198. data/lib/arel/nodes/ascending.rb +23 -0
  199. data/lib/arel/nodes/binary.rb +52 -0
  200. data/lib/arel/nodes/bind_param.rb +36 -0
  201. data/lib/arel/nodes/case.rb +55 -0
  202. data/lib/arel/nodes/casted.rb +50 -0
  203. data/lib/arel/nodes/comment.rb +29 -0
  204. data/lib/arel/nodes/count.rb +12 -0
  205. data/lib/arel/nodes/delete_statement.rb +45 -0
  206. data/lib/arel/nodes/descending.rb +23 -0
  207. data/lib/arel/nodes/equality.rb +18 -0
  208. data/lib/arel/nodes/extract.rb +24 -0
  209. data/lib/arel/nodes/false.rb +16 -0
  210. data/lib/arel/nodes/full_outer_join.rb +8 -0
  211. data/lib/arel/nodes/function.rb +44 -0
  212. data/lib/arel/nodes/grouping.rb +8 -0
  213. data/lib/arel/nodes/in.rb +8 -0
  214. data/lib/arel/nodes/infix_operation.rb +80 -0
  215. data/lib/arel/nodes/inner_join.rb +8 -0
  216. data/lib/arel/nodes/insert_statement.rb +37 -0
  217. data/lib/arel/nodes/join_source.rb +20 -0
  218. data/lib/arel/nodes/matches.rb +18 -0
  219. data/lib/arel/nodes/named_function.rb +23 -0
  220. data/lib/arel/nodes/node.rb +50 -0
  221. data/lib/arel/nodes/node_expression.rb +13 -0
  222. data/lib/arel/nodes/outer_join.rb +8 -0
  223. data/lib/arel/nodes/over.rb +15 -0
  224. data/lib/arel/nodes/regexp.rb +16 -0
  225. data/lib/arel/nodes/right_outer_join.rb +8 -0
  226. data/lib/arel/nodes/select_core.rb +67 -0
  227. data/lib/arel/nodes/select_statement.rb +41 -0
  228. data/lib/arel/nodes/sql_literal.rb +16 -0
  229. data/lib/arel/nodes/string_join.rb +11 -0
  230. data/lib/arel/nodes/table_alias.rb +27 -0
  231. data/lib/arel/nodes/terminal.rb +16 -0
  232. data/lib/arel/nodes/true.rb +16 -0
  233. data/lib/arel/nodes/unary.rb +45 -0
  234. data/lib/arel/nodes/unary_operation.rb +20 -0
  235. data/lib/arel/nodes/unqualified_column.rb +22 -0
  236. data/lib/arel/nodes/update_statement.rb +41 -0
  237. data/lib/arel/nodes/values_list.rb +9 -0
  238. data/lib/arel/nodes/window.rb +126 -0
  239. data/lib/arel/nodes/with.rb +11 -0
  240. data/lib/arel/order_predications.rb +13 -0
  241. data/lib/arel/predications.rb +257 -0
  242. data/lib/arel/select_manager.rb +271 -0
  243. data/lib/arel/table.rb +110 -0
  244. data/lib/arel/tree_manager.rb +72 -0
  245. data/lib/arel/update_manager.rb +34 -0
  246. data/lib/arel/visitors.rb +20 -0
  247. data/lib/arel/visitors/depth_first.rb +204 -0
  248. data/lib/arel/visitors/dot.rb +297 -0
  249. data/lib/arel/visitors/ibm_db.rb +34 -0
  250. data/lib/arel/visitors/informix.rb +62 -0
  251. data/lib/arel/visitors/mssql.rb +157 -0
  252. data/lib/arel/visitors/mysql.rb +83 -0
  253. data/lib/arel/visitors/oracle.rb +159 -0
  254. data/lib/arel/visitors/oracle12.rb +66 -0
  255. data/lib/arel/visitors/postgresql.rb +110 -0
  256. data/lib/arel/visitors/sqlite.rb +39 -0
  257. data/lib/arel/visitors/to_sql.rb +889 -0
  258. data/lib/arel/visitors/visitor.rb +46 -0
  259. data/lib/arel/visitors/where_sql.rb +23 -0
  260. data/lib/arel/window_predications.rb +9 -0
  261. data/lib/rails/generators/active_record/migration.rb +14 -1
  262. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  263. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  264. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +108 -26
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/strip"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters
7
5
  class AbstractAdapter
@@ -17,14 +15,13 @@ module ActiveRecord
17
15
  end
18
16
 
19
17
  delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
20
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn
21
- private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
22
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options
18
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
19
+ to: :@conn, private: true
23
20
 
24
21
  private
25
22
 
26
23
  def visit_AlterTable(o)
27
- sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
24
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
28
25
  sql << o.adds.map { |col| accept col }.join(" ")
29
26
  sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
30
27
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
@@ -32,17 +29,19 @@ module ActiveRecord
32
29
 
33
30
  def visit_ColumnDefinition(o)
34
31
  o.sql_type = type_to_sql(o.type, o.options)
35
- column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
32
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
36
33
  add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
37
34
  column_sql
38
35
  end
39
36
 
40
37
  def visit_AddColumnDefinition(o)
41
- "ADD #{accept(o.column)}".dup
38
+ +"ADD #{accept(o.column)}"
42
39
  end
43
40
 
44
41
  def visit_TableDefinition(o)
45
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
42
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
43
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
44
+ create_sql << "#{quote_table_name(o.name)} "
46
45
 
47
46
  statements = o.columns.map { |c| accept c }
48
47
  statements << accept(o.primary_keys) if o.primary_keys
@@ -51,7 +50,7 @@ module ActiveRecord
51
50
  statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
52
51
  end
53
52
 
54
- if supports_foreign_keys_in_create?
53
+ if supports_foreign_keys?
55
54
  statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
56
55
  end
57
56
 
@@ -66,7 +65,7 @@ module ActiveRecord
66
65
  end
67
66
 
68
67
  def visit_ForeignKeyDefinition(o)
69
- sql = <<-SQL.strip_heredoc
68
+ sql = +<<~SQL
70
69
  CONSTRAINT #{quote_column_name(o.name)}
71
70
  FOREIGN KEY (#{quote_column_name(o.column)})
72
71
  REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
@@ -122,7 +121,15 @@ module ActiveRecord
122
121
  sql
123
122
  end
124
123
 
124
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
125
+ def table_modifier_in_create(o)
126
+ " TEMPORARY" if o.temporary
127
+ end
128
+
125
129
  def foreign_key_in_create(from_table, to_table, options)
130
+ prefix = ActiveRecord::Base.table_name_prefix
131
+ suffix = ActiveRecord::Base.table_name_suffix
132
+ to_table = "#{prefix}#{to_table}#{suffix}"
126
133
  options = foreign_key_options(from_table, to_table, options)
127
134
  accept ForeignKeyDefinition.new(from_table, to_table, options)
128
135
  end
@@ -133,7 +140,7 @@ module ActiveRecord
133
140
  when :cascade then "ON #{action} CASCADE"
134
141
  when :restrict then "ON #{action} RESTRICT"
135
142
  else
136
- raise ArgumentError, <<-MSG.strip_heredoc
143
+ raise ArgumentError, <<~MSG
137
144
  '#{dependency}' is not supported for :on_update or :on_delete.
138
145
  Supported values are: :nullify, :cascade, :restrict
139
146
  MSG
@@ -101,13 +101,13 @@ module ActiveRecord
101
101
  end
102
102
  alias validated? validate?
103
103
 
104
- def defined_for?(to_table_ord = nil, to_table: nil, **options)
105
- if to_table_ord
106
- self.to_table == to_table_ord.to_s
107
- else
108
- (to_table.nil? || to_table.to_s == self.to_table) &&
109
- options.all? { |k, v| self.options[k].to_s == v.to_s }
110
- end
104
+ def export_name_on_schema_dump?
105
+ !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
106
+ end
107
+
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 }
111
111
  end
112
112
 
113
113
  private
@@ -151,13 +151,8 @@ module ActiveRecord
151
151
  end
152
152
  end
153
153
 
154
- # TODO Change this to private once we've dropped Ruby 2.2 support.
155
- # Workaround for Ruby 2.2 "private attribute?" warning.
156
- protected
157
-
158
- attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
159
-
160
154
  private
155
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
161
156
 
162
157
  def as_options(value)
163
158
  value.is_a?(Hash) ? value : {}
@@ -199,41 +194,44 @@ module ActiveRecord
199
194
  end
200
195
 
201
196
  module ColumnMethods
197
+ extend ActiveSupport::Concern
198
+
202
199
  # Appends a primary key definition to the table definition.
203
200
  # Can be called multiple times, but this is probably not a good idea.
204
201
  def primary_key(name, type = :primary_key, **options)
205
202
  column(name, type, options.merge(primary_key: true))
206
203
  end
207
204
 
205
+ ##
206
+ # :method: column
207
+ # :call-seq: column(name, type, **options)
208
+ #
208
209
  # Appends a column or columns of a specified type.
209
210
  #
210
211
  # t.string(:goat)
211
212
  # t.string(:goat, :sheep)
212
213
  #
213
214
  # See TableDefinition#column
214
- [
215
- :bigint,
216
- :binary,
217
- :boolean,
218
- :date,
219
- :datetime,
220
- :decimal,
221
- :float,
222
- :integer,
223
- :json,
224
- :string,
225
- :text,
226
- :time,
227
- :timestamp,
228
- :virtual,
229
- ].each do |column_type|
230
- module_eval <<-CODE, __FILE__, __LINE__ + 1
231
- def #{column_type}(*args, **options)
232
- args.each { |name| column(name, :#{column_type}, options) }
215
+
216
+ included do
217
+ define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
218
+ :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
219
+
220
+ alias :numeric :decimal
221
+ end
222
+
223
+ class_methods do
224
+ private def define_column_methods(*column_types) # :nodoc:
225
+ column_types.each do |column_type|
226
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
227
+ def #{column_type}(*names, **options)
228
+ raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
229
+ names.each { |name| column(name, :#{column_type}, options) }
230
+ end
231
+ RUBY
233
232
  end
234
- CODE
233
+ end
235
234
  end
236
- alias_method :numeric, :decimal
237
235
  end
238
236
 
239
237
  # Represents the schema of an SQL table in an abstract way. This class
@@ -257,15 +255,25 @@ module ActiveRecord
257
255
  class TableDefinition
258
256
  include ColumnMethods
259
257
 
260
- attr_accessor :indexes
261
- attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
258
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
262
259
 
263
- def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
260
+ def initialize(
261
+ conn,
262
+ name,
263
+ temporary: false,
264
+ if_not_exists: false,
265
+ options: nil,
266
+ as: nil,
267
+ comment: nil,
268
+ **
269
+ )
270
+ @conn = conn
264
271
  @columns_hash = {}
265
272
  @indexes = []
266
273
  @foreign_keys = []
267
274
  @primary_keys = nil
268
275
  @temporary = temporary
276
+ @if_not_exists = if_not_exists
269
277
  @options = options
270
278
  @as = as
271
279
  @name = name
@@ -349,16 +357,20 @@ module ActiveRecord
349
357
  #
350
358
  # create_table :taggings do |t|
351
359
  # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
352
- # t.references :tagger, polymorphic: true, index: true
353
- # t.references :taggable, polymorphic: { default: 'Photo' }
360
+ # t.references :tagger, polymorphic: true
361
+ # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
354
362
  # end
355
- def column(name, type, options = {})
363
+ def column(name, type, **options)
356
364
  name = name.to_s
357
365
  type = type.to_sym if type
358
366
  options = options.dup
359
367
 
360
- if @columns_hash[name] && @columns_hash[name].primary_key?
361
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
368
+ if @columns_hash[name]
369
+ if @columns_hash[name].primary_key?
370
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
371
+ else
372
+ raise ArgumentError, "you can't define an already defined column '#{name}'."
373
+ end
362
374
  end
363
375
 
364
376
  index_options = options.delete(:index)
@@ -382,10 +394,7 @@ module ActiveRecord
382
394
  end
383
395
 
384
396
  def foreign_key(table_name, options = {}) # :nodoc:
385
- table_name_prefix = ActiveRecord::Base.table_name_prefix
386
- table_name_suffix = ActiveRecord::Base.table_name_suffix
387
- table_name = "#{table_name_prefix}#{table_name}#{table_name_suffix}"
388
- foreign_keys.push([table_name, options])
397
+ foreign_keys << [table_name, options]
389
398
  end
390
399
 
391
400
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -395,6 +404,10 @@ module ActiveRecord
395
404
  def timestamps(**options)
396
405
  options[:null] = false if options[:null].nil?
397
406
 
407
+ if !options.key?(:precision) && @conn.supports_datetime_with_precision?
408
+ options[:precision] = 6
409
+ end
410
+
398
411
  column(:created_at, :datetime, options)
399
412
  column(:updated_at, :datetime, options)
400
413
  end
@@ -403,6 +416,7 @@ module ActiveRecord
403
416
  #
404
417
  # t.references(:user)
405
418
  # t.belongs_to(:supplier, foreign_key: true)
419
+ # t.belongs_to(:supplier, foreign_key: true, type: :integer)
406
420
  #
407
421
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
408
422
  def references(*args, **options)
@@ -502,6 +516,7 @@ module ActiveRecord
502
516
  # t.json
503
517
  # t.virtual
504
518
  # t.remove
519
+ # t.remove_foreign_key
505
520
  # t.remove_references
506
521
  # t.remove_belongs_to
507
522
  # t.remove_index
@@ -523,8 +538,10 @@ module ActiveRecord
523
538
  # t.column(:name, :string)
524
539
  #
525
540
  # See TableDefinition#column for details of the options you can use.
526
- def column(column_name, type, options = {})
541
+ def column(column_name, type, **options)
542
+ index_options = options.delete(:index)
527
543
  @base.add_column(name, column_name, type, options)
544
+ index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
528
545
  end
529
546
 
530
547
  # Checks to see if a column exists.
@@ -663,15 +680,26 @@ module ActiveRecord
663
680
  end
664
681
  alias :remove_belongs_to :remove_references
665
682
 
666
- # Adds a foreign key.
683
+ # Adds a foreign key to the table using a supplied table name.
667
684
  #
668
685
  # t.foreign_key(:authors)
686
+ # t.foreign_key(:authors, column: :author_id, primary_key: "id")
669
687
  #
670
688
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
671
689
  def foreign_key(*args)
672
690
  @base.add_foreign_key(name, *args)
673
691
  end
674
692
 
693
+ # Removes the given foreign key from the table.
694
+ #
695
+ # t.remove_foreign_key(:authors)
696
+ # t.remove_foreign_key(column: :author_id)
697
+ #
698
+ # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
699
+ def remove_foreign_key(*args)
700
+ @base.remove_foreign_key(name, *args)
701
+ end
702
+
675
703
  # Checks to see if a foreign key exists.
676
704
  #
677
705
  # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/compact"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters # :nodoc:
7
5
  class SchemaDumper < SchemaDumper # :nodoc:
@@ -17,7 +15,7 @@ module ActiveRecord
17
15
  def column_spec_for_primary_key(column)
18
16
  return {} if default_primary_key?(column)
19
17
  spec = { id: schema_type(column).inspect }
20
- spec.merge!(prepare_column_options(column).except!(:null))
18
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
21
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
22
20
  spec
23
21
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_record/migration/join_table"
4
4
  require "active_support/core_ext/string/access"
5
+ require "active_support/deprecation"
5
6
  require "digest/sha2"
6
7
 
7
8
  module ActiveRecord
@@ -129,11 +130,11 @@ module ActiveRecord
129
130
  # column_exists?(:suppliers, :name, :string, null: false)
130
131
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
131
132
  #
132
- def column_exists?(table_name, column_name, type = nil, options = {})
133
+ def column_exists?(table_name, column_name, type = nil, **options)
133
134
  column_name = column_name.to_s
134
135
  checks = []
135
136
  checks << lambda { |c| c.name == column_name }
136
- checks << lambda { |c| c.type == type } if type
137
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
137
138
  column_options_keys.each do |attr|
138
139
  checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
139
140
  end
@@ -205,19 +206,22 @@ module ActiveRecord
205
206
  # Set to true to drop the table before creating it.
206
207
  # Set to +:cascade+ to drop dependent objects as well.
207
208
  # Defaults to false.
209
+ # [<tt>:if_not_exists</tt>]
210
+ # Set to true to avoid raising an error when the table already exists.
211
+ # Defaults to false.
208
212
  # [<tt>:as</tt>]
209
213
  # SQL to use to generate the table. When this option is used, the block is
210
214
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
211
215
  #
212
216
  # ====== Add a backend specific option to the generated SQL (MySQL)
213
217
  #
214
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
215
219
  #
216
220
  # generates:
217
221
  #
218
222
  # CREATE TABLE suppliers (
219
223
  # id bigint auto_increment PRIMARY KEY
220
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
221
225
  #
222
226
  # ====== Rename the primary key column
223
227
  #
@@ -287,8 +291,8 @@ module ActiveRecord
287
291
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
288
292
  #
289
293
  # See also TableDefinition#column for details on how to create columns.
290
- def create_table(table_name, comment: nil, **options)
291
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
294
+ def create_table(table_name, **options)
295
+ td = create_table_definition(table_name, options)
292
296
 
293
297
  if options[:id] != false && !options[:as]
294
298
  pk = options.fetch(:primary_key) do
@@ -298,7 +302,7 @@ module ActiveRecord
298
302
  if pk.is_a?(Array)
299
303
  td.primary_keys pk
300
304
  else
301
- td.primary_key pk, options.fetch(:id, :primary_key), options
305
+ td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
302
306
  end
303
307
  end
304
308
 
@@ -317,7 +321,9 @@ module ActiveRecord
317
321
  end
318
322
 
319
323
  if supports_comments? && !supports_comments_in_create?
320
- change_table_comment(table_name, comment) if comment.present?
324
+ if table_comment = options[:comment].presence
325
+ change_table_comment(table_name, table_comment)
326
+ end
321
327
 
322
328
  td.columns.each do |column|
323
329
  change_column_comment(table_name, column.name, column.comment) if column.comment.present?
@@ -512,16 +518,20 @@ module ActiveRecord
512
518
  # Available options are (none of these exists by default):
513
519
  # * <tt>:limit</tt> -
514
520
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
515
- # 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.
516
522
  # This option is ignored by some backends.
517
523
  # * <tt>:default</tt> -
518
524
  # The column's default value. Use +nil+ for +NULL+.
519
525
  # * <tt>:null</tt> -
520
526
  # Allows or disallows +NULL+ values in the column.
521
527
  # * <tt>:precision</tt> -
522
- # 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.
523
530
  # * <tt>:scale</tt> -
524
531
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
532
+ # * <tt>:collation</tt> -
533
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
534
+ # column will have the same collation as the table.
525
535
  # * <tt>:comment</tt> -
526
536
  # Specifies the comment for the column. This option is ignored by some backends.
527
537
  #
@@ -575,7 +585,7 @@ module ActiveRecord
575
585
  # # Defines a column with a database-specific type.
576
586
  # add_column(:shapes, :triangle, 'polygon')
577
587
  # # ALTER TABLE "shapes" ADD "triangle" polygon
578
- def add_column(table_name, column_name, type, options = {})
588
+ def add_column(table_name, column_name, type, **options)
579
589
  at = create_alter_table table_name
580
590
  at.add_column(column_name, type, options)
581
591
  execute schema_creation.accept at
@@ -599,6 +609,7 @@ module ActiveRecord
599
609
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
600
610
  # to provide these in a migration's +change+ method so it can be reverted.
601
611
  # In that case, +type+ and +options+ will be used by #add_column.
612
+ # Indexes on the column are automatically removed.
602
613
  def remove_column(table_name, column_name, type = nil, options = {})
603
614
  execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
604
615
  end
@@ -760,6 +771,17 @@ module ActiveRecord
760
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
761
772
  #
762
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].
763
785
  def add_index(table_name, column_name, options = {})
764
786
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
765
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -783,6 +805,15 @@ module ActiveRecord
783
805
  #
784
806
  # remove_index :accounts, name: :by_branch_party
785
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].
786
817
  def remove_index(table_name, options = {})
787
818
  index_name = index_name_for_remove(table_name, options)
788
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
@@ -842,17 +873,17 @@ module ActiveRecord
842
873
  # [<tt>:null</tt>]
843
874
  # Whether the column allows nulls. Defaults to true.
844
875
  #
845
- # ====== Create a user_id bigint column
876
+ # ====== Create a user_id bigint column without an index
846
877
  #
847
- # add_reference(:products, :user)
878
+ # add_reference(:products, :user, index: false)
848
879
  #
849
880
  # ====== Create a user_id string column
850
881
  #
851
882
  # add_reference(:products, :user, type: :string)
852
883
  #
853
- # ====== Create supplier_id, supplier_type columns and appropriate index
884
+ # ====== Create supplier_id, supplier_type columns
854
885
  #
855
- # add_reference(:products, :supplier, polymorphic: true, index: true)
886
+ # add_reference(:products, :supplier, polymorphic: true)
856
887
  #
857
888
  # ====== Create a supplier_id column with a unique index
858
889
  #
@@ -880,7 +911,7 @@ module ActiveRecord
880
911
  #
881
912
  # ====== Remove the reference
882
913
  #
883
- # remove_reference(:products, :user, index: true)
914
+ # remove_reference(:products, :user, index: false)
884
915
  #
885
916
  # ====== Remove polymorphic reference
886
917
  #
@@ -888,7 +919,7 @@ module ActiveRecord
888
919
  #
889
920
  # ====== Remove the reference with a foreign key
890
921
  #
891
- # remove_reference(:products, :user, index: true, foreign_key: true)
922
+ # remove_reference(:products, :user, foreign_key: true)
892
923
  #
893
924
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
894
925
  if foreign_key
@@ -956,7 +987,7 @@ module ActiveRecord
956
987
  # [<tt>:on_update</tt>]
957
988
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
958
989
  # [<tt>:validate</tt>]
959
- # (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+.
960
991
  def add_foreign_key(from_table, to_table, options = {})
961
992
  return unless supports_foreign_keys?
962
993
 
@@ -980,15 +1011,22 @@ module ActiveRecord
980
1011
  #
981
1012
  # remove_foreign_key :accounts, column: :owner_id
982
1013
  #
1014
+ # Removes the foreign key on +accounts.owner_id+.
1015
+ #
1016
+ # remove_foreign_key :accounts, to_table: :owners
1017
+ #
983
1018
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
984
1019
  #
985
1020
  # remove_foreign_key :accounts, name: :special_fk_name
986
1021
  #
987
- # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
988
- def remove_foreign_key(from_table, options_or_to_table = {})
1022
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1023
+ # with an addition of
1024
+ # [<tt>:to_table</tt>]
1025
+ # The name of the table that contains the referenced primary key.
1026
+ def remove_foreign_key(from_table, to_table = nil, **options)
989
1027
  return unless supports_foreign_keys?
990
1028
 
991
- 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
992
1030
 
993
1031
  at = create_alter_table from_table
994
1032
  at.drop_foreign_key fk_name_to_delete
@@ -1007,14 +1045,12 @@ module ActiveRecord
1007
1045
  # # Checks to see if a foreign key with a custom name exists.
1008
1046
  # foreign_key_exists?(:accounts, name: "special_fk_name")
1009
1047
  #
1010
- def foreign_key_exists?(from_table, options_or_to_table = {})
1011
- 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?
1012
1050
  end
1013
1051
 
1014
1052
  def foreign_key_column_for(table_name) # :nodoc:
1015
- prefix = Base.table_name_prefix
1016
- suffix = Base.table_name_suffix
1017
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
1018
1054
  "#{name.singularize}_id"
1019
1055
  end
1020
1056
 
@@ -1025,8 +1061,8 @@ module ActiveRecord
1025
1061
  options
1026
1062
  end
1027
1063
 
1028
- def dump_schema_information #:nodoc:
1029
- versions = ActiveRecord::SchemaMigration.all_versions
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1030
1066
  insert_versions_sql(versions) if versions.any?
1031
1067
  end
1032
1068
 
@@ -1034,15 +1070,18 @@ module ActiveRecord
1034
1070
  { primary_key: true }
1035
1071
  end
1036
1072
 
1037
- def assume_migrated_upto_version(version, migrations_paths)
1038
- migrations_paths = Array(migrations_paths)
1073
+ def assume_migrated_upto_version(version, migrations_paths = nil)
1074
+ unless migrations_paths.nil?
1075
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
1076
+ Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1077
+ MSG
1078
+ end
1079
+
1039
1080
  version = version.to_i
1040
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
1041
1082
 
1042
- migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
1043
- versions = migration_context.migration_files.map do |file|
1044
- migration_context.parse_migration_filename(file).first.to_i
1045
- end
1083
+ migrated = migration_context.get_all_versions
1084
+ versions = migration_context.migrations.map(&:version)
1046
1085
 
1047
1086
  unless migrated.include?(version)
1048
1087
  execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
@@ -1053,13 +1092,7 @@ module ActiveRecord
1053
1092
  if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1054
1093
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1055
1094
  end
1056
- if supports_multi_insert?
1057
- execute insert_versions_sql(inserting)
1058
- else
1059
- inserting.each do |v|
1060
- execute insert_versions_sql(v)
1061
- end
1062
- end
1095
+ execute insert_versions_sql(inserting)
1063
1096
  end
1064
1097
  end
1065
1098
 
@@ -1085,7 +1118,7 @@ module ActiveRecord
1085
1118
  if (0..6) === precision
1086
1119
  column_type_sql << "(#{precision})"
1087
1120
  else
1088
- 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"
1089
1122
  end
1090
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1091
1124
  column_type_sql << "(#{limit})"
@@ -1115,6 +1148,10 @@ module ActiveRecord
1115
1148
  def add_timestamps(table_name, options = {})
1116
1149
  options[:null] = false if options[:null].nil?
1117
1150
 
1151
+ if !options.key?(:precision) && supports_datetime_with_precision?
1152
+ options[:precision] = 6
1153
+ end
1154
+
1118
1155
  add_column table_name, :created_at, :datetime, options
1119
1156
  add_column table_name, :updated_at, :datetime, options
1120
1157
  end
@@ -1169,12 +1206,22 @@ module ActiveRecord
1169
1206
  end
1170
1207
 
1171
1208
  # Changes the comment for a table or removes it if +nil+.
1172
- 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)
1173
1215
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1174
1216
  end
1175
1217
 
1176
1218
  # Changes the comment for a column or removes it if +nil+.
1177
- 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)
1178
1225
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1179
1226
  end
1180
1227
 
@@ -1276,7 +1323,7 @@ module ActiveRecord
1276
1323
  end
1277
1324
 
1278
1325
  def create_table_definition(*args)
1279
- TableDefinition.new(*args)
1326
+ TableDefinition.new(self, *args)
1280
1327
  end
1281
1328
 
1282
1329
  def create_alter_table(name)
@@ -1310,6 +1357,12 @@ module ActiveRecord
1310
1357
  { column: column_names }
1311
1358
  end
1312
1359
 
1360
+ def strip_table_name_prefix_and_suffix(table_name)
1361
+ prefix = Base.table_name_prefix
1362
+ suffix = Base.table_name_suffix
1363
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1364
+ end
1365
+
1313
1366
  def foreign_key_name(table_name, options)
1314
1367
  options.fetch(:name) do
1315
1368
  identifier = "#{table_name}_#{options.fetch(:column)}_fk"
@@ -1319,14 +1372,14 @@ module ActiveRecord
1319
1372
  end
1320
1373
  end
1321
1374
 
1322
- def foreign_key_for(from_table, options_or_to_table = {})
1375
+ def foreign_key_for(from_table, **options)
1323
1376
  return unless supports_foreign_keys?
1324
- foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
1377
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1325
1378
  end
1326
1379
 
1327
- def foreign_key_for!(from_table, options_or_to_table = {})
1328
- foreign_key_for(from_table, options_or_to_table) || \
1329
- 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}")
1330
1383
  end
1331
1384
 
1332
1385
  def extract_foreign_key_action(specifier)
@@ -1352,11 +1405,37 @@ module ActiveRecord
1352
1405
  default_or_changes
1353
1406
  end
1354
1407
  end
1408
+ alias :extract_new_comment_value :extract_new_default_value
1355
1409
 
1356
1410
  def can_remove_index_by_name?(options)
1357
1411
  options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1358
1412
  end
1359
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
+
1360
1439
  def add_column_for_alter(table_name, column_name, type, options = {})
1361
1440
  td = create_table_definition(table_name)
1362
1441
  cd = td.new_column_definition(column_name, type, options)
@@ -1372,10 +1451,10 @@ module ActiveRecord
1372
1451
  end
1373
1452
 
1374
1453
  def insert_versions_sql(versions)
1375
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1454
+ sm_table = quote_table_name(schema_migration.table_name)
1376
1455
 
1377
1456
  if versions.is_a?(Array)
1378
- sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
1457
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1379
1458
  sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1380
1459
  sql << ";\n\n"
1381
1460
  sql