activerecord 4.2.11.1 → 5.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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -10,12 +10,12 @@ module ActiveRecord
10
10
  # Indicates the format used to generate the timestamp in the cache key.
11
11
  # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
12
12
  #
13
- # This is +:nsec+, by default.
13
+ # This is +:usec+, by default.
14
14
  class_attribute :cache_timestamp_format, :instance_writer => false
15
- self.cache_timestamp_format = :nsec
15
+ self.cache_timestamp_format = :usec
16
16
  end
17
17
 
18
- # Returns a String, which Action Pack uses for constructing an URL to this
18
+ # Returns a String, which Action Pack uses for constructing a URL to this
19
19
  # object. The default implementation returns this record's id as a String,
20
20
  # or nil if this record's unsaved.
21
21
  #
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  # Values longer than 20 characters will be truncated. The value
85
85
  # is truncated word by word.
86
86
  #
87
- # user = User.find_by(name: 'David HeinemeierHansson')
87
+ # user = User.find_by(name: 'David Heinemeier Hansson')
88
88
  # user.id # => 125
89
89
  # user_path(user) # => "/users/125-david"
90
90
  #
@@ -0,0 +1,56 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+
4
+ module ActiveRecord
5
+ # This class is used to create a table that keeps track of values and keys such
6
+ # as which environment migrations were run in.
7
+ class InternalMetadata < ActiveRecord::Base # :nodoc:
8
+ class << self
9
+ def primary_key
10
+ "key"
11
+ end
12
+
13
+ def table_name
14
+ "#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
15
+ end
16
+
17
+ def original_table_name
18
+ "#{table_name_prefix}active_record_internal_metadatas#{table_name_suffix}"
19
+ end
20
+
21
+ def []=(key, value)
22
+ find_or_initialize_by(key: key).update_attributes!(value: value)
23
+ end
24
+
25
+ def [](key)
26
+ where(key: key).pluck(:value).first
27
+ end
28
+
29
+ def table_exists?
30
+ ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
31
+ end
32
+
33
+ def original_table_exists?
34
+ # This method will be removed in Rails 5.1
35
+ # Since it is only necessary when `active_record_internal_metadatas` could exist
36
+ ActiveSupport::Deprecation.silence { connection.table_exists?(original_table_name) }
37
+ end
38
+
39
+ # Creates an internal metadata table with columns +key+ and +value+
40
+ def create_table
41
+ if original_table_exists?
42
+ connection.rename_table(original_table_name, table_name)
43
+ end
44
+ unless table_exists?
45
+ key_options = connection.internal_string_options_for_primary_key
46
+
47
+ connection.create_table(table_name, id: false) do |t|
48
+ t.string :key, key_options
49
+ t.string :value
50
+ t.timestamps
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -4,16 +4,32 @@ module ActiveRecord
4
4
  return coder unless coder.is_a?(Psych::Coder)
5
5
 
6
6
  case coder["active_record_yaml_version"]
7
- when 0 then coder
7
+ when 1 then coder
8
8
  else
9
9
  if coder["attributes"].is_a?(AttributeSet)
10
- coder
10
+ Rails420.convert(klass, coder)
11
11
  else
12
12
  Rails41.convert(klass, coder)
13
13
  end
14
14
  end
15
15
  end
16
16
 
17
+ module Rails420
18
+ def self.convert(klass, coder)
19
+ attribute_set = coder["attributes"]
20
+
21
+ klass.attribute_names.each do |attr_name|
22
+ attribute = attribute_set[attr_name]
23
+ if attribute.type.is_a?(Delegator)
24
+ type_from_klass = klass.type_for_attribute(attr_name)
25
+ attribute_set[attr_name] = attribute.with_type(type_from_klass)
26
+ end
27
+ end
28
+
29
+ coder
30
+ end
31
+ end
32
+
17
33
  module Rails41
18
34
  def self.convert(klass, coder)
19
35
  attributes = klass.attributes_builder
@@ -7,6 +7,7 @@ en:
7
7
  # Default error messages
8
8
  errors:
9
9
  messages:
10
+ required: "must exist"
10
11
  taken: "has already been taken"
11
12
 
12
13
  # Active Record models configuration
@@ -15,8 +16,8 @@ en:
15
16
  messages:
16
17
  record_invalid: "Validation failed: %{errors}"
17
18
  restrict_dependent_destroy:
18
- one: "Cannot delete record because a dependent %{record} exists"
19
- many: "Cannot delete record because dependent %{record} exist"
19
+ has_one: "Cannot delete record because a dependent %{record} exists"
20
+ has_many: "Cannot delete record because dependent %{record} exist"
20
21
  # Append your own errors here or at the model/attributes scope.
21
22
 
22
23
  # You can define own errors for models or model attributes.
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  #
12
12
  # == Usage
13
13
  #
14
- # Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
14
+ # Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
15
15
  # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
16
16
  # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
17
17
  #
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  # p1.save
23
23
  #
24
24
  # p2.first_name = "should fail"
25
- # p2.save # Raises a ActiveRecord::StaleObjectError
25
+ # p2.save # Raises an ActiveRecord::StaleObjectError
26
26
  #
