activerecord 4.2.11.3 → 5.0.0.beta1
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -29
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +16 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +9 -14
- data/lib/active_record/type/time.rb +3 -21
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- 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 +:
|
13
|
+
# This is +:usec+, by default.
|
14
14
|
class_attribute :cache_timestamp_format, :instance_writer => false
|
15
|
-
self.cache_timestamp_format = :
|
15
|
+
self.cache_timestamp_format = :usec
|
16
16
|
end
|
17
17
|
|
18
|
-
# Returns a String, which Action Pack uses for constructing
|
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
|
87
|
+
# user = User.find_by(name: 'David Heinemeier Hansson')
|
88
88
|
# user.id # => 125
|
89
89
|
# user_path(user) # => "/users/125-david"
|
90
90
|
#
|
@@ -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
|
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
|
-
|
19
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
129
|
-
|
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,7 +144,7 @@ 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
|
-
|
147
|
+
reload_schema_from_cache
|
152
148
|
@locking_column = value.to_s
|
153
149
|
end
|
154
150
|
|
@@ -188,8 +184,8 @@ module ActiveRecord
|
|
188
184
|
end
|
189
185
|
end
|
190
186
|
|
191
|
-
class LockingType <
|
192
|
-
def
|
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
|
@@ -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.
|
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,21 @@ module ActiveRecord
|
|
20
20
|
@odd = false
|
21
21
|
end
|
22
22
|
|
23
|
-
def render_bind(
|
24
|
-
if
|
25
|
-
|
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>"
|
29
|
-
end
|
30
|
-
|
31
|
-
[column.name, value]
|
23
|
+
def render_bind(attribute)
|
24
|
+
value = if attribute.type.binary? && attribute.value
|
25
|
+
"<#{attribute.value.bytesize} bytes of binary data>"
|
32
26
|
else
|
33
|
-
|
27
|
+
attribute.value_for_database
|
34
28
|
end
|
29
|
+
|
30
|
+
[attribute.name, value]
|
35
31
|
end
|
36
32
|
|
37
33
|
def sql(event)
|
38
|
-
self.class.runtime += event.duration
|
39
34
|
return unless logger.debug?
|
40
35
|
|
36
|
+
self.class.runtime += event.duration
|
37
|
+
|
41
38
|
payload = event.payload
|
42
39
|
|
43
40
|
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
@@ -47,23 +44,44 @@ module ActiveRecord
|
|
47
44
|
binds = nil
|
48
45
|
|
49
46
|
unless (payload[:binds] || []).empty?
|
50
|
-
binds = " " + payload[:binds].map { |
|
51
|
-
render_bind(col, v)
|
52
|
-
}.inspect
|
47
|
+
binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
|
53
48
|
end
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
sql = color(sql, nil, true)
|
58
|
-
else
|
59
|
-
name = color(name, MAGENTA, true)
|
60
|
-
end
|
50
|
+
name = colorize_payload_name(name, payload[:name])
|
51
|
+
sql = color(sql, sql_color(sql), true)
|
61
52
|
|
62
53
|
debug " #{name} #{sql}#{binds}"
|
63
54
|
end
|
64
55
|
|
65
|
-
|
66
|
-
|
56
|
+
private
|
57
|
+
|
58
|
+
def colorize_payload_name(name, payload_name)
|
59
|
+
if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
|
60
|
+
color(name, MAGENTA, true)
|
61
|
+
else
|
62
|
+
color(name, CYAN, true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def sql_color(sql)
|
67
|
+
case sql
|
68
|
+
when /\A\s*rollback/mi
|
69
|
+
RED
|
70
|
+
when /\s*.*?select .*for update/mi, /\A\s*lock/mi
|
71
|
+
WHITE
|
72
|
+
when /\A\s*select/i
|
73
|
+
BLUE
|
74
|
+
when /\A\s*insert/i
|
75
|
+
GREEN
|
76
|
+
when /\A\s*update/i
|
77
|
+
YELLOW
|
78
|
+
when /\A\s*delete/i
|
79
|
+
RED
|
80
|
+
when /transaction\s*\Z/i
|
81
|
+
CYAN
|
82
|
+
else
|
83
|
+
MAGENTA
|
84
|
+
end
|
67
85
|
end
|
68
86
|
|
69
87
|
def logger
|
@@ -9,44 +9,140 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
# Exception that can be raised to stop migrations from
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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:
|
41
|
-
def initialize
|
42
|
-
if defined?(Rails.env)
|
43
|
-
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/
|
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.")
|
44
132
|
else
|
45
|
-
super
|
133
|
+
super
|
46
134
|
end
|
47
135
|
end
|
48
136
|
end
|
49
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
|
+
|
50
146
|
# = Active Record Migrations
|
51
147
|
#
|
52
148
|
# Migrations can manage the evolution of a schema used by several physical
|
@@ -59,7 +155,7 @@ module ActiveRecord
|
|
59
155
|
#
|
60
156
|
# Example of a simple migration:
|
61
157
|
#
|
62
|
-
# class AddSsl < ActiveRecord::Migration
|
158
|
+
# class AddSsl < ActiveRecord::Migration[5.0]
|
63
159
|
# def up
|
64
160
|
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
65
161
|
# end
|
@@ -79,7 +175,7 @@ module ActiveRecord
|
|
79
175
|
#
|
80
176
|
# Example of a more complex migration that also needs to initialize data:
|
81
177
|
#
|
82
|
-
# class AddSystemSettings < ActiveRecord::Migration
|
178
|
+
# class AddSystemSettings < ActiveRecord::Migration[5.0]
|
83
179
|
# def up
|
84
180
|
# create_table :system_settings do |t|
|
85
181
|
# t.string :name
|
@@ -106,17 +202,18 @@ module ActiveRecord
|
|
106
202
|
#
|
107
203
|
# == Available transformations
|
108
204
|
#
|
205
|
+
# === Creation
|
206
|
+
#
|
207
|
+
# * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
|
208
|
+
# table having its name as the lexical order of the first two
|
209
|
+
# arguments. See
|
210
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
|
211
|
+
# details.
|
109
212
|
# * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
|
110
213
|
# makes the table object available to a block that can then add columns to it,
|
111
214
|
# following the same format as +add_column+. See example above. The options hash
|
112
215
|
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
|
113
216
|
# 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
217
|
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
|
121
218
|
# to the table called +table_name+
|
122
219
|
# named +column_name+ specified to be one of the following types:
|
@@ -127,21 +224,59 @@ module ActiveRecord
|
|
127
224
|
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
|
128
225
|
# <tt>{ limit: 50, null: false }</tt>) -- see
|
129
226
|
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
|
130
|
-
# * <tt>
|
131
|
-
#
|
132
|
-
#
|
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+.
|
227
|
+
# * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
|
228
|
+
# foreign key. +from_table+ is the table with the key column, +to_table+ contains
|
229
|
+
# the referenced primary key.
|
136
230
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
137
231
|
# with the name of the column. Other options include
|
138
232
|
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
139
233
|
# <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
|
140
234
|
# (e.g. <tt>{ order: { name: :desc } }</tt>).
|
141
|
-
# * <tt>
|
142
|
-
#
|
235
|
+
# * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
|
236
|
+
# +reference_name_id+ by default an integer. See
|
237
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
|
238
|
+
# * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
|
239
|
+
# and +updated_at+) columns to +table_name+.
|
240
|
+
#
|
241
|
+
# === Modification
|
242
|
+
#
|
243
|
+
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
|
244
|
+
# the column to a different type using the same parameters as add_column.
|
245
|
+
# * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
|
246
|
+
# default value for +column_name+ definded by +default+ on +table_name+.
|
247
|
+
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
|
248
|
+
# Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
|
249
|
+
# indicates whether the value can be +NULL+. See
|
250
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
|
251
|
+
# details.
|
252
|
+
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
253
|
+
# the table called +name+. It makes the table object available to a block that
|
254
|
+
# can then add/remove columns, indexes or foreign keys to it.
|
255
|
+
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
|
256
|
+
# a column but keeps the type and content.
|
257
|
+
# * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
|
258
|
+
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
|
259
|
+
# to +new_name+.
|
260
|
+
#
|
261
|
+
# === Deletion
|
262
|
+
#
|
263
|
+
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
|
264
|
+
# * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
|
265
|
+
# specified by the given arguments.
|
266
|
+
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
|
267
|
+
# named +column_name+ from the table called +table_name+.
|
268
|
+
# * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
|
269
|
+
# columns from the table definition.
|
270
|
+
# * <tt>remove_foreign_key(from_table, options_or_to_table)</tt>: Removes the
|
271
|
+
# given foreign key from the table called +table_name+.
|
272
|
+
# * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
|
273
|
+
# specified by +column_names+.
|
143
274
|
# * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
|
144
275
|
# specified by +index_name+.
|
276
|
+
# * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
|
277
|
+
# reference(s) on +table_name+ specified by +ref_name+.
|
278
|
+
# * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
|
279
|
+
# columns (+created_at+ and +updated_at+) from the table definition.
|
145
280
|
#
|
146
281
|
# == Irreversible transformations
|
147
282
|
#
|
@@ -165,24 +300,24 @@ module ActiveRecord
|
|
165
300
|
#
|
166
301
|
# rails generate migration add_fieldname_to_tablename fieldname:string
|
167
302
|
#
|
168
|
-
# This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
|
169
|
-
# class AddFieldnameToTablename < ActiveRecord::Migration
|
303
|
+
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
304
|
+
# class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
|
170
305
|
# def change
|
171
|
-
# add_column :tablenames, :
|
306
|
+
# add_column :tablenames, :fieldname, :string
|
172
307
|
# end
|
173
308
|
# end
|
174
309
|
#
|
175
310
|
# To run migrations against the currently configured database, use
|
176
|
-
# <tt>
|
311
|
+
# <tt>rails db:migrate</tt>. This will update the database by running all of the
|
177
312
|
# pending migrations, creating the <tt>schema_migrations</tt> table
|
178
313
|
# (see "About the schema_migrations table" section below) if missing. It will also
|
179
314
|
# invoke the db:schema:dump task, which will update your db/schema.rb file
|
180
315
|
# to match the structure of your database.
|
181
316
|
#
|
182
317
|
# To roll the database back to a previous migration version, use
|
183
|
-
# <tt>
|
318
|
+
# <tt>rails db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
|
184
319
|
# you wish to downgrade. Alternatively, you can also use the STEP option if you
|
185
|
-
# wish to rollback last few migrations. <tt>
|
320
|
+
# wish to rollback last few migrations. <tt>rails db:migrate STEP=2</tt> will rollback
|
186
321
|
# the latest two migrations.
|
187
322
|
#
|
188
323
|
# If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
|
@@ -197,7 +332,7 @@ module ActiveRecord
|
|
197
332
|
#
|
198
333
|
# Not all migrations change the schema. Some just fix the data:
|
199
334
|
#
|
200
|
-
# class RemoveEmptyTags < ActiveRecord::Migration
|
335
|
+
# class RemoveEmptyTags < ActiveRecord::Migration[5.0]
|
201
336
|
# def up
|
202
337
|
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
203
338
|
# end
|
@@ -210,7 +345,7 @@ module ActiveRecord
|
|
210
345
|
#
|
211
346
|
# Others remove columns when they migrate up instead of down:
|
212
347
|
#
|
213
|
-
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
|
348
|
+
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
|
214
349
|
# def up
|
215
350
|
# remove_column :items, :incomplete_items_count
|
216
351
|
# remove_column :items, :completed_items_count
|
@@ -224,7 +359,7 @@ module ActiveRecord
|
|
224
359
|
#
|
225
360
|
# And sometimes you need to do something in SQL not abstracted directly by migrations:
|
226
361
|
#
|
227
|
-
# class MakeJoinUnique < ActiveRecord::Migration
|
362
|
+
# class MakeJoinUnique < ActiveRecord::Migration[5.0]
|
228
363
|
# def up
|
229
364
|
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
|
230
365
|
# end
|
@@ -241,7 +376,7 @@ module ActiveRecord
|
|
241
376
|
# <tt>Base#reset_column_information</tt> in order to ensure that the model has the
|
242
377
|
# latest column data from after the new column was added. Example:
|
243
378
|
#
|
244
|
-
# class AddPeopleSalary < ActiveRecord::Migration
|
379
|
+
# class AddPeopleSalary < ActiveRecord::Migration[5.0]
|
245
380
|
# def up
|
246
381
|
# add_column :people, :salary, :integer
|
247
382
|
# Person.reset_column_information
|
@@ -275,21 +410,6 @@ module ActiveRecord
|
|
275
410
|
# The phrase "Updating salaries..." would then be printed, along with the
|
276
411
|
# benchmark for the block when the block completes.
|
277
412
|
#
|
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
413
|
# == Timestamped Migrations
|
294
414
|
#
|
295
415
|
# By default, Rails generates migrations that look like:
|
@@ -314,7 +434,7 @@ module ActiveRecord
|
|
314
434
|
# To define a reversible migration, define the +change+ method in your
|
315
435
|
# migration like this:
|
316
436
|
#
|
317
|
-
# class TenderloveMigration < ActiveRecord::Migration
|
437
|
+
# class TenderloveMigration < ActiveRecord::Migration[5.0]
|
318
438
|
# def change
|
319
439
|
# create_table(:horses) do |t|
|
320
440
|
# t.column :content, :text
|
@@ -344,7 +464,7 @@ module ActiveRecord
|
|
344
464
|
# can't execute inside a transaction though, and for these situations
|
345
465
|
# you can turn the automatic transactions off.
|
346
466
|
#
|
347
|
-
# class ChangeEnum < ActiveRecord::Migration
|
467
|
+
# class ChangeEnum < ActiveRecord::Migration[5.0]
|
348
468
|
# disable_ddl_transaction!
|
349
469
|
#
|
350
470
|
# def up
|
@@ -356,7 +476,34 @@ module ActiveRecord
|
|
356
476
|
# are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
|
357
477
|
class Migration
|
358
478
|
autoload :CommandRecorder, 'active_record/migration/command_recorder'
|
479
|
+
autoload :Compatibility, 'active_record/migration/compatibility'
|
480
|
+
|
481
|
+
# This must be defined before the inherited hook, below
|
482
|
+
class Current < Migration # :nodoc:
|
483
|
+
end
|
484
|
+
|
485
|
+
def self.inherited(subclass) # :nodoc:
|
486
|
+
super
|
487
|
+
if subclass.superclass == Migration
|
488
|
+
subclass.include Compatibility::Legacy
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def self.[](version)
|
493
|
+
version = version.to_s
|
494
|
+
name = "V#{version.tr('.', '_')}"
|
495
|
+
unless Compatibility.const_defined?(name)
|
496
|
+
versions = Compatibility.constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect }
|
497
|
+
raise "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
|
498
|
+
end
|
499
|
+
Compatibility.const_get(name)
|
500
|
+
end
|
359
501
|
|
502
|
+
def self.current_version
|
503
|
+
Rails.version.to_f
|
504
|
+
end
|
505
|
+
|
506
|
+
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
|
360
507
|
|
361
508
|
# This class is used to verify that all migrations have been run before
|
362
509
|
# loading a web page if config.active_record.migration_error is set to :page_load
|
@@ -388,17 +535,22 @@ module ActiveRecord
|
|
388
535
|
attr_accessor :delegate # :nodoc:
|
389
536
|
attr_accessor :disable_ddl_transaction # :nodoc:
|
390
537
|
|
538
|
+
def nearest_delegate # :nodoc:
|
539
|
+
delegate || superclass.nearest_delegate
|
540
|
+
end
|
541
|
+
|
542
|
+
# Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
|
391
543
|
def check_pending!(connection = Base.connection)
|
392
544
|
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
|
393
545
|
end
|
394
546
|
|
395
547
|
def load_schema_if_pending!
|
396
548
|
if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
|
397
|
-
#
|
549
|
+
# Roundtrip to Rake to allow plugins to hook into database initialization.
|
398
550
|
FileUtils.cd Rails.root do
|
399
551
|
current_config = Base.connection_config
|
400
552
|
Base.clear_all_connections!
|
401
|
-
system("bin/
|
553
|
+
system("bin/rails db:test:prepare")
|
402
554
|
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
|
403
555
|
Base.establish_connection(current_config)
|
404
556
|
end
|
@@ -413,7 +565,7 @@ module ActiveRecord
|
|
413
565
|
end
|
414
566
|
|
415
567
|
def method_missing(name, *args, &block) # :nodoc:
|
416
|
-
|
568
|
+
nearest_delegate.send(name, *args, &block)
|
417
569
|
end
|
418
570
|
|
419
571
|
def migrate(direction)
|
@@ -453,7 +605,7 @@ module ActiveRecord
|
|
453
605
|
# and create the table 'apples' on the way up, and the reverse
|
454
606
|
# on the way down.
|
455
607
|
#
|
456
|
-
# class FixTLMigration < ActiveRecord::Migration
|
608
|
+
# class FixTLMigration < ActiveRecord::Migration[5.0]
|
457
609
|
# def change
|
458
610
|
# revert do
|
459
611
|
# create_table(:horses) do |t|
|
@@ -470,9 +622,9 @@ module ActiveRecord
|
|
470
622
|
# Or equivalently, if +TenderloveMigration+ is defined as in the
|
471
623
|
# documentation for Migration:
|
472
624
|
#
|
473
|
-
# require_relative '
|
625
|
+
# require_relative '20121212123456_tenderlove_migration'
|
474
626
|
#
|
475
|
-
# class FixupTLMigration < ActiveRecord::Migration
|
627
|
+
# class FixupTLMigration < ActiveRecord::Migration[5.0]
|
476
628
|
# def change
|
477
629
|
# revert TenderloveMigration
|
478
630
|
#
|
@@ -486,13 +638,13 @@ module ActiveRecord
|
|
486
638
|
def revert(*migration_classes)
|
487
639
|
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
|
488
640
|
if block_given?
|
489
|
-
if
|
490
|
-
|
641
|
+
if connection.respond_to? :revert
|
642
|
+
connection.revert { yield }
|
491
643
|
else
|
492
|
-
recorder = CommandRecorder.new(
|
644
|
+
recorder = CommandRecorder.new(connection)
|
493
645
|
@connection = recorder
|
494
646
|
suppress_messages do
|
495
|
-
|
647
|
+
connection.revert { yield }
|
496
648
|
end
|
497
649
|
@connection = recorder.delegate
|
498
650
|
recorder.commands.each do |cmd, args, block|
|
@@ -503,7 +655,7 @@ module ActiveRecord
|
|
503
655
|
end
|
504
656
|
|
505
657
|
def reverting?
|
506
|
-
|
658
|
+
connection.respond_to?(:reverting) && connection.reverting
|
507
659
|
end
|
508
660
|
|
509
661
|
class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
|
@@ -525,7 +677,7 @@ module ActiveRecord
|
|
525
677
|
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
526
678
|
# even when migrating down:
|
527
679
|
#
|
528
|
-
# class SplitNameMigration < ActiveRecord::Migration
|
680
|
+
# class SplitNameMigration < ActiveRecord::Migration[5.0]
|
529
681
|
# def change
|
530
682
|
# add_column :users, :first_name, :string
|
531
683
|
# add_column :users, :last_name, :string
|
@@ -560,7 +712,7 @@ module ActiveRecord
|
|
560
712
|
revert { run(*migration_classes, direction: dir, revert: true) }
|
561
713
|
else
|
562
714
|
migration_classes.each do |migration_class|
|
563
|
-
migration_class.new.exec_migration(
|
715
|
+
migration_class.new.exec_migration(connection, dir)
|
564
716
|
end
|
565
717
|
end
|
566
718
|
end
|
@@ -649,10 +801,10 @@ module ActiveRecord
|
|
649
801
|
end
|
650
802
|
|
651
803
|
def method_missing(method, *arguments, &block)
|
652
|
-
arg_list = arguments.map
|
804
|
+
arg_list = arguments.map(&:inspect) * ', '
|
653
805
|
|
654
806
|
say_with_time "#{method}(#{arg_list})" do
|
655
|
-
unless
|
807
|
+
unless connection.respond_to? :revert
|
656
808
|
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
657
809
|
arguments[0] = proper_table_name(arguments.first, table_name_options)
|
658
810
|
if [:rename_table, :add_foreign_key].include?(method) ||
|
@@ -731,7 +883,9 @@ module ActiveRecord
|
|
731
883
|
end
|
732
884
|
end
|
733
885
|
|
734
|
-
|
886
|
+
# Builds a hash for use in ActiveRecord::Migration#proper_table_name using
|
887
|
+
# the Active Record object's table_name prefix and suffix
|
888
|
+
def table_name_options(config = ActiveRecord::Base) #:nodoc:
|
735
889
|
{
|
736
890
|
table_name_prefix: config.table_name_prefix,
|
737
891
|
table_name_suffix: config.table_name_suffix
|
@@ -823,7 +977,7 @@ module ActiveRecord
|
|
823
977
|
new(:up, migrations, target_version).migrate
|
824
978
|
end
|
825
979
|
|
826
|
-
def down(migrations_paths, target_version = nil
|
980
|
+
def down(migrations_paths, target_version = nil)
|
827
981
|
migrations = migrations(migrations_paths)
|
828
982
|
migrations.select! { |m| yield m } if block_given?
|
829
983
|
|
@@ -843,10 +997,12 @@ module ActiveRecord
|
|
843
997
|
end
|
844
998
|
|
845
999
|
def get_all_versions(connection = Base.connection)
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
1000
|
+
ActiveSupport::Deprecation.silence do
|
1001
|
+
if connection.table_exists?(schema_migrations_table_name)
|
1002
|
+
SchemaMigration.all.map { |x| x.version.to_i }.sort
|
1003
|
+
else
|
1004
|
+
[]
|
1005
|
+
end
|
850
1006
|
end
|
851
1007
|
end
|
852
1008
|
|
@@ -862,32 +1018,30 @@ module ActiveRecord
|
|
862
1018
|
migrations(migrations_paths).any?
|
863
1019
|
end
|
864
1020
|
|
865
|
-
def last_version
|
866
|
-
last_migration.version
|
867
|
-
end
|
868
|
-
|
869
1021
|
def last_migration #:nodoc:
|
870
1022
|
migrations(migrations_paths).last || NullMigration.new
|
871
1023
|
end
|
872
1024
|
|
873
1025
|
def migrations_paths
|
874
1026
|
@migrations_paths ||= ['db/migrate']
|
875
|
-
# just to not break things if someone uses:
|
1027
|
+
# just to not break things if someone uses: migrations_path = some_string
|
876
1028
|
Array(@migrations_paths)
|
877
1029
|
end
|
878
1030
|
|
879
|
-
def
|
880
|
-
|
1031
|
+
def match_to_migration_filename?(filename) # :nodoc:
|
1032
|
+
File.basename(filename) =~ Migration::MigrationFilenameRegexp
|
881
1033
|
end
|
882
1034
|
|
883
1035
|
def parse_migration_filename(filename) # :nodoc:
|
884
|
-
File.basename(filename).scan(
|
1036
|
+
File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
|
885
1037
|
end
|
886
1038
|
|
887
1039
|
def migrations(paths)
|
888
1040
|
paths = Array(paths)
|
889
1041
|
|
890
|
-
|
1042
|
+
files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
|
1043
|
+
|
1044
|
+
migrations = files.map do |file|
|
891
1045
|
version, name, scope = parse_migration_filename(file)
|
892
1046
|
raise IllegalMigrationNameError.new(file) unless version
|
893
1047
|
version = version.to_i
|
@@ -899,30 +1053,6 @@ module ActiveRecord
|
|
899
1053
|
migrations.sort_by(&:version)
|
900
1054
|
end
|
901
1055
|
|
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
1056
|
private
|
927
1057
|
|
928
1058
|
def move(direction, migrations_paths, steps)
|
@@ -960,32 +1090,18 @@ module ActiveRecord
|
|
960
1090
|
alias :current :current_migration
|
961
1091
|
|
962
1092
|
def run
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
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
|
1093
|
+
if use_advisory_lock?
|
1094
|
+
with_advisory_lock { run_without_lock }
|
1095
|
+
else
|
1096
|
+
run_without_lock
|
972
1097
|
end
|
973
1098
|
end
|
974
1099
|
|
975
1100
|
def migrate
|
976
|
-
if
|
977
|
-
|
978
|
-
|
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
|
1101
|
+
if use_advisory_lock?
|
1102
|
+
with_advisory_lock { migrate_without_lock }
|
1103
|
+
else
|
1104
|
+
migrate_without_lock
|
989
1105
|
end
|
990
1106
|
end
|
991
1107
|
|
@@ -1010,10 +1126,45 @@ module ActiveRecord
|
|
1010
1126
|
end
|
1011
1127
|
|
1012
1128
|
def migrated
|
1013
|
-
@migrated_versions
|
1129
|
+
@migrated_versions || load_migrated
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
def load_migrated
|
1133
|
+
@migrated_versions = Set.new(self.class.get_all_versions)
|
1014
1134
|
end
|
1015
1135
|
|
1016
1136
|
private
|
1137
|
+
|
1138
|
+
def run_without_lock
|
1139
|
+
migration = migrations.detect { |m| m.version == @target_version }
|
1140
|
+
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
1141
|
+
unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
|
1142
|
+
begin
|
1143
|
+
execute_migration_in_transaction(migration, @direction)
|
1144
|
+
rescue => e
|
1145
|
+
canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
|
1146
|
+
raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def migrate_without_lock
|
1152
|
+
if !target && @target_version && @target_version > 0
|
1153
|
+
raise UnknownMigrationVersionError.new(@target_version)
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
runnable.each do |migration|
|
1157
|
+
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
1158
|
+
|
1159
|
+
begin
|
1160
|
+
execute_migration_in_transaction(migration, @direction)
|
1161
|
+
rescue => e
|
1162
|
+
canceled_msg = use_transaction?(migration) ? "this and " : ""
|
1163
|
+
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1017
1168
|
def ran?(migration)
|
1018
1169
|
migrated.include?(migration.version.to_i)
|
1019
1170
|
end
|
@@ -1075,5 +1226,25 @@ module ActiveRecord
|
|
1075
1226
|
def use_transaction?(migration)
|
1076
1227
|
!migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
|
1077
1228
|
end
|
1229
|
+
|
1230
|
+
def use_advisory_lock?
|
1231
|
+
Base.connection.supports_advisory_locks?
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
def with_advisory_lock
|
1235
|
+
lock_id = generate_migrator_advisory_lock_id
|
1236
|
+
got_lock = Base.connection.get_advisory_lock(lock_id)
|
1237
|
+
raise ConcurrentMigrationError unless got_lock
|
1238
|
+
load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
|
1239
|
+
yield
|
1240
|
+
ensure
|
1241
|
+
Base.connection.release_advisory_lock(lock_id) if got_lock
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
MIGRATOR_SALT = 2053462845
|
1245
|
+
def generate_migrator_advisory_lock_id
|
1246
|
+
db_name_hash = Zlib.crc32(Base.connection.current_database)
|
1247
|
+
MIGRATOR_SALT * db_name_hash
|
1248
|
+
end
|
1078
1249
|
end
|
1079
1250
|
end
|