activerecord 4.2.11.3 → 5.0.7.2
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 +4 -4
- data/CHANGELOG.md +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- 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/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- 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 +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -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 -4
- 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 +31 -17
- 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 +56 -19
- 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 +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- 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/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- 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 +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- 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 +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -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/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- 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 +15 -14
- data/lib/active_record/type/time.rb +10 -16
- 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 +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- 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
@@ -6,45 +6,100 @@ module ActiveRecord
|
|
6
6
|
# We can then redefine how certain data types may be handled in the schema dumper on the
|
7
7
|
# Adapter level by over-writing this code inside the database specific adapters
|
8
8
|
module ColumnDumper
|
9
|
-
def column_spec(column
|
10
|
-
spec = prepare_column_options(column,
|
11
|
-
|
9
|
+
def column_spec(column)
|
10
|
+
spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
|
11
|
+
spec[:name] = column.name.inspect
|
12
|
+
spec[:type] = schema_type(column).to_s
|
12
13
|
spec
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
+
def column_spec_for_primary_key(column)
|
17
|
+
return {} if default_primary_key?(column)
|
18
|
+
spec = { id: schema_type(column).inspect }
|
19
|
+
spec.merge!(prepare_column_options(column).except!(:null))
|
20
|
+
end
|
21
|
+
|
22
|
+
# This can be overridden on an Adapter level basis to support other
|
16
23
|
# extended datatypes (Example: Adding an array option in the
|
17
|
-
#
|
18
|
-
def prepare_column_options(column
|
24
|
+
# PostgreSQL::ColumnDumper)
|
25
|
+
def prepare_column_options(column)
|
19
26
|
spec = {}
|
20
|
-
spec[:name] = column.name.inspect
|
21
|
-
spec[:type] = column.type.to_s
|
22
|
-
spec[:null] = 'false' unless column.null
|
23
27
|
|
24
|
-
limit = column
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
if limit = schema_limit(column)
|
29
|
+
spec[:limit] = limit
|
30
|
+
end
|
31
|
+
|
32
|
+
if precision = schema_precision(column)
|
33
|
+
spec[:precision] = precision
|
34
|
+
end
|
35
|
+
|
36
|
+
if scale = schema_scale(column)
|
37
|
+
spec[:scale] = scale
|
38
|
+
end
|
28
39
|
|
29
40
|
default = schema_default(column) if column.has_default?
|
30
41
|
spec[:default] = default unless default.nil?
|
31
42
|
|
43
|
+
spec[:null] = 'false' unless column.null
|
44
|
+
|
45
|
+
if collation = schema_collation(column)
|
46
|
+
spec[:collation] = collation
|
47
|
+
end
|
48
|
+
|
49
|
+
spec[:comment] = column.comment.inspect if column.comment.present?
|
50
|
+
|
32
51
|
spec
|
33
52
|
end
|
34
53
|
|
35
54
|
# Lists the valid migration options
|
36
55
|
def migration_keys
|
37
|
-
[:name, :limit, :precision, :scale, :default, :null]
|
56
|
+
[:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
|
38
57
|
end
|
39
58
|
|
40
59
|
private
|
41
60
|
|
61
|
+
def default_primary_key?(column)
|
62
|
+
schema_type(column) == :integer
|
63
|
+
end
|
64
|
+
|
65
|
+
def schema_type(column)
|
66
|
+
if column.bigint?
|
67
|
+
:bigint
|
68
|
+
else
|
69
|
+
column.type
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def schema_limit(column)
|
74
|
+
limit = column.limit unless column.bigint?
|
75
|
+
limit.inspect if limit && limit != native_database_types[column.type][:limit]
|
76
|
+
end
|
77
|
+
|
78
|
+
def schema_precision(column)
|
79
|
+
column.precision.inspect if column.precision
|
80
|
+
end
|
81
|
+
|
82
|
+
def schema_scale(column)
|
83
|
+
column.scale.inspect if column.scale
|
84
|
+
end
|
85
|
+
|
42
86
|
def schema_default(column)
|
43
|
-
|
44
|
-
|
45
|
-
|
87
|
+
type = lookup_cast_type_from_column(column)
|
88
|
+
default = type.deserialize(column.default)
|
89
|
+
if default.nil?
|
90
|
+
schema_expression(column)
|
91
|
+
else
|
92
|
+
type.type_cast_for_schema(default)
|
46
93
|
end
|
47
94
|
end
|
95
|
+
|
96
|
+
def schema_expression(column)
|
97
|
+
"-> { #{column.default_function.inspect} }" if column.default_function
|
98
|
+
end
|
99
|
+
|
100
|
+
def schema_collation(column)
|
101
|
+
column.collation.inspect if column.collation
|
102
|
+
end
|
48
103
|
end
|
49
104
|
end
|
50
105
|
end
|
@@ -14,15 +14,24 @@ module ActiveRecord
|
|
14
14
|
{}
|
15
15
|
end
|
16
16
|
|
17
|
+
def table_options(table_name)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the table comment that's stored in database metadata.
|
22
|
+
def table_comment(table_name)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
17
26
|
# Truncates a table alias according to the limits of the current adapter.
|
18
27
|
def table_alias_for(table_name)
|
19
28
|
table_name[0...table_alias_length].tr('.', '_')
|
20
29
|
end
|
21
30
|
|
22
31
|
# Returns the relation names useable to back Active Record models.
|
23
|
-
# For most adapters this means all tables and views.
|
32
|
+
# For most adapters this means all #tables and #views.
|
24
33
|
def data_sources
|
25
|
-
tables
|
34
|
+
tables | views
|
26
35
|
end
|
27
36
|
|
28
37
|
# Checks to see if the data source +name+ exists on the database.
|
@@ -33,6 +42,11 @@ module ActiveRecord
|
|
33
42
|
data_sources.include?(name.to_s)
|
34
43
|
end
|
35
44
|
|
45
|
+
# Returns an array of table names defined in the database.
|
46
|
+
def tables(name = nil)
|
47
|
+
raise NotImplementedError, "#tables is not implemented"
|
48
|
+
end
|
49
|
+
|
36
50
|
# Checks to see if the table +table_name+ exists on the database.
|
37
51
|
#
|
38
52
|
# table_exists?(:developers)
|
@@ -41,6 +55,19 @@ module ActiveRecord
|
|
41
55
|
tables.include?(table_name.to_s)
|
42
56
|
end
|
43
57
|
|
58
|
+
# Returns an array of view names defined in the database.
|
59
|
+
def views
|
60
|
+
raise NotImplementedError, "#views is not implemented"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Checks to see if the view +view_name+ exists on the database.
|
64
|
+
#
|
65
|
+
# view_exists?(:ebooks)
|
66
|
+
#
|
67
|
+
def view_exists?(view_name)
|
68
|
+
views.include?(view_name.to_s)
|
69
|
+
end
|
70
|
+
|
44
71
|
# Returns an array of indexes for the given table.
|
45
72
|
# def indexes(table_name, name = nil) end
|
46
73
|
|
@@ -60,18 +87,19 @@ module ActiveRecord
|
|
60
87
|
#
|
61
88
|
def index_exists?(table_name, column_name, options = {})
|
62
89
|
column_names = Array(column_name).map(&:to_s)
|
63
|
-
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
|
64
90
|
checks = []
|
65
|
-
checks << lambda { |i| i.name == index_name }
|
66
91
|
checks << lambda { |i| i.columns == column_names }
|
67
92
|
checks << lambda { |i| i.unique } if options[:unique]
|
93
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
68
94
|
|
69
95
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
70
96
|
end
|
71
97
|
|
72
98
|
# Returns an array of Column objects for the table specified by +table_name+.
|
73
99
|
# See the concrete implementation for details on the expected parameter values.
|
74
|
-
def columns(table_name)
|
100
|
+
def columns(table_name)
|
101
|
+
raise NotImplementedError, "#columns is not implemented"
|
102
|
+
end
|
75
103
|
|
76
104
|
# Checks to see if a column exists in a given table.
|
77
105
|
#
|
@@ -89,19 +117,27 @@ module ActiveRecord
|
|
89
117
|
#
|
90
118
|
def column_exists?(table_name, column_name, type = nil, options = {})
|
91
119
|
column_name = column_name.to_s
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
120
|
+
checks = []
|
121
|
+
checks << lambda { |c| c.name == column_name }
|
122
|
+
checks << lambda { |c| c.type == type } if type
|
123
|
+
(migration_keys - [:name]).each do |attr|
|
124
|
+
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
125
|
+
end
|
126
|
+
|
127
|
+
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns just a table's primary key
|
131
|
+
def primary_key(table_name)
|
132
|
+
pk = primary_keys(table_name)
|
133
|
+
pk = pk.first unless pk.size > 1
|
134
|
+
pk
|
99
135
|
end
|
100
136
|
|
101
137
|
# Creates a new table with the name +table_name+. +table_name+ may either
|
102
138
|
# be a String or a Symbol.
|
103
139
|
#
|
104
|
-
# There are two ways to work with
|
140
|
+
# There are two ways to work with #create_table. You can use the block
|
105
141
|
# form or the regular form, like this:
|
106
142
|
#
|
107
143
|
# === Block form
|
@@ -133,13 +169,16 @@ module ActiveRecord
|
|
133
169
|
# The +options+ hash can include the following keys:
|
134
170
|
# [<tt>:id</tt>]
|
135
171
|
# Whether to automatically add a primary key column. Defaults to true.
|
136
|
-
# Join tables for
|
172
|
+
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
173
|
+
#
|
174
|
+
# A Symbol can be used to specify the type of the generated primary key column.
|
137
175
|
# [<tt>:primary_key</tt>]
|
138
176
|
# The name of the primary key, if one is to be added automatically.
|
139
177
|
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
140
178
|
#
|
141
179
|
# Note that Active Record models will automatically detect their
|
142
|
-
# primary key. This can be avoided by using
|
180
|
+
# primary key. This can be avoided by using
|
181
|
+
# {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
|
143
182
|
# to define the key explicitly.
|
144
183
|
#
|
145
184
|
# [<tt>:options</tt>]
|
@@ -161,7 +200,7 @@ module ActiveRecord
|
|
161
200
|
# generates:
|
162
201
|
#
|
163
202
|
# CREATE TABLE suppliers (
|
164
|
-
# id int
|
203
|
+
# id int auto_increment PRIMARY KEY
|
165
204
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
166
205
|
#
|
167
206
|
# ====== Rename the primary key column
|
@@ -173,10 +212,23 @@ module ActiveRecord
|
|
173
212
|
# generates:
|
174
213
|
#
|
175
214
|
# CREATE TABLE objects (
|
176
|
-
# guid int
|
215
|
+
# guid int auto_increment PRIMARY KEY,
|
177
216
|
# name varchar(80)
|
178
217
|
# )
|
179
218
|
#
|
219
|
+
# ====== Change the primary key column type
|
220
|
+
#
|
221
|
+
# create_table(:tags, id: :string) do |t|
|
222
|
+
# t.column :label, :string
|
223
|
+
# end
|
224
|
+
#
|
225
|
+
# generates:
|
226
|
+
#
|
227
|
+
# CREATE TABLE tags (
|
228
|
+
# id varchar PRIMARY KEY,
|
229
|
+
# label varchar
|
230
|
+
# )
|
231
|
+
#
|
180
232
|
# ====== Do not add a primary key column
|
181
233
|
#
|
182
234
|
# create_table(:categories_suppliers, id: false) do |t|
|
@@ -202,33 +254,41 @@ module ActiveRecord
|
|
202
254
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
203
255
|
#
|
204
256
|
# See also TableDefinition#column for details on how to create columns.
|
205
|
-
def create_table(table_name,
|
206
|
-
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
|
257
|
+
def create_table(table_name, comment: nil, **options)
|
258
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
207
259
|
|
208
260
|
if options[:id] != false && !options[:as]
|
209
261
|
pk = options.fetch(:primary_key) do
|
210
262
|
Base.get_primary_key table_name.to_s.singularize
|
211
263
|
end
|
212
264
|
|
213
|
-
|
265
|
+
if pk.is_a?(Array)
|
266
|
+
td.primary_keys pk
|
267
|
+
else
|
268
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options
|
269
|
+
end
|
214
270
|
end
|
215
271
|
|
216
272
|
yield td if block_given?
|
217
273
|
|
218
|
-
if options[:force] &&
|
274
|
+
if options[:force] && data_source_exists?(table_name)
|
219
275
|
drop_table(table_name, options)
|
220
276
|
end
|
221
277
|
|
222
278
|
result = execute schema_creation.accept td
|
223
279
|
|
224
280
|
unless supports_indexes_in_create?
|
225
|
-
td.indexes.
|
281
|
+
td.indexes.each do |column_name, index_options|
|
226
282
|
add_index(table_name, column_name, index_options)
|
227
283
|
end
|
228
284
|
end
|
229
285
|
|
230
|
-
|
231
|
-
|
286
|
+
if supports_comments? && !supports_comments_in_create?
|
287
|
+
change_table_comment(table_name, comment) if comment.present?
|
288
|
+
|
289
|
+
td.columns.each do |column|
|
290
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
291
|
+
end
|
232
292
|
end
|
233
293
|
|
234
294
|
result
|
@@ -253,7 +313,7 @@ module ActiveRecord
|
|
253
313
|
# Set to true to drop the table before creating it.
|
254
314
|
# Defaults to false.
|
255
315
|
#
|
256
|
-
# Note that
|
316
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
257
317
|
# its block form to do so yourself:
|
258
318
|
#
|
259
319
|
# create_join_table :products, :categories do |t|
|
@@ -277,22 +337,23 @@ module ActiveRecord
|
|
277
337
|
|
278
338
|
column_options = options.delete(:column_options) || {}
|
279
339
|
column_options.reverse_merge!(null: false)
|
340
|
+
type = column_options.delete(:type) || :integer
|
280
341
|
|
281
342
|
t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
|
282
343
|
|
283
344
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
284
|
-
td.
|
285
|
-
td.
|
345
|
+
td.send type, t1_column, column_options
|
346
|
+
td.send type, t2_column, column_options
|
286
347
|
yield td if block_given?
|
287
348
|
end
|
288
349
|
end
|
289
350
|
|
290
351
|
# Drops the join table specified by the given arguments.
|
291
|
-
# See
|
352
|
+
# See #create_join_table for details.
|
292
353
|
#
|
293
354
|
# Although this command ignores the block if one is given, it can be helpful
|
294
355
|
# to provide one in a migration's +change+ method so it can be reverted.
|
295
|
-
# In that case, the block will be used by create_join_table.
|
356
|
+
# In that case, the block will be used by #create_join_table.
|
296
357
|
def drop_join_table(table_1, table_2, options = {})
|
297
358
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
298
359
|
drop_table(join_table_name)
|
@@ -310,7 +371,7 @@ module ActiveRecord
|
|
310
371
|
# [<tt>:bulk</tt>]
|
311
372
|
# Set this to true to make this a bulk alter query, such as
|
312
373
|
#
|
313
|
-
# ALTER TABLE `users` ADD COLUMN age INT
|
374
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
314
375
|
#
|
315
376
|
# Defaults to false.
|
316
377
|
#
|
@@ -391,16 +452,90 @@ module ActiveRecord
|
|
391
452
|
# [<tt>:force</tt>]
|
392
453
|
# Set to +:cascade+ to drop dependent objects as well.
|
393
454
|
# Defaults to false.
|
455
|
+
# [<tt>:if_exists</tt>]
|
456
|
+
# Set to +true+ to only drop the table if it exists.
|
457
|
+
# Defaults to false.
|
394
458
|
#
|
395
459
|
# Although this command ignores most +options+ and the block if one is given,
|
396
460
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
397
|
-
# In that case, +options+ and the block will be used by create_table.
|
461
|
+
# In that case, +options+ and the block will be used by #create_table.
|
398
462
|
def drop_table(table_name, options = {})
|
399
|
-
execute "DROP TABLE #{quote_table_name(table_name)}"
|
463
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
400
464
|
end
|
401
465
|
|
402
|
-
#
|
403
|
-
#
|
466
|
+
# Add a new +type+ column named +column_name+ to +table_name+.
|
467
|
+
#
|
468
|
+
# The +type+ parameter is normally one of the migrations native types,
|
469
|
+
# which is one of the following:
|
470
|
+
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
471
|
+
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
472
|
+
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
473
|
+
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
474
|
+
#
|
475
|
+
# You may use a type not in this list as long as it is supported by your
|
476
|
+
# database (for example, "polygon" in MySQL), but this will not be database
|
477
|
+
# agnostic and should usually be avoided.
|
478
|
+
#
|
479
|
+
# Available options are (none of these exists by default):
|
480
|
+
# * <tt>:limit</tt> -
|
481
|
+
# Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
|
482
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
|
483
|
+
# * <tt>:default</tt> -
|
484
|
+
# The column's default value. Use nil for NULL.
|
485
|
+
# * <tt>:null</tt> -
|
486
|
+
# Allows or disallows +NULL+ values in the column. This option could
|
487
|
+
# have been named <tt>:null_allowed</tt>.
|
488
|
+
# * <tt>:precision</tt> -
|
489
|
+
# Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
490
|
+
# * <tt>:scale</tt> -
|
491
|
+
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
492
|
+
#
|
493
|
+
# Note: The precision is the total number of significant digits
|
494
|
+
# and the scale is the number of digits that can be stored following
|
495
|
+
# the decimal point. For example, the number 123.45 has a precision of 5
|
496
|
+
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
497
|
+
# range from -999.99 to 999.99.
|
498
|
+
#
|
499
|
+
# Please be aware of different RDBMS implementations behavior with
|
500
|
+
# <tt>:decimal</tt> columns:
|
501
|
+
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
502
|
+
# <tt>:precision</tt>, and makes no comments about the requirements of
|
503
|
+
# <tt>:precision</tt>.
|
504
|
+
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
|
505
|
+
# Default is (10,0).
|
506
|
+
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
507
|
+
# <tt>:scale</tt> [0..infinity]. No default.
|
508
|
+
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
|
509
|
+
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
510
|
+
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
511
|
+
# Default is (38,0).
|
512
|
+
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
513
|
+
# Default unknown.
|
514
|
+
# * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
515
|
+
# Default (38,0).
|
516
|
+
#
|
517
|
+
# == Examples
|
518
|
+
#
|
519
|
+
# add_column(:users, :picture, :binary, limit: 2.megabytes)
|
520
|
+
# # ALTER TABLE "users" ADD "picture" blob(2097152)
|
521
|
+
#
|
522
|
+
# add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
|
523
|
+
# # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
|
524
|
+
#
|
525
|
+
# add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
|
526
|
+
# # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
|
527
|
+
#
|
528
|
+
# add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
|
529
|
+
# # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
|
530
|
+
#
|
531
|
+
# # While :scale defaults to zero on most databases, it
|
532
|
+
# # probably wouldn't hurt to include it.
|
533
|
+
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
534
|
+
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
535
|
+
#
|
536
|
+
# # Defines a column with a database-specific type.
|
537
|
+
# add_column(:shapes, :triangle, 'polygon')
|
538
|
+
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
404
539
|
def add_column(table_name, column_name, type, options = {})
|
405
540
|
at = create_alter_table table_name
|
406
541
|
at.add_column(column_name, type, options)
|
@@ -448,11 +583,16 @@ module ActiveRecord
|
|
448
583
|
#
|
449
584
|
# change_column_default(:users, :email, nil)
|
450
585
|
#
|
451
|
-
|
586
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
587
|
+
# reversible in migration:
|
588
|
+
#
|
589
|
+
# change_column_default(:posts, :state, from: nil, to: "draft")
|
590
|
+
#
|
591
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
452
592
|
raise NotImplementedError, "change_column_default is not implemented"
|
453
593
|
end
|
454
594
|
|
455
|
-
# Sets or removes a
|
595
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
456
596
|
# indicates whether the value can be +NULL+. For example
|
457
597
|
#
|
458
598
|
# change_column_null(:users, :nickname, false)
|
@@ -464,7 +604,7 @@ module ActiveRecord
|
|
464
604
|
# allows them to be +NULL+ (drops the constraint).
|
465
605
|
#
|
466
606
|
# The method accepts an optional fourth argument to replace existing
|
467
|
-
#
|
607
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
468
608
|
# constraint if needed, since otherwise those rows would not be valid.
|
469
609
|
#
|
470
610
|
# Please note the fourth argument does not set a column's default.
|
@@ -518,6 +658,8 @@ module ActiveRecord
|
|
518
658
|
#
|
519
659
|
# CREATE INDEX by_name ON accounts(name(10))
|
520
660
|
#
|
661
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
662
|
+
#
|
521
663
|
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
522
664
|
#
|
523
665
|
# generates:
|
@@ -565,7 +707,7 @@ module ActiveRecord
|
|
565
707
|
#
|
566
708
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
567
709
|
#
|
568
|
-
# Note: only supported by MySQL.
|
710
|
+
# Note: only supported by MySQL.
|
569
711
|
def add_index(table_name, column_name, options = {})
|
570
712
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
571
713
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -573,15 +715,15 @@ module ActiveRecord
|
|
573
715
|
|
574
716
|
# Removes the given index from the table.
|
575
717
|
#
|
576
|
-
# Removes the +
|
718
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
577
719
|
#
|
578
|
-
# remove_index :accounts, :
|
720
|
+
# remove_index :accounts, :branch_id
|
579
721
|
#
|
580
|
-
# Removes the index
|
722
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
581
723
|
#
|
582
724
|
# remove_index :accounts, column: :branch_id
|
583
725
|
#
|
584
|
-
# Removes the index
|
726
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
585
727
|
#
|
586
728
|
# remove_index :accounts, column: [:branch_id, :party_id]
|
587
729
|
#
|
@@ -590,10 +732,7 @@ module ActiveRecord
|
|
590
732
|
# remove_index :accounts, name: :by_branch_party
|
591
733
|
#
|
592
734
|
def remove_index(table_name, options = {})
|
593
|
-
|
594
|
-
end
|
595
|
-
|
596
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
735
|
+
index_name = index_name_for_remove(table_name, options)
|
597
736
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
598
737
|
end
|
599
738
|
|
@@ -623,7 +762,7 @@ module ActiveRecord
|
|
623
762
|
raise ArgumentError, "You must specify the index name"
|
624
763
|
end
|
625
764
|
else
|
626
|
-
index_name(table_name,
|
765
|
+
index_name(table_name, index_name_options(options))
|
627
766
|
end
|
628
767
|
end
|
629
768
|
|
@@ -640,17 +779,20 @@ module ActiveRecord
|
|
640
779
|
# Adds a reference. The reference column is an integer by default,
|
641
780
|
# the <tt>:type</tt> option can be used to specify a different type.
|
642
781
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
643
|
-
#
|
782
|
+
# #add_reference and #add_belongs_to are acceptable.
|
644
783
|
#
|
645
784
|
# The +options+ hash can include the following keys:
|
646
785
|
# [<tt>:type</tt>]
|
647
786
|
# The reference column type. Defaults to +:integer+.
|
648
787
|
# [<tt>:index</tt>]
|
649
|
-
# Add an appropriate index. Defaults to
|
788
|
+
# Add an appropriate index. Defaults to true.
|
789
|
+
# See #add_index for usage of this option.
|
650
790
|
# [<tt>:foreign_key</tt>]
|
651
|
-
# Add an appropriate foreign key. Defaults to false.
|
791
|
+
# Add an appropriate foreign key constraint. Defaults to false.
|
652
792
|
# [<tt>:polymorphic</tt>]
|
653
|
-
#
|
793
|
+
# Whether an additional +_type+ column should be added. Defaults to false.
|
794
|
+
# [<tt>:null</tt>]
|
795
|
+
# Whether the column allows nulls. Defaults to true.
|
654
796
|
#
|
655
797
|
# ====== Create a user_id integer column
|
656
798
|
#
|
@@ -664,28 +806,29 @@ module ActiveRecord
|
|
664
806
|
#
|
665
807
|
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
666
808
|
#
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
809
|
+
# ====== Create a supplier_id column with a unique index
|
810
|
+
#
|
811
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
812
|
+
#
|
813
|
+
# ====== Create a supplier_id column with a named index
|
814
|
+
#
|
815
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
816
|
+
#
|
817
|
+
# ====== Create a supplier_id column and appropriate foreign key
|
818
|
+
#
|
819
|
+
# add_reference(:products, :supplier, foreign_key: true)
|
820
|
+
#
|
821
|
+
# ====== Create a supplier_id column and a foreign key to the firms table
|
822
|
+
#
|
823
|
+
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
824
|
+
#
|
825
|
+
def add_reference(table_name, *args)
|
826
|
+
ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
|
684
827
|
end
|
685
828
|
alias :add_belongs_to :add_reference
|
686
829
|
|
687
830
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
688
|
-
#
|
831
|
+
# #remove_reference and #remove_belongs_to are acceptable.
|
689
832
|
#
|
690
833
|
# ====== Remove the reference
|
691
834
|
#
|
@@ -699,19 +842,24 @@ module ActiveRecord
|
|
699
842
|
#
|
700
843
|
# remove_reference(:products, :user, index: true, foreign_key: true)
|
701
844
|
#
|
702
|
-
def remove_reference(table_name, ref_name,
|
703
|
-
if
|
704
|
-
|
705
|
-
|
845
|
+
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
846
|
+
if foreign_key
|
847
|
+
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
848
|
+
if foreign_key.is_a?(Hash)
|
849
|
+
foreign_key_options = foreign_key
|
850
|
+
else
|
851
|
+
foreign_key_options = { to_table: reference_name }
|
852
|
+
end
|
853
|
+
remove_foreign_key(table_name, **foreign_key_options)
|
706
854
|
end
|
707
855
|
|
708
856
|
remove_column(table_name, "#{ref_name}_id")
|
709
|
-
remove_column(table_name, "#{ref_name}_type") if
|
857
|
+
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
710
858
|
end
|
711
859
|
alias :remove_belongs_to :remove_reference
|
712
860
|
|
713
861
|
# Returns an array of foreign keys for the given table.
|
714
|
-
# The foreign keys are represented as
|
862
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
715
863
|
def foreign_keys(table_name)
|
716
864
|
raise NotImplementedError, "foreign_keys is not implemented"
|
717
865
|
end
|
@@ -729,11 +877,11 @@ module ActiveRecord
|
|
729
877
|
#
|
730
878
|
# generates:
|
731
879
|
#
|
732
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
880
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
733
881
|
#
|
734
882
|
# ====== Creating a foreign key on a specific column
|
735
883
|
#
|
736
|
-
# add_foreign_key :articles, :users, column: :author_id, primary_key:
|
884
|
+
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
737
885
|
#
|
738
886
|
# generates:
|
739
887
|
#
|
@@ -745,7 +893,7 @@ module ActiveRecord
|
|
745
893
|
#
|
746
894
|
# generates:
|
747
895
|
#
|
748
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
896
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
749
897
|
#
|
750
898
|
# The +options+ hash can include the following keys:
|
751
899
|
# [<tt>:column</tt>]
|
@@ -755,28 +903,23 @@ module ActiveRecord
|
|
755
903
|
# [<tt>:name</tt>]
|
756
904
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
757
905
|
# [<tt>:on_delete</tt>]
|
758
|
-
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade
|
906
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
759
907
|
# [<tt>:on_update</tt>]
|
760
|
-
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade
|
908
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
761
909
|
def add_foreign_key(from_table, to_table, options = {})
|
762
910
|
return unless supports_foreign_keys?
|
763
911
|
|
764
|
-
options
|
765
|
-
|
766
|
-
options = {
|
767
|
-
column: options[:column],
|
768
|
-
primary_key: options[:primary_key],
|
769
|
-
name: foreign_key_name(from_table, options),
|
770
|
-
on_delete: options[:on_delete],
|
771
|
-
on_update: options[:on_update]
|
772
|
-
}
|
912
|
+
options = foreign_key_options(from_table, to_table, options)
|
773
913
|
at = create_alter_table from_table
|
774
914
|
at.add_foreign_key to_table, options
|
775
915
|
|
776
916
|
execute schema_creation.accept(at)
|
777
917
|
end
|
778
918
|
|
779
|
-
# Removes the given foreign key from the table.
|
919
|
+
# Removes the given foreign key from the table. Any option parameters provided
|
920
|
+
# will be used to re-add the foreign key in case of a migration rollback.
|
921
|
+
# It is recommended that you provide any options used when creating the foreign
|
922
|
+
# key so that the migration can be reverted properly.
|
780
923
|
#
|
781
924
|
# Removes the foreign key on +accounts.branch_id+.
|
782
925
|
#
|
@@ -790,24 +933,11 @@ module ActiveRecord
|
|
790
933
|
#
|
791
934
|
# remove_foreign_key :accounts, name: :special_fk_name
|
792
935
|
#
|
936
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
793
937
|
def remove_foreign_key(from_table, options_or_to_table = {})
|
794
938
|
return unless supports_foreign_keys?
|
795
939
|
|
796
|
-
|
797
|
-
options = options_or_to_table
|
798
|
-
else
|
799
|
-
options = { column: foreign_key_column_for(options_or_to_table) }
|
800
|
-
end
|
801
|
-
|
802
|
-
fk_name_to_delete = options.fetch(:name) do
|
803
|
-
fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
|
804
|
-
|
805
|
-
if fk_to_delete
|
806
|
-
fk_to_delete.name
|
807
|
-
else
|
808
|
-
raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
|
809
|
-
end
|
810
|
-
end
|
940
|
+
fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
|
811
941
|
|
812
942
|
at = create_alter_table from_table
|
813
943
|
at.drop_foreign_key fk_name_to_delete
|
@@ -815,6 +945,31 @@ module ActiveRecord
|
|
815
945
|
execute schema_creation.accept(at)
|
816
946
|
end
|
817
947
|
|
948
|
+
# Checks to see if a foreign key exists on a table for a given foreign key definition.
|
949
|
+
#
|
950
|
+
# # Check a foreign key exists
|
951
|
+
# foreign_key_exists?(:accounts, :branches)
|
952
|
+
#
|
953
|
+
# # Check a foreign key on a specified column exists
|
954
|
+
# foreign_key_exists?(:accounts, column: :owner_id)
|
955
|
+
#
|
956
|
+
# # Check a foreign key with a custom name exists
|
957
|
+
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
958
|
+
#
|
959
|
+
def foreign_key_exists?(from_table, options_or_to_table = {})
|
960
|
+
foreign_key_for(from_table, options_or_to_table).present?
|
961
|
+
end
|
962
|
+
|
963
|
+
def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
|
964
|
+
return unless supports_foreign_keys?
|
965
|
+
foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
|
966
|
+
end
|
967
|
+
|
968
|
+
def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
|
969
|
+
foreign_key_for(from_table, options_or_to_table) or \
|
970
|
+
raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
|
971
|
+
end
|
972
|
+
|
818
973
|
def foreign_key_column_for(table_name) # :nodoc:
|
819
974
|
prefix = Base.table_name_prefix
|
820
975
|
suffix = Base.table_name_suffix
|
@@ -822,12 +977,29 @@ module ActiveRecord
|
|
822
977
|
"#{name.singularize}_id"
|
823
978
|
end
|
824
979
|
|
980
|
+
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
981
|
+
options = options.dup
|
982
|
+
options[:column] ||= foreign_key_column_for(to_table)
|
983
|
+
options[:name] ||= foreign_key_name(from_table, options)
|
984
|
+
options
|
985
|
+
end
|
986
|
+
|
825
987
|
def dump_schema_information #:nodoc:
|
826
|
-
|
988
|
+
versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
|
989
|
+
insert_versions_sql(versions) if versions.any?
|
990
|
+
end
|
827
991
|
|
828
|
-
|
829
|
-
|
830
|
-
|
992
|
+
def insert_versions_sql(versions) # :nodoc:
|
993
|
+
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
994
|
+
|
995
|
+
if versions.is_a?(Array)
|
996
|
+
sql = "INSERT INTO #{sm_table} (version) VALUES\n"
|
997
|
+
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
998
|
+
sql << ";\n\n"
|
999
|
+
sql
|
1000
|
+
else
|
1001
|
+
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
1002
|
+
end
|
831
1003
|
end
|
832
1004
|
|
833
1005
|
# Should not be called normally, but this operation is non-destructive.
|
@@ -836,7 +1008,15 @@ module ActiveRecord
|
|
836
1008
|
ActiveRecord::SchemaMigration.create_table
|
837
1009
|
end
|
838
1010
|
|
839
|
-
def
|
1011
|
+
def initialize_internal_metadata_table
|
1012
|
+
ActiveRecord::InternalMetadata.create_table
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1016
|
+
{ primary_key: true }
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def assume_migrated_upto_version(version, migrations_paths)
|
840
1020
|
migrations_paths = Array(migrations_paths)
|
841
1021
|
version = version.to_i
|
842
1022
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
@@ -847,22 +1027,27 @@ module ActiveRecord
|
|
847
1027
|
end
|
848
1028
|
|
849
1029
|
unless migrated.include?(version)
|
850
|
-
execute "INSERT INTO #{sm_table} (version) VALUES (
|
1030
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
851
1031
|
end
|
852
1032
|
|
853
|
-
|
854
|
-
|
855
|
-
if
|
856
|
-
raise "Duplicate migration #{
|
857
|
-
|
858
|
-
|
859
|
-
|
1033
|
+
inserting = (versions - migrated).select {|v| v < version}
|
1034
|
+
if inserting.any?
|
1035
|
+
if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
|
1036
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1037
|
+
end
|
1038
|
+
if supports_multi_insert?
|
1039
|
+
execute insert_versions_sql(inserting)
|
1040
|
+
else
|
1041
|
+
inserting.each do |v|
|
1042
|
+
execute insert_versions_sql(v)
|
1043
|
+
end
|
860
1044
|
end
|
861
1045
|
end
|
862
1046
|
end
|
863
1047
|
|
864
1048
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
865
|
-
|
1049
|
+
type = type.to_sym if type
|
1050
|
+
if native = native_database_types[type]
|
866
1051
|
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
867
1052
|
|
868
1053
|
if type == :decimal # ignore limit, use precision and scale
|
@@ -878,6 +1063,12 @@ module ActiveRecord
|
|
878
1063
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
879
1064
|
end
|
880
1065
|
|
1066
|
+
elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
|
1067
|
+
if (0..6) === precision
|
1068
|
+
column_type_sql << "(#{precision})"
|
1069
|
+
else
|
1070
|
+
raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
1071
|
+
end
|
881
1072
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
882
1073
|
column_type_sql << "(#{limit})"
|
883
1074
|
end
|
@@ -898,14 +1089,14 @@ module ActiveRecord
|
|
898
1089
|
columns
|
899
1090
|
end
|
900
1091
|
|
901
|
-
include TimestampDefaultDeprecation
|
902
1092
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
903
|
-
# Additional options (like
|
1093
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
904
1094
|
#
|
905
|
-
# add_timestamps(:suppliers, null:
|
1095
|
+
# add_timestamps(:suppliers, null: true)
|
906
1096
|
#
|
907
1097
|
def add_timestamps(table_name, options = {})
|
908
|
-
|
1098
|
+
options[:null] = false if options[:null].nil?
|
1099
|
+
|
909
1100
|
add_column table_name, :created_at, :datetime, options
|
910
1101
|
add_column table_name, :updated_at, :datetime, options
|
911
1102
|
end
|
@@ -923,15 +1114,15 @@ module ActiveRecord
|
|
923
1114
|
Table.new(table_name, base)
|
924
1115
|
end
|
925
1116
|
|
926
|
-
def add_index_options(table_name, column_name,
|
927
|
-
column_names =
|
928
|
-
index_name = index_name(table_name, column: column_names)
|
1117
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
1118
|
+
column_names = index_column_names(column_name)
|
929
1119
|
|
930
1120
|
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
931
1121
|
|
932
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
933
1122
|
index_type = options[:type].to_s if options.key?(:type)
|
1123
|
+
index_type ||= options[:unique] ? "UNIQUE" : ""
|
934
1124
|
index_name = options[:name].to_s if options.key?(:name)
|
1125
|
+
index_name ||= index_name(table_name, column_names)
|
935
1126
|
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
936
1127
|
|
937
1128
|
if options.key?(:algorithm)
|
@@ -949,60 +1140,92 @@ module ActiveRecord
|
|
949
1140
|
if index_name.length > max_index_length
|
950
1141
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
|
951
1142
|
end
|
952
|
-
if
|
1143
|
+
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
|
953
1144
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
954
1145
|
end
|
955
1146
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
956
1147
|
|
957
|
-
[index_name, index_type, index_columns, index_options, algorithm, using]
|
1148
|
+
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def options_include_default?(options)
|
1152
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
# Changes the comment for a table or removes it if +nil+.
|
1156
|
+
def change_table_comment(table_name, comment)
|
1157
|
+
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
# Changes the comment for a column or removes it if +nil+.
|
1161
|
+
def change_column_comment(table_name, column_name, comment) #:nodoc:
|
1162
|
+
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
958
1163
|
end
|
959
1164
|
|
960
1165
|
protected
|
961
|
-
|
962
|
-
|
1166
|
+
|
1167
|
+
def add_index_sort_order(quoted_columns, **options)
|
1168
|
+
if order = options[:order]
|
963
1169
|
case order
|
964
1170
|
when Hash
|
965
|
-
|
1171
|
+
order = order.symbolize_keys
|
1172
|
+
quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
|
966
1173
|
when String
|
967
|
-
|
1174
|
+
quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
|
968
1175
|
end
|
969
1176
|
end
|
970
1177
|
|
971
|
-
|
1178
|
+
quoted_columns
|
972
1179
|
end
|
973
1180
|
|
974
1181
|
# Overridden by the MySQL adapter for supporting index lengths
|
975
|
-
def
|
976
|
-
option_strings = Hash[column_names.map {|name| [name, '']}]
|
977
|
-
|
978
|
-
# add index sort order if supported
|
1182
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
979
1183
|
if supports_index_sort_order?
|
980
|
-
|
1184
|
+
quoted_columns = add_index_sort_order(quoted_columns, options)
|
981
1185
|
end
|
982
1186
|
|
983
|
-
|
1187
|
+
quoted_columns
|
984
1188
|
end
|
985
1189
|
|
986
|
-
def
|
987
|
-
|
1190
|
+
def quoted_columns_for_index(column_names, **options)
|
1191
|
+
return [column_names] if column_names.is_a?(String)
|
1192
|
+
|
1193
|
+
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
1194
|
+
add_options_for_index_columns(quoted_columns, options).values
|
988
1195
|
end
|
989
1196
|
|
990
1197
|
def index_name_for_remove(table_name, options = {})
|
991
|
-
|
1198
|
+
return options[:name] if can_remove_index_by_name?(options)
|
992
1199
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
options_without_column.delete :column
|
997
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
1200
|
+
# if the adapter doesn't support the indexes call the best we can do
|
1201
|
+
# is return the default index name for the options provided
|
1202
|
+
return index_name(table_name, options) unless respond_to?(:indexes)
|
998
1203
|
|
999
|
-
|
1000
|
-
end
|
1204
|
+
checks = []
|
1001
1205
|
|
1002
|
-
|
1206
|
+
if options.is_a?(Hash)
|
1207
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1208
|
+
column_names = index_column_names(options[:column])
|
1209
|
+
else
|
1210
|
+
column_names = index_column_names(options)
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
if column_names.present?
|
1214
|
+
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1003
1215
|
end
|
1004
1216
|
|
1005
|
-
|
1217
|
+
raise ArgumentError, "No name or columns specified" if checks.none?
|
1218
|
+
|
1219
|
+
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
1220
|
+
|
1221
|
+
if matching_indexes.count > 1
|
1222
|
+
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1223
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1224
|
+
elsif matching_indexes.none?
|
1225
|
+
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1226
|
+
else
|
1227
|
+
matching_indexes.first.name
|
1228
|
+
end
|
1006
1229
|
end
|
1007
1230
|
|
1008
1231
|
def rename_table_indexes(table_name, new_name)
|
@@ -1028,12 +1251,28 @@ module ActiveRecord
|
|
1028
1251
|
end
|
1029
1252
|
|
1030
1253
|
private
|
1031
|
-
def create_table_definition(
|
1032
|
-
TableDefinition.new
|
1254
|
+
def create_table_definition(*args)
|
1255
|
+
TableDefinition.new(*args)
|
1033
1256
|
end
|
1034
1257
|
|
1035
1258
|
def create_alter_table(name)
|
1036
|
-
AlterTable.new create_table_definition(name
|
1259
|
+
AlterTable.new create_table_definition(name)
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
def index_column_names(column_names)
|
1263
|
+
if column_names.is_a?(String) && /\W/ === column_names
|
1264
|
+
column_names
|
1265
|
+
else
|
1266
|
+
Array(column_names)
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
def index_name_options(column_names) # :nodoc:
|
1271
|
+
if column_names.is_a?(String) && /\W/ === column_names
|
1272
|
+
column_names = column_names.scan(/\w+/).join('_')
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
{ column: column_names }
|
1037
1276
|
end
|
1038
1277
|
|
1039
1278
|
def foreign_key_name(table_name, options) # :nodoc:
|
@@ -1044,11 +1283,23 @@ module ActiveRecord
|
|
1044
1283
|
end
|
1045
1284
|
end
|
1046
1285
|
|
1047
|
-
def validate_index_length!(table_name, new_name)
|
1286
|
+
def validate_index_length!(table_name, new_name) # :nodoc:
|
1048
1287
|
if new_name.length > allowed_index_name_length
|
1049
1288
|
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
1050
1289
|
end
|
1051
1290
|
end
|
1291
|
+
|
1292
|
+
def extract_new_default_value(default_or_changes)
|
1293
|
+
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1294
|
+
default_or_changes[:to]
|
1295
|
+
else
|
1296
|
+
default_or_changes
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
def can_remove_index_by_name?(options)
|
1301
|
+
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1302
|
+
end
|
1052
1303
|
end
|
1053
1304
|
end
|
1054
1305
|
end
|