27
27
  # Optimistic locking will also check for stale data when objects are destroyed. Example:
28
28
  #
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  # p1.first_name = "Michael"
33
33
  # p1.save
34
34
  #
35
- # p2.destroy # Raises a ActiveRecord::StaleObjectError
35
+ # p2.destroy # Raises an ActiveRecord::StaleObjectError
36
36
  #
37
37
  # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
38
38
  # or otherwise apply the business logic needed to resolve the conflict.
@@ -93,9 +93,9 @@ module ActiveRecord
93
93
  self.class.primary_key => id,
94
94
  lock_col => previous_lock_value,
95
95
  ).update_all(
96
- Hash[attributes_for_update(attribute_names).map do |name|
96
+ attributes_for_update(attribute_names).map do |name|
97
97
  [name, _read_attribute(name)]
98
- end]
98
+ end.to_h
99
99
  )
100
100
 
101
101
  unless affected_rows == 1
@@ -125,12 +125,8 @@ module ActiveRecord
125
125
  relation = super
126
126
 
127
127
  if locking_enabled?
128
- column_name = self.class.locking_column
129
- column = self.class.columns_hash[column_name]
130
- substitute = self.class.connection.substitute_at(column)
131
-
132
- relation = relation.where(self.class.arel_table[column_name].eq(substitute))
133
- relation.bind_values << [column, self[column_name].to_i]
128
+ locking_column = self.class.locking_column
129
+ relation = relation.where(locking_column => _read_attribute(locking_column))
134
130
  end
135
131
 
136
132
  relation
@@ -148,13 +144,13 @@ module ActiveRecord
148
144
 
149
145
  # Set the column to use for optimistic locking. Defaults to +lock_version+.
150
146
  def locking_column=(value)
151
- clear_caches_calculated_from_columns
147
+ reload_schema_from_cache
152
148
  @locking_column = value.to_s
153
149
  end
154
150
 
155
151
  # The version column used for optimistic locking. Defaults to +lock_version+.
156
152
  def locking_column
157
- reset_locking_column unless defined?(@locking_column)
153
+ @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
158
154
  @locking_column
159
155
  end
160
156
 
@@ -188,12 +184,16 @@ module ActiveRecord
188
184
  end
189
185
  end
190
186
 
191
- class LockingType < SimpleDelegator # :nodoc:
192
- def type_cast_from_database(value)
187
+ class LockingType < DelegateClass(Type::Value) # :nodoc:
188
+ def deserialize(value)
193
189
  # `nil` *should* be changed to 0
194
190
  super.to_i
195
191
  end
196
192
 
193
+ def serialize(value)
194
+ super.to_i
195
+ end
196
+
197
197
  def init_with(coder)
198
198
  __setobj__(coder['subtype'])
199
199
  end
@@ -51,7 +51,7 @@ module ActiveRecord
51
51
  # end
52
52
  #
53
53
  # Database-specific information on row locking:
54
- # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
54
+ # MySQL: http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
55
55
  # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
56
  module Pessimistic
57
57
  # Obtain a row lock on this record. Reloads the record to obtain the requested
@@ -20,24 +20,25 @@ module ActiveRecord
20
20
  @odd = false
21
21
  end
22
22
 
23
- def render_bind(column, value)
24
- if column
25
- if column.binary?
26
- # This specifically deals with the PG adapter that casts bytea columns into a Hash.
27
- value = value[:value] if value.is_a?(Hash)
28
- value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
23
+ def render_bind(attribute)
24
+ value = if attribute.type.binary? && attribute.value
25
+ if attribute.value.is_a?(Hash)
26
+ "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
27
+ else
28
+ "<#{attribute.value.bytesize} bytes of binary data>"
29
29
  end
30
-
31
- [column.name, value]
32
30
  else
33
- [nil, value]
31
+ attribute.value_for_database
34
32
  end
33
+
34
+ [attribute.name, value]
35
35
  end
36
36
 
37
37
  def sql(event)
38
- self.class.runtime += event.duration
39
38
  return unless logger.debug?
40
39
 
40
+ self.class.runtime += event.duration
41
+
41
42
  payload = event.payload
42
43
 
43
44
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
@@ -47,23 +48,44 @@ module ActiveRecord
47
48
  binds = nil
48
49
 
49
50
  unless (payload[:binds] || []).empty?
50
- binds = " " + payload[:binds].map { |col,v|
51
- render_bind(col, v)
52
- }.inspect
51
+ binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
53
52
  end
54
53
 
55
- if odd?
56
- name = color(name, CYAN, true)
57
- sql = color(sql, nil, true)
58
- else
59
- name = color(name, MAGENTA, true)
60
- end
54
+ name = colorize_payload_name(name, payload[:name])
55
+ sql = color(sql, sql_color(sql), true)
61
56
 
62
57
  debug " #{name} #{sql}#{binds}"
63
58
  end
64
59
 
65
- def odd?
66
- @odd = !@odd
60
+ private
61
+
62
+ def colorize_payload_name(name, payload_name)
63
+ if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
64
+ color(name, MAGENTA, true)
65
+ else
66
+ color(name, CYAN, true)
67
+ end
68
+ end
69
+
70
+ def sql_color(sql)
71
+ case sql
72
+ when /\A\s*rollback/mi
73
+ RED
74
+ when /select .*for update/mi, /\A\s*lock/mi
75
+ WHITE
76
+ when /\A\s*select/i
77
+ BLUE
78
+ when /\A\s*insert/i
79
+ GREEN
80
+ when /\A\s*update/i
81
+ YELLOW
82
+ when /\A\s*delete/i
83
+ RED
84
+ when /transaction\s*\Z/i
85
+ CYAN
86
+ else
87
+ MAGENTA
88
+ end
67
89
  end
68
90
 
69
91
  def logger
@@ -9,40 +9,170 @@ module ActiveRecord
9
9
  end
10
10
  end
11
11
 
12
- # Exception that can be raised to stop migrations from going backwards.
12
+ # Exception that can be raised to stop migrations from being rolled back.
13
+ # For example the following migration is not reversible.
14
+ # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
15
+ #
16
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[5.0]
17
+ # def change
18
+ # create_table :distributors do |t|
19
+ # t.string :zipcode
20
+ # end
21
+ #
22
+ # execute <<-SQL
23
+ # ALTER TABLE distributors
24
+ # ADD CONSTRAINT zipchk
25
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
26
+ # SQL
27
+ # end
28
+ # end
29
+ #
30
+ # There are two ways to mitigate this problem.
31
+ #
32
+ # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
33
+ #
34
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
35
+ # def up
36
+ # create_table :distributors do |t|
37
+ # t.string :zipcode
38
+ # end
39
+ #
40
+ # execute <<-SQL
41
+ # ALTER TABLE distributors
42
+ # ADD CONSTRAINT zipchk
43
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
44
+ # SQL
45
+ # end
46
+ #
47
+ # def down
48
+ # execute <<-SQL
49
+ # ALTER TABLE distributors
50
+ # DROP CONSTRAINT zipchk
51
+ # SQL
52
+ #
53
+ # drop_table :distributors
54
+ # end
55
+ # end
56
+ #
57
+ # 2. Use the #reversible method in <tt>#change</tt> method:
58
+ #
59
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
60
+ # def change
61
+ # create_table :distributors do |t|
62
+ # t.string :zipcode
63
+ # end
64
+ #
65
+ # reversible do |dir|
66
+ # dir.up do
67
+ # execute <<-SQL
68
+ # ALTER TABLE distributors
69
+ # ADD CONSTRAINT zipchk
70
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
71
+ # SQL
72
+ # end
73
+ #
74
+ # dir.down do
75
+ # execute <<-SQL
76
+ # ALTER TABLE distributors
77
+ # DROP CONSTRAINT zipchk
78
+ # SQL
79
+ # end
80
+ # end
81
+ # end
82
+ # end
13
83
  class IrreversibleMigration < MigrationError
14
84
  end
15
85
 
16
86
  class DuplicateMigrationVersionError < MigrationError#:nodoc:
17
- def initialize(version)
18
- super("Multiple migrations have the version number #{version}")
87
+ def initialize(version = nil)
88
+ if version
89
+ super("Multiple migrations have the version number #{version}.")
90
+ else
91
+ super("Duplicate migration version error.")
92
+ end
19
93
  end
20
94
  end
21
95
 
22
96
  class DuplicateMigrationNameError < MigrationError#:nodoc:
23
- def initialize(name)
24
- super("Multiple migrations have the name #{name}")
97
+ def initialize(name = nil)
98
+ if name
99
+ super("Multiple migrations have the name #{name}.")
100
+ else
101
+ super("Duplicate migration name.")
102
+ end
25
103
  end
26
104
  end
27
105
 
28
106
  class UnknownMigrationVersionError < MigrationError #:nodoc:
29
- def initialize(version)
30
- super("No migration with version number #{version}")
107
+ def initialize(version = nil)
108
+ if version
109
+ super("No migration with version number #{version}.")
110
+ else
111
+ super("Unknown migration version.")
112
+ end
31
113
  end
32
114
  end
33
115
 
34
116
  class IllegalMigrationNameError < MigrationError#:nodoc:
35
- def initialize(name)
36
- super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
117
+ def initialize(name = nil)
118
+ if name
119
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
120
+ else
121
+ super("Illegal name for migration.")
122
+ end
37
123
  end
38
124
  end
39
125
 
40
126
  class PendingMigrationError < MigrationError#:nodoc:
127
+ def initialize(message = nil)
128
+ if !message && defined?(Rails.env)
129
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}")
130
+ elsif !message
131
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate")
132
+ else
133
+ super
134
+ end
135
+ end
136
+ end
137
+
138
+ class ConcurrentMigrationError < MigrationError #:nodoc:
139
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running.".freeze
140
+
141
+ def initialize(message = DEFAULT_MESSAGE)
142
+ super
143
+ end
144
+ end
145
+
146
+ class NoEnvironmentInSchemaError < MigrationError #:nodoc:
41
147
  def initialize
148
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rails db:environment:set"
149
+ if defined?(Rails.env)
150
+ super("#{msg} RAILS_ENV=#{::Rails.env}")
151
+ else
152
+ super(msg)
153
+ end
154
+ end
155
+ end
156
+
157
+ class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
158
+ def initialize(env = "production")
159
+ msg = "You are attempting to run a destructive action against your '#{env}' database.\n"
160
+ msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
161
+ msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
162
+ super(msg)
163
+ end
164
+ end
165
+
166
+ class EnvironmentMismatchError < ActiveRecordError
167
+ def initialize(current: nil, stored: nil)
168
+ msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
169
+ msg << "You are running in `#{ current }` environment. "
170
+ msg << "If you are sure you want to continue, first set the environment using:\n\n"
171
+ msg << "\tbin/rails db:environment:set"
42
172
  if defined?(Rails.env)
43
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
173
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
44
174
  else
45
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
175
+ super("#{msg}\n\n")
46
176
  end
47
177
  end
48
178
  end
@@ -59,7 +189,7 @@ module ActiveRecord
59
189
  #
60
190
  # Example of a simple migration:
61
191
  #
62
- # class AddSsl < ActiveRecord::Migration
192
+ # class AddSsl < ActiveRecord::Migration[5.0]
63
193
  # def up
64
194
  # add_column :accounts, :ssl_enabled, :boolean, default: true
65
195
  # end
@@ -79,7 +209,7 @@ module ActiveRecord
79
209
  #
80
210
  # Example of a more complex migration that also needs to initialize data:
81
211
  #
82
- # class AddSystemSettings < ActiveRecord::Migration
212
+ # class AddSystemSettings < ActiveRecord::Migration[5.0]
83
213
  # def up
84
214
  # create_table :system_settings do |t|
85
215
  # t.string :name
@@ -106,17 +236,18 @@ module ActiveRecord
106
236
  #
107
237
  # == Available transformations
108
238
  #
239
+ # === Creation
240
+ #
241
+ # * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
242
+ # table having its name as the lexical order of the first two
243
+ # arguments. See
244
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
245
+ # details.
109
246
  # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
110
247
  # makes the table object available to a block that can then add columns to it,
111
248
  # following the same format as +add_column+. See example above. The options hash
112
249
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
113
250
  # table definition.
114
- # * <tt>drop_table(name)</tt>: Drops the table called +name+.
115
- # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
116
- # the table called +name+. It makes the table object available to a block that
117
- # can then add/remove columns, indexes or foreign keys to it.
118
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
119
- # to +new_name+.
120
251
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
121
252
  # to the table called +table_name+
122
253
  # named +column_name+ specified to be one of the following types:
@@ -127,21 +258,59 @@ module ActiveRecord
127
258
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
128
259
  # <tt>{ limit: 50, null: false }</tt>) -- see
129
260
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
130
- # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
131
- # a column but keeps the type and content.
132
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
133
- # the column to a different type using the same parameters as add_column.
134
- # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
135
- # named +column_name+ from the table called +table_name+.
261
+ # * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
262
+ # foreign key. +from_table+ is the table with the key column, +to_table+ contains
263
+ # the referenced primary key.
136
264
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
137
265
  # with the name of the column. Other options include
138
266
  # <tt>:name</tt>, <tt>:unique</tt> (e.g.
139
267
  # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
140
268
  # (e.g. <tt>{ order: { name: :desc } }</tt>).
141
- # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
142
- # specified by +column_name+.
269
+ # * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
270
+ # +reference_name_id+ by default an integer. See
271
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
272
+ # * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
273
+ # and +updated_at+) columns to +table_name+.
274
+ #
275
+ # === Modification
276
+ #
277
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
278
+ # the column to a different type using the same parameters as add_column.
279
+ # * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
280
+ # default value for +column_name+ defined by +default+ on +table_name+.
281
+ # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
282
+ # Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
283
+ # indicates whether the value can be +NULL+. See
284
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
285
+ # details.
286
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
287
+ # the table called +name+. It makes the table object available to a block that
288
+ # can then add/remove columns, indexes or foreign keys to it.
289
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
290
+ # a column but keeps the type and content.
291
+ # * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
292
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
293
+ # to +new_name+.
294
+ #
295
+ # === Deletion
296
+ #
297
+ # * <tt>drop_table(name)</tt>: Drops the table called +name+.
298
+ # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
299
+ # specified by the given arguments.
300
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
301
+ # named +column_name+ from the table called +table_name+.
302
+ # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
303
+ # columns from the table definition.
304
+ # * <tt>remove_foreign_key(from_table, options_or_to_table)</tt>: Removes the
305
+ # given foreign key from the table called +table_name+.
306
+ # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
307
+ # specified by +column_names+.
143
308
  # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
144
309
  # specified by +index_name+.
310
+ # * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
311
+ # reference(s) on +table_name+ specified by +ref_name+.
312
+ # * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
313
+ # columns (+created_at+ and +updated_at+) from the table definition.
145
314
  #
146
315
  # == Irreversible transformations
147
316
  #
@@ -165,24 +334,24 @@ module ActiveRecord
165
334
  #
166
335
  # rails generate migration add_fieldname_to_tablename fieldname:string
167
336
  #
168
- # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
169
- # class AddFieldnameToTablename < ActiveRecord::Migration
337
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
338
+ # class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
170
339
  # def change
171
- # add_column :tablenames, :field, :string
340
+ # add_column :tablenames, :fieldname, :string
172
341
  # end
173
342
  # end
174
343
  #
175
344
  # To run migrations against the currently configured database, use
176
- # <tt>rake db:migrate</tt>. This will update the database by running all of the
345
+ # <tt>rails db:migrate</tt>. This will update the database by running all of the
177
346
  # pending migrations, creating the <tt>schema_migrations</tt> table
178
347
  # (see "About the schema_migrations table" section below) if missing. It will also
179
348
  # invoke the db:schema:dump task, which will update your db/schema.rb file
180
349
  # to match the structure of your database.
181
350
  #
182
351
  # To roll the database back to a previous migration version, use
183
- # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
352
+ # <tt>rails db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
184
353
  # you wish to downgrade. Alternatively, you can also use the STEP option if you
185
- # wish to rollback last few migrations. <tt>rake db:migrate STEP=2</tt> will rollback
354
+ # wish to rollback last few migrations. <tt>rails db:migrate STEP=2</tt> will rollback
186
355
  # the latest two migrations.
187
356
  #
188
357
  # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
@@ -197,7 +366,7 @@ module ActiveRecord
197
366
  #
198
367
  # Not all migrations change the schema. Some just fix the data:
199
368
  #
200
- # class RemoveEmptyTags < ActiveRecord::Migration
369
+ # class RemoveEmptyTags < ActiveRecord::Migration[5.0]
201
370
  # def up
202
371
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
203
372
  # end
@@ -210,7 +379,7 @@ module ActiveRecord
210
379
  #
211
380
  # Others remove columns when they migrate up instead of down:
212
381
  #
213
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
382
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
214
383
  # def up
215
384
  # remove_column :items, :incomplete_items_count
216
385
  # remove_column :items, :completed_items_count
@@ -224,7 +393,7 @@ module ActiveRecord
224
393
  #
225
394
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
226
395
  #
227
- # class MakeJoinUnique < ActiveRecord::Migration
396
+ # class MakeJoinUnique < ActiveRecord::Migration[5.0]
228
397
  # def up
229
398
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
230
399
  # end
@@ -241,7 +410,7 @@ module ActiveRecord
241
410
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
242
411
  # latest column data from after the new column was added. Example:
243
412
  #
244
- # class AddPeopleSalary < ActiveRecord::Migration
413
+ # class AddPeopleSalary < ActiveRecord::Migration[5.0]
245
414
  # def up
246
415
  # add_column :people, :salary, :integer
247
416
  # Person.reset_column_information
@@ -275,21 +444,6 @@ module ActiveRecord
275
444
  # The phrase "Updating salaries..." would then be printed, along with the
276
445
  # benchmark for the block when the block completes.
277
446
  #
278
- # == About the schema_migrations table
279
- #
280
- # Rails versions 2.0 and prior used to create a table called
281
- # <tt>schema_info</tt> when using migrations. This table contained the
282
- # version of the schema as of the last applied migration.
283
- #
284
- # Starting with Rails 2.1, the <tt>schema_info</tt> table is
285
- # (automatically) replaced by the <tt>schema_migrations</tt> table, which
286
- # contains the version numbers of all the migrations applied.
287
- #
288
- # As a result, it is now possible to add migration files that are numbered
289
- # lower than the current schema version: when migrating up, those
290
- # never-applied "interleaved" migrations will be automatically applied, and
291
- # when migrating down, never-applied "interleaved" migrations will be skipped.
292
- #
293
447
  # == Timestamped Migrations
294
448
  #
295
449
  # By default, Rails generates migrations that look like:
@@ -314,7 +468,7 @@ module ActiveRecord
314
468
  # To define a reversible migration, define the +change+ method in your
315
469
  # migration like this:
316
470
  #
317
- # class TenderloveMigration < ActiveRecord::Migration
471
+ # class TenderloveMigration < ActiveRecord::Migration[5.0]
318
472
  # def change
319
473
  # create_table(:horses) do |t|
320
474
  # t.column :content, :text
@@ -344,7 +498,7 @@ module ActiveRecord
344
498
  # can't execute inside a transaction though, and for these situations
345
499
  # you can turn the automatic transactions off.
346
500
  #
347
- # class ChangeEnum < ActiveRecord::Migration
501
+ # class ChangeEnum < ActiveRecord::Migration[5.0]
348
502
  # disable_ddl_transaction!
349
503
  #
350
504
  # def up
@@ -356,10 +510,31 @@ module ActiveRecord
356
510
  # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
357
511
  class Migration
358
512
  autoload :CommandRecorder, 'active_record/migration/command_recorder'
513
+ autoload :Compatibility, 'active_record/migration/compatibility'
514
+
515
+ # This must be defined before the inherited hook, below
516
+ class Current < Migration # :nodoc:
517
+ end
518
+
519
+ def self.inherited(subclass) # :nodoc:
520
+ super
521
+ if subclass.superclass == Migration
522
+ subclass.include Compatibility::Legacy
523
+ end
524
+ end
525
+
526
+ def self.[](version)
527
+ Compatibility.find(version)
528
+ end
529
+
530
+ def self.current_version
531
+ ActiveRecord::VERSION::STRING.to_f
532
+ end
359
533
 
534
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
360
535
 
361
536
  # This class is used to verify that all migrations have been run before
362
- # loading a web page if config.active_record.migration_error is set to :page_load
537
+ # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
363
538
  class CheckPending
364
539
  def initialize(app)
365
540
  @app = app
@@ -388,17 +563,22 @@ module ActiveRecord
388
563
  attr_accessor :delegate # :nodoc:
389
564
  attr_accessor :disable_ddl_transaction # :nodoc:
390
565
 
566
+ def nearest_delegate # :nodoc:
567
+ delegate || superclass.nearest_delegate
568
+ end
569
+
570
+ # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
391
571
  def check_pending!(connection = Base.connection)
392
572
  raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
393
573
  end
394
574
 
395
575
  def load_schema_if_pending!
396
576
  if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
397
- # Roundrip to Rake to allow plugins to hook into database initialization.
577
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
398
578
  FileUtils.cd Rails.root do
399
579
  current_config = Base.connection_config
400
580
  Base.clear_all_connections!
401
- system("bin/rake db:test:prepare")
581
+ system("bin/rails db:test:prepare")
402
582
  # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
403
583
  Base.establish_connection(current_config)
404
584
  end
@@ -413,7 +593,7 @@ module ActiveRecord
413
593
  end
414
594
 
415
595
  def method_missing(name, *args, &block) # :nodoc:
416
- (delegate || superclass.delegate).send(name, *args, &block)
596
+ nearest_delegate.send(name, *args, &block)
417
597
  end
418
598
 
419
599
  def migrate(direction)
@@ -453,7 +633,7 @@ module ActiveRecord
453
633
  # and create the table 'apples' on the way up, and the reverse
454
634
  # on the way down.
455
635
  #
456
- # class FixTLMigration < ActiveRecord::Migration
636
+ # class FixTLMigration < ActiveRecord::Migration[5.0]
457
637
  # def change
458
638
  # revert do
459
639
  # create_table(:horses) do |t|
@@ -470,9 +650,9 @@ module ActiveRecord
470
650
  # Or equivalently, if +TenderloveMigration+ is defined as in the
471
651
  # documentation for Migration:
472
652
  #
473
- # require_relative '2012121212_tenderlove_migration'
653
+ # require_relative '20121212123456_tenderlove_migration'
474
654
  #
475
- # class FixupTLMigration < ActiveRecord::Migration
655
+ # class FixupTLMigration < ActiveRecord::Migration[5.0]
476
656
  # def change
477
657
  # revert TenderloveMigration
478
658
  #
@@ -486,13 +666,13 @@ module ActiveRecord
486
666
  def revert(*migration_classes)
487
667
  run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
488
668
  if block_given?
489
- if @connection.respond_to? :revert
490
- @connection.revert { yield }
669
+ if connection.respond_to? :revert
670
+ connection.revert { yield }
491
671
  else
492
- recorder = CommandRecorder.new(@connection)
672
+ recorder = CommandRecorder.new(connection)
493
673
  @connection = recorder
494
674
  suppress_messages do
495
- @connection.revert { yield }
675
+ connection.revert { yield }
496
676
  end
497
677
  @connection = recorder.delegate
498
678
  recorder.commands.each do |cmd, args, block|
@@ -503,7 +683,7 @@ module ActiveRecord
503
683
  end
504
684
 
505
685
  def reverting?
506
- @connection.respond_to?(:reverting) && @connection.reverting
686
+ connection.respond_to?(:reverting) && connection.reverting
507
687
  end
508
688
 
509
689
  class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
@@ -525,7 +705,7 @@ module ActiveRecord
525
705
  # when the three columns 'first_name', 'last_name' and 'full_name' exist,
526
706
  # even when migrating down:
527
707
  #
528
- # class SplitNameMigration < ActiveRecord::Migration
708
+ # class SplitNameMigration < ActiveRecord::Migration[5.0]
529
709
  # def change
530
710
  # add_column :users, :first_name, :string
531
711
  # add_column :users, :last_name, :string
@@ -560,7 +740,7 @@ module ActiveRecord
560
740
  revert { run(*migration_classes, direction: dir, revert: true) }
561
741
  else
562
742
  migration_classes.each do |migration_class|
563
- migration_class.new.exec_migration(@connection, dir)
743
+ migration_class.new.exec_migration(connection, dir)
564
744
  end
565
745
  end
566
746
  end
@@ -649,10 +829,10 @@ module ActiveRecord
649
829
  end
650
830
 
651
831
  def method_missing(method, *arguments, &block)
652
- arg_list = arguments.map{ |a| a.inspect } * ', '
832
+ arg_list = arguments.map(&:inspect) * ', '
653
833
 
654
834
  say_with_time "#{method}(#{arg_list})" do
655
- unless @connection.respond_to? :revert
835
+ unless connection.respond_to? :revert
656
836
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
657
837
  arguments[0] = proper_table_name(arguments.first, table_name_options)
658
838
  if [:rename_table, :add_foreign_key].include?(method) ||
@@ -731,7 +911,9 @@ module ActiveRecord
731
911
  end
732
912
  end
733
913
 
734
- def table_name_options(config = ActiveRecord::Base)
914
+ # Builds a hash for use in ActiveRecord::Migration#proper_table_name using
915
+ # the Active Record object's table_name prefix and suffix
916
+ def table_name_options(config = ActiveRecord::Base) #:nodoc:
735
917
  {
736
918
  table_name_prefix: config.table_name_prefix,
737
919
  table_name_suffix: config.table_name_suffix
@@ -823,7 +1005,7 @@ module ActiveRecord
823
1005
  new(:up, migrations, target_version).migrate
824
1006
  end
825
1007
 
826
- def down(migrations_paths, target_version = nil, &block)
1008
+ def down(migrations_paths, target_version = nil)
827
1009
  migrations = migrations(migrations_paths)
828
1010
  migrations.select! { |m| yield m } if block_given?
829
1011
 
@@ -843,10 +1025,12 @@ module ActiveRecord
843
1025
  end
844
1026
 
845
1027
  def get_all_versions(connection = Base.connection)
846
- if connection.table_exists?(schema_migrations_table_name)
847
- SchemaMigration.all.map { |x| x.version.to_i }.sort
848
- else
849
- []
1028
+ ActiveSupport::Deprecation.silence do
1029
+ if connection.table_exists?(schema_migrations_table_name)
1030
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
1031
+ else
1032
+ []
1033
+ end
850
1034
  end
851
1035
  end
852
1036
 
@@ -862,32 +1046,30 @@ module ActiveRecord
862
1046
  migrations(migrations_paths).any?
863
1047
  end
864
1048
 
865
- def last_version
866
- last_migration.version
867
- end
868
-
869
1049
  def last_migration #:nodoc:
870
1050
  migrations(migrations_paths).last || NullMigration.new
871
1051
  end
872
1052
 
873
1053
  def migrations_paths
874
1054
  @migrations_paths ||= ['db/migrate']
875
- # just to not break things if someone uses: migration_path = some_string
1055
+ # just to not break things if someone uses: migrations_path = some_string
876
1056
  Array(@migrations_paths)
877
1057
  end
878
1058
 
879
- def migrations_path
880
- migrations_paths.first
1059
+ def match_to_migration_filename?(filename) # :nodoc:
1060
+ File.basename(filename) =~ Migration::MigrationFilenameRegexp
881
1061
  end
882
1062
 
883
1063
  def parse_migration_filename(filename) # :nodoc:
884
- File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
1064
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
885
1065
  end
886
1066
 
887
1067
  def migrations(paths)
888
1068
  paths = Array(paths)
889
1069
 
890
- migrations = migration_files(paths).map do |file|
1070
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
1071
+
1072
+ migrations = files.map do |file|
891
1073
  version, name, scope = parse_migration_filename(file)
892
1074
  raise IllegalMigrationNameError.new(file) unless version
893
1075
  version = version.to_i
@@ -899,30 +1081,6 @@ module ActiveRecord
899
1081
  migrations.sort_by(&:version)
900
1082
  end
901
1083
 
902
- def migrations_status(paths)
903
- paths = Array(paths)
904
-
905
- db_list = ActiveRecord::SchemaMigration.normalized_versions
906
-
907
- file_list = migration_files(paths).map do |file|
908
- version, name, scope = parse_migration_filename(file)
909
- raise IllegalMigrationNameError.new(file) unless version
910
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
- status = db_list.delete(version) ? "up" : "down"
912
- [status, version, (name + scope).humanize]
913
- end.compact
914
-
915
- db_list.map! do |version|
916
- ["up", version, "********** NO FILE **********"]
917
- end
918
-
919
- (db_list + file_list).sort_by { |_, version, _| version }
920
- end
921
-
922
- def migration_files(paths)
923
- Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
- end
925
-
926
1084
  private
927
1085
 
928
1086
  def move(direction, migrations_paths, steps)
@@ -948,6 +1106,7 @@ module ActiveRecord
948
1106
  validate(@migrations)
949
1107
 
950
1108
  Base.connection.initialize_schema_migrations_table
1109
+ Base.connection.initialize_internal_metadata_table
951
1110
  end
952
1111
 
953
1112
  def current_version
@@ -960,32 +1119,18 @@ module ActiveRecord
960
1119
  alias :current :current_migration
961
1120
 
962
1121
  def run
963
- migration = migrations.detect { |m| m.version == @target_version }
964
- raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
965
- unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
966
- begin
967
- execute_migration_in_transaction(migration, @direction)
968
- rescue => e
969
- canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
970
- raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
971
- end
1122
+ if use_advisory_lock?
1123
+ with_advisory_lock { run_without_lock }
1124
+ else
1125
+ run_without_lock
972
1126
  end
973
1127
  end
974
1128
 
975
1129
  def migrate
976
- if !target && @target_version && @target_version > 0
977
- raise UnknownMigrationVersionError.new(@target_version)
978
- end
979
-
980
- runnable.each do |migration|
981
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
982
-
983
- begin
984
- execute_migration_in_transaction(migration, @direction)
985
- rescue => e
986
- canceled_msg = use_transaction?(migration) ? "this and " : ""
987
- raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
988
- end
1130
+ if use_advisory_lock?
1131
+ with_advisory_lock { migrate_without_lock }
1132
+ else
1133
+ migrate_without_lock
989
1134
  end
990
1135
  end
991
1136
 
@@ -1010,19 +1155,67 @@ module ActiveRecord
1010
1155
  end
1011
1156
 
1012
1157
  def migrated
1013
- @migrated_versions ||= Set.new(self.class.get_all_versions)
1158
+ @migrated_versions || load_migrated
1159
+ end
1160
+
1161
+ def load_migrated
1162
+ @migrated_versions = Set.new(self.class.get_all_versions)
1014
1163
  end
1015
1164
 
1016
1165
  private
1166
+
1167
+ # Used for running a specific migration.
1168
+ def run_without_lock
1169
+ migration = migrations.detect { |m| m.version == @target_version }
1170
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1171
+ execute_migration_in_transaction(migration, @direction)
1172
+
1173
+ record_environment
1174
+ end
1175
+
1176
+ # Used for running multiple migrations up to or down to a certain value.
1177
+ def migrate_without_lock
1178
+ if invalid_target?
1179
+ raise UnknownMigrationVersionError.new(@target_version)
1180
+ end
1181
+
1182
+ runnable.each do |migration|
1183
+ execute_migration_in_transaction(migration, @direction)
1184
+ end
1185
+
1186
+ record_environment
1187
+ end
1188
+
1189
+ # Stores the current environment in the database.
1190
+ def record_environment
1191
+ return if down?
1192
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
1193
+ end
1194
+
1017
1195
  def ran?(migration)
1018
1196
  migrated.include?(migration.version.to_i)
1019
1197
  end
1020
1198
 
1199
+ # Return true if a valid version is not provided.
1200
+ def invalid_target?
1201
+ !target && @target_version && @target_version > 0
1202
+ end
1203
+
1021
1204
  def execute_migration_in_transaction(migration, direction)
1205
+ return if down? && !migrated.include?(migration.version.to_i)
1206
+ return if up? && migrated.include?(migration.version.to_i)
1207
+
1208
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1209
+
1022
1210
  ddl_transaction(migration) do
1023
1211
  migration.migrate(direction)
1024
1212
  record_version_state_after_migrating(migration.version)
1025
1213
  end
1214
+ rescue => e
1215
+ msg = "An error has occurred, "
1216
+ msg << "this and " if use_transaction?(migration)
1217
+ msg << "all later migrations canceled:\n\n#{e}"
1218
+ raise StandardError, msg, e.backtrace
1026
1219
  end
1027
1220
 
1028
1221
  def target
@@ -1051,10 +1244,27 @@ module ActiveRecord
1051
1244
  ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
1052
1245
  else
1053
1246
  migrated << version
1054
- ActiveRecord::SchemaMigration.create!(:version => version.to_s)
1247
+ ActiveRecord::SchemaMigration.create!(version: version.to_s)
1055
1248
  end
1056
1249
  end
1057
1250
 
1251
+ def self.last_stored_environment
1252
+ return nil if current_version == 0
1253
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
1254
+
1255
+ environment = ActiveRecord::InternalMetadata[:environment]
1256
+ raise NoEnvironmentInSchemaError unless environment
1257
+ environment
1258
+ end
1259
+
1260
+ def self.current_environment
1261
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1262
+ end
1263
+
1264
+ def self.protected_environment?
1265
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1266
+ end
1267
+
1058
1268
  def up?
1059
1269
  @direction == :up
1060
1270
  end
@@ -1075,5 +1285,25 @@ module ActiveRecord
1075
1285
  def use_transaction?(migration)
1076
1286
  !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1077
1287
  end
1288
+
1289
+ def use_advisory_lock?
1290
+ Base.connection.supports_advisory_locks?
1291
+ end
1292
+
1293
+ def with_advisory_lock
1294
+ lock_id = generate_migrator_advisory_lock_id
1295
+ got_lock = Base.connection.get_advisory_lock(lock_id)
1296
+ raise ConcurrentMigrationError unless got_lock
1297
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1298
+ yield
1299
+ ensure
1300
+ Base.connection.release_advisory_lock(lock_id) if got_lock
1301
+ end
1302
+
1303
+ MIGRATOR_SALT = 2053462845
1304
+ def generate_migrator_advisory_lock_id
1305
+ db_name_hash = Zlib.crc32(Base.connection.current_database)
1306
+ MIGRATOR_SALT * db_name_hash
1307
+ end
1078
1308
  end
1079
1309
  end