activerecord 4.2.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1537 -789
- 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/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- 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 +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- 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 +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- 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 +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- 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 +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- 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 +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- 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 +125 -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 +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- 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 -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/type_map_initializer.rb +17 -5
- 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/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- 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 +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -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 +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -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/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- 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/relation.rb +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- 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 +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +21 -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.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- 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 +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- 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 +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- 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 -101
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'active_record/migration/join_table'
|
2
|
+
require 'active_support/core_ext/string/access'
|
3
|
+
require 'digest'
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters # :nodoc:
|
@@ -12,11 +14,39 @@ module ActiveRecord
|
|
12
14
|
{}
|
13
15
|
end
|
14
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
|
+
|
15
26
|
# Truncates a table alias according to the limits of the current adapter.
|
16
27
|
def table_alias_for(table_name)
|
17
28
|
table_name[0...table_alias_length].tr('.', '_')
|
18
29
|
end
|
19
30
|
|
31
|
+
# Returns the relation names useable to back Active Record models.
|
32
|
+
# For most adapters this means all #tables and #views.
|
33
|
+
def data_sources
|
34
|
+
tables | views
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks to see if the data source +name+ exists on the database.
|
38
|
+
#
|
39
|
+
# data_source_exists?(:ebooks)
|
40
|
+
#
|
41
|
+
def data_source_exists?(name)
|
42
|
+
data_sources.include?(name.to_s)
|
43
|
+
end
|
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
|
+
|
20
50
|
# Checks to see if the table +table_name+ exists on the database.
|
21
51
|
#
|
22
52
|
# table_exists?(:developers)
|
@@ -25,6 +55,19 @@ module ActiveRecord
|
|
25
55
|
tables.include?(table_name.to_s)
|
26
56
|
end
|
27
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
|
+
|
28
71
|
# Returns an array of indexes for the given table.
|
29
72
|
# def indexes(table_name, name = nil) end
|
30
73
|
|
@@ -44,18 +87,19 @@ module ActiveRecord
|
|
44
87
|
#
|
45
88
|
def index_exists?(table_name, column_name, options = {})
|
46
89
|
column_names = Array(column_name).map(&:to_s)
|
47
|
-
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
|
48
90
|
checks = []
|
49
|
-
checks << lambda { |i| i.name == index_name }
|
50
91
|
checks << lambda { |i| i.columns == column_names }
|
51
92
|
checks << lambda { |i| i.unique } if options[:unique]
|
93
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
52
94
|
|
53
95
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
54
96
|
end
|
55
97
|
|
56
98
|
# Returns an array of Column objects for the table specified by +table_name+.
|
57
99
|
# See the concrete implementation for details on the expected parameter values.
|
58
|
-
def columns(table_name)
|
100
|
+
def columns(table_name)
|
101
|
+
raise NotImplementedError, "#columns is not implemented"
|
102
|
+
end
|
59
103
|
|
60
104
|
# Checks to see if a column exists in a given table.
|
61
105
|
#
|
@@ -73,19 +117,32 @@ module ActiveRecord
|
|
73
117
|
#
|
74
118
|
def column_exists?(table_name, column_name, type = nil, options = {})
|
75
119
|
column_name = column_name.to_s
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
+
pks = primary_keys(table_name)
|
133
|
+
warn <<-WARNING.strip_heredoc if pks.count > 1
|
134
|
+
WARNING: Rails does not support composite primary key.
|
135
|
+
|
136
|
+
#{table_name} has composite primary key. Composite primary key is ignored.
|
137
|
+
WARNING
|
138
|
+
|
139
|
+
pks.first if pks.one?
|
83
140
|
end
|
84
141
|
|
85
142
|
# Creates a new table with the name +table_name+. +table_name+ may either
|
86
143
|
# be a String or a Symbol.
|
87
144
|
#
|
88
|
-
# There are two ways to work with
|
145
|
+
# There are two ways to work with #create_table. You can use the block
|
89
146
|
# form or the regular form, like this:
|
90
147
|
#
|
91
148
|
# === Block form
|
@@ -117,13 +174,16 @@ module ActiveRecord
|
|
117
174
|
# The +options+ hash can include the following keys:
|
118
175
|
# [<tt>:id</tt>]
|
119
176
|
# Whether to automatically add a primary key column. Defaults to true.
|
120
|
-
# Join tables for
|
177
|
+
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
178
|
+
#
|
179
|
+
# A Symbol can be used to specify the type of the generated primary key column.
|
121
180
|
# [<tt>:primary_key</tt>]
|
122
181
|
# The name of the primary key, if one is to be added automatically.
|
123
182
|
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
124
183
|
#
|
125
184
|
# Note that Active Record models will automatically detect their
|
126
|
-
# primary key. This can be avoided by using
|
185
|
+
# primary key. This can be avoided by using
|
186
|
+
# {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
|
127
187
|
# to define the key explicitly.
|
128
188
|
#
|
129
189
|
# [<tt>:options</tt>]
|
@@ -145,7 +205,7 @@ module ActiveRecord
|
|
145
205
|
# generates:
|
146
206
|
#
|
147
207
|
# CREATE TABLE suppliers (
|
148
|
-
# id int
|
208
|
+
# id int auto_increment PRIMARY KEY
|
149
209
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
150
210
|
#
|
151
211
|
# ====== Rename the primary key column
|
@@ -157,10 +217,23 @@ module ActiveRecord
|
|
157
217
|
# generates:
|
158
218
|
#
|
159
219
|
# CREATE TABLE objects (
|
160
|
-
# guid int
|
220
|
+
# guid int auto_increment PRIMARY KEY,
|
161
221
|
# name varchar(80)
|
162
222
|
# )
|
163
223
|
#
|
224
|
+
# ====== Change the primary key column type
|
225
|
+
#
|
226
|
+
# create_table(:tags, id: :string) do |t|
|
227
|
+
# t.column :label, :string
|
228
|
+
# end
|
229
|
+
#
|
230
|
+
# generates:
|
231
|
+
#
|
232
|
+
# CREATE TABLE tags (
|
233
|
+
# id varchar PRIMARY KEY,
|
234
|
+
# label varchar
|
235
|
+
# )
|
236
|
+
#
|
164
237
|
# ====== Do not add a primary key column
|
165
238
|
#
|
166
239
|
# create_table(:categories_suppliers, id: false) do |t|
|
@@ -186,25 +259,43 @@ module ActiveRecord
|
|
186
259
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
187
260
|
#
|
188
261
|
# See also TableDefinition#column for details on how to create columns.
|
189
|
-
def create_table(table_name,
|
190
|
-
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
|
262
|
+
def create_table(table_name, comment: nil, **options)
|
263
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
191
264
|
|
192
265
|
if options[:id] != false && !options[:as]
|
193
266
|
pk = options.fetch(:primary_key) do
|
194
267
|
Base.get_primary_key table_name.to_s.singularize
|
195
268
|
end
|
196
269
|
|
197
|
-
|
270
|
+
if pk.is_a?(Array)
|
271
|
+
td.primary_keys pk
|
272
|
+
else
|
273
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options
|
274
|
+
end
|
198
275
|
end
|
199
276
|
|
200
277
|
yield td if block_given?
|
201
278
|
|
202
|
-
if options[:force] &&
|
279
|
+
if options[:force] && data_source_exists?(table_name)
|
203
280
|
drop_table(table_name, options)
|
204
281
|
end
|
205
282
|
|
206
283
|
result = execute schema_creation.accept td
|
207
|
-
|
284
|
+
|
285
|
+
unless supports_indexes_in_create?
|
286
|
+
td.indexes.each_pair do |column_name, index_options|
|
287
|
+
add_index(table_name, column_name, index_options)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
if supports_comments? && !supports_comments_in_create?
|
292
|
+
change_table_comment(table_name, comment) if comment
|
293
|
+
|
294
|
+
td.columns.each do |column|
|
295
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
208
299
|
result
|
209
300
|
end
|
210
301
|
|
@@ -227,7 +318,7 @@ module ActiveRecord
|
|
227
318
|
# Set to true to drop the table before creating it.
|
228
319
|
# Defaults to false.
|
229
320
|
#
|
230
|
-
# Note that
|
321
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
231
322
|
# its block form to do so yourself:
|
232
323
|
#
|
233
324
|
# create_join_table :products, :categories do |t|
|
@@ -251,22 +342,23 @@ module ActiveRecord
|
|
251
342
|
|
252
343
|
column_options = options.delete(:column_options) || {}
|
253
344
|
column_options.reverse_merge!(null: false)
|
345
|
+
type = column_options.delete(:type) || :integer
|
254
346
|
|
255
347
|
t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
|
256
348
|
|
257
349
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
258
|
-
td.
|
259
|
-
td.
|
350
|
+
td.send type, t1_column, column_options
|
351
|
+
td.send type, t2_column, column_options
|
260
352
|
yield td if block_given?
|
261
353
|
end
|
262
354
|
end
|
263
355
|
|
264
356
|
# Drops the join table specified by the given arguments.
|
265
|
-
# See
|
357
|
+
# See #create_join_table for details.
|
266
358
|
#
|
267
359
|
# Although this command ignores the block if one is given, it can be helpful
|
268
360
|
# to provide one in a migration's +change+ method so it can be reverted.
|
269
|
-
# In that case, the block will be used by create_join_table.
|
361
|
+
# In that case, the block will be used by #create_join_table.
|
270
362
|
def drop_join_table(table_1, table_2, options = {})
|
271
363
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
272
364
|
drop_table(join_table_name)
|
@@ -284,7 +376,7 @@ module ActiveRecord
|
|
284
376
|
# [<tt>:bulk</tt>]
|
285
377
|
# Set this to true to make this a bulk alter query, such as
|
286
378
|
#
|
287
|
-
# ALTER TABLE `users` ADD COLUMN age INT
|
379
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
288
380
|
#
|
289
381
|
# Defaults to false.
|
290
382
|
#
|
@@ -365,16 +457,90 @@ module ActiveRecord
|
|
365
457
|
# [<tt>:force</tt>]
|
366
458
|
# Set to +:cascade+ to drop dependent objects as well.
|
367
459
|
# Defaults to false.
|
460
|
+
# [<tt>:if_exists</tt>]
|
461
|
+
# Set to +true+ to only drop the table if it exists.
|
462
|
+
# Defaults to false.
|
368
463
|
#
|
369
464
|
# Although this command ignores most +options+ and the block if one is given,
|
370
465
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
371
|
-
# In that case, +options+ and the block will be used by create_table.
|
466
|
+
# In that case, +options+ and the block will be used by #create_table.
|
372
467
|
def drop_table(table_name, options = {})
|
373
|
-
execute "DROP TABLE #{quote_table_name(table_name)}"
|
468
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
374
469
|
end
|
375
470
|
|
376
|
-
#
|
377
|
-
#
|
471
|
+
# Add a new +type+ column named +column_name+ to +table_name+.
|
472
|
+
#
|
473
|
+
# The +type+ parameter is normally one of the migrations native types,
|
474
|
+
# which is one of the following:
|
475
|
+
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
476
|
+
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
477
|
+
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
478
|
+
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
479
|
+
#
|
480
|
+
# You may use a type not in this list as long as it is supported by your
|
481
|
+
# database (for example, "polygon" in MySQL), but this will not be database
|
482
|
+
# agnostic and should usually be avoided.
|
483
|
+
#
|
484
|
+
# Available options are (none of these exists by default):
|
485
|
+
# * <tt>:limit</tt> -
|
486
|
+
# Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
|
487
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
|
488
|
+
# * <tt>:default</tt> -
|
489
|
+
# The column's default value. Use nil for NULL.
|
490
|
+
# * <tt>:null</tt> -
|
491
|
+
# Allows or disallows +NULL+ values in the column. This option could
|
492
|
+
# have been named <tt>:null_allowed</tt>.
|
493
|
+
# * <tt>:precision</tt> -
|
494
|
+
# Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
495
|
+
# * <tt>:scale</tt> -
|
496
|
+
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
497
|
+
#
|
498
|
+
# Note: The precision is the total number of significant digits
|
499
|
+
# and the scale is the number of digits that can be stored following
|
500
|
+
# the decimal point. For example, the number 123.45 has a precision of 5
|
501
|
+
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
502
|
+
# range from -999.99 to 999.99.
|
503
|
+
#
|
504
|
+
# Please be aware of different RDBMS implementations behavior with
|
505
|
+
# <tt>:decimal</tt> columns:
|
506
|
+
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
507
|
+
# <tt>:precision</tt>, and makes no comments about the requirements of
|
508
|
+
# <tt>:precision</tt>.
|
509
|
+
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
|
510
|
+
# Default is (10,0).
|
511
|
+
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
512
|
+
# <tt>:scale</tt> [0..infinity]. No default.
|
513
|
+
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
|
514
|
+
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
515
|
+
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
516
|
+
# Default is (38,0).
|
517
|
+
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
518
|
+
# Default unknown.
|
519
|
+
# * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
520
|
+
# Default (38,0).
|
521
|
+
#
|
522
|
+
# == Examples
|
523
|
+
#
|
524
|
+
# add_column(:users, :picture, :binary, limit: 2.megabytes)
|
525
|
+
# # ALTER TABLE "users" ADD "picture" blob(2097152)
|
526
|
+
#
|
527
|
+
# add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
|
528
|
+
# # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
|
529
|
+
#
|
530
|
+
# add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
|
531
|
+
# # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
|
532
|
+
#
|
533
|
+
# add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
|
534
|
+
# # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
|
535
|
+
#
|
536
|
+
# # While :scale defaults to zero on most databases, it
|
537
|
+
# # probably wouldn't hurt to include it.
|
538
|
+
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
539
|
+
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
540
|
+
#
|
541
|
+
# # Defines a column with a database-specific type.
|
542
|
+
# add_column(:shapes, :triangle, 'polygon')
|
543
|
+
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
378
544
|
def add_column(table_name, column_name, type, options = {})
|
379
545
|
at = create_alter_table table_name
|
380
546
|
at.add_column(column_name, type, options)
|
@@ -422,11 +588,16 @@ module ActiveRecord
|
|
422
588
|
#
|
423
589
|
# change_column_default(:users, :email, nil)
|
424
590
|
#
|
425
|
-
|
591
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
592
|
+
# reversible in migration:
|
593
|
+
#
|
594
|
+
# change_column_default(:posts, :state, from: nil, to: "draft")
|
595
|
+
#
|
596
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
426
597
|
raise NotImplementedError, "change_column_default is not implemented"
|
427
598
|
end
|
428
599
|
|
429
|
-
# Sets or removes a
|
600
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
430
601
|
# indicates whether the value can be +NULL+. For example
|
431
602
|
#
|
432
603
|
# change_column_null(:users, :nickname, false)
|
@@ -438,7 +609,7 @@ module ActiveRecord
|
|
438
609
|
# allows them to be +NULL+ (drops the constraint).
|
439
610
|
#
|
440
611
|
# The method accepts an optional fourth argument to replace existing
|
441
|
-
#
|
612
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
442
613
|
# constraint if needed, since otherwise those rows would not be valid.
|
443
614
|
#
|
444
615
|
# Please note the fourth argument does not set a column's default.
|
@@ -492,6 +663,8 @@ module ActiveRecord
|
|
492
663
|
#
|
493
664
|
# CREATE INDEX by_name ON accounts(name(10))
|
494
665
|
#
|
666
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
667
|
+
#
|
495
668
|
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
496
669
|
#
|
497
670
|
# generates:
|
@@ -518,6 +691,8 @@ module ActiveRecord
|
|
518
691
|
#
|
519
692
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
520
693
|
#
|
694
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
|
695
|
+
#
|
521
696
|
# ====== Creating an index with a specific method
|
522
697
|
#
|
523
698
|
# add_index(:developers, :name, using: 'btree')
|
@@ -537,7 +712,7 @@ module ActiveRecord
|
|
537
712
|
#
|
538
713
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
539
714
|
#
|
540
|
-
# Note: only supported by MySQL.
|
715
|
+
# Note: only supported by MySQL.
|
541
716
|
def add_index(table_name, column_name, options = {})
|
542
717
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
543
718
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -545,15 +720,15 @@ module ActiveRecord
|
|
545
720
|
|
546
721
|
# Removes the given index from the table.
|
547
722
|
#
|
548
|
-
# Removes the +
|
723
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
549
724
|
#
|
550
|
-
# remove_index :accounts, :
|
725
|
+
# remove_index :accounts, :branch_id
|
551
726
|
#
|
552
|
-
# Removes the index
|
727
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
553
728
|
#
|
554
729
|
# remove_index :accounts, column: :branch_id
|
555
730
|
#
|
556
|
-
# Removes the index
|
731
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
557
732
|
#
|
558
733
|
# remove_index :accounts, column: [:branch_id, :party_id]
|
559
734
|
#
|
@@ -562,10 +737,7 @@ module ActiveRecord
|
|
562
737
|
# remove_index :accounts, name: :by_branch_party
|
563
738
|
#
|
564
739
|
def remove_index(table_name, options = {})
|
565
|
-
|
566
|
-
end
|
567
|
-
|
568
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
740
|
+
index_name = index_name_for_remove(table_name, options)
|
569
741
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
570
742
|
end
|
571
743
|
|
@@ -576,9 +748,8 @@ module ActiveRecord
|
|
576
748
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
577
749
|
#
|
578
750
|
def rename_index(table_name, old_name, new_name)
|
579
|
-
|
580
|
-
|
581
|
-
end
|
751
|
+
validate_index_length!(table_name, new_name)
|
752
|
+
|
582
753
|
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
583
754
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
584
755
|
return unless old_index_def
|
@@ -610,10 +781,23 @@ module ActiveRecord
|
|
610
781
|
indexes(table_name).detect { |i| i.name == index_name }
|
611
782
|
end
|
612
783
|
|
613
|
-
# Adds a reference.
|
614
|
-
#
|
615
|
-
# a
|
616
|
-
#
|
784
|
+
# Adds a reference. The reference column is an integer by default,
|
785
|
+
# the <tt>:type</tt> option can be used to specify a different type.
|
786
|
+
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
787
|
+
# #add_reference and #add_belongs_to are acceptable.
|
788
|
+
#
|
789
|
+
# The +options+ hash can include the following keys:
|
790
|
+
# [<tt>:type</tt>]
|
791
|
+
# The reference column type. Defaults to +:integer+.
|
792
|
+
# [<tt>:index</tt>]
|
793
|
+
# Add an appropriate index. Defaults to false.
|
794
|
+
# See #add_index for usage of this option.
|
795
|
+
# [<tt>:foreign_key</tt>]
|
796
|
+
# Add an appropriate foreign key constraint. Defaults to false.
|
797
|
+
# [<tt>:polymorphic</tt>]
|
798
|
+
# Whether an additional +_type+ column should be added. Defaults to false.
|
799
|
+
# [<tt>:null</tt>]
|
800
|
+
# Whether the column allows nulls. Defaults to true.
|
617
801
|
#
|
618
802
|
# ====== Create a user_id integer column
|
619
803
|
#
|
@@ -623,26 +807,33 @@ module ActiveRecord
|
|
623
807
|
#
|
624
808
|
# add_reference(:products, :user, type: :string)
|
625
809
|
#
|
626
|
-
# ====== Create
|
810
|
+
# ====== Create supplier_id, supplier_type columns and appropriate index
|
811
|
+
#
|
812
|
+
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
627
813
|
#
|
628
|
-
#
|
814
|
+
# ====== Create a supplier_id column with a unique index
|
629
815
|
#
|
630
|
-
#
|
816
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
631
817
|
#
|
632
|
-
#
|
818
|
+
# ====== Create a supplier_id column with a named index
|
633
819
|
#
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
820
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
821
|
+
#
|
822
|
+
# ====== Create a supplier_id column and appropriate foreign key
|
823
|
+
#
|
824
|
+
# add_reference(:products, :supplier, foreign_key: true)
|
825
|
+
#
|
826
|
+
# ====== Create a supplier_id column and a foreign key to the firms table
|
827
|
+
#
|
828
|
+
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
829
|
+
#
|
830
|
+
def add_reference(table_name, *args)
|
831
|
+
ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
|
641
832
|
end
|
642
833
|
alias :add_belongs_to :add_reference
|
643
834
|
|
644
835
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
645
|
-
#
|
836
|
+
# #remove_reference and #remove_belongs_to are acceptable.
|
646
837
|
#
|
647
838
|
# ====== Remove the reference
|
648
839
|
#
|
@@ -652,14 +843,23 @@ module ActiveRecord
|
|
652
843
|
#
|
653
844
|
# remove_reference(:products, :supplier, polymorphic: true)
|
654
845
|
#
|
846
|
+
# ====== Remove the reference with a foreign key
|
847
|
+
#
|
848
|
+
# remove_reference(:products, :user, index: true, foreign_key: true)
|
849
|
+
#
|
655
850
|
def remove_reference(table_name, ref_name, options = {})
|
851
|
+
if options[:foreign_key]
|
852
|
+
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
853
|
+
remove_foreign_key(table_name, reference_name)
|
854
|
+
end
|
855
|
+
|
656
856
|
remove_column(table_name, "#{ref_name}_id")
|
657
857
|
remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
|
658
858
|
end
|
659
859
|
alias :remove_belongs_to :remove_reference
|
660
860
|
|
661
861
|
# Returns an array of foreign keys for the given table.
|
662
|
-
# The foreign keys are represented as
|
862
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
663
863
|
def foreign_keys(table_name)
|
664
864
|
raise NotImplementedError, "foreign_keys is not implemented"
|
665
865
|
end
|
@@ -668,8 +868,8 @@ module ActiveRecord
|
|
668
868
|
# +to_table+ contains the referenced primary key.
|
669
869
|
#
|
670
870
|
# The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
|
671
|
-
# +identifier+ is a 10 character long
|
672
|
-
# the <tt>:name</tt> option.
|
871
|
+
# +identifier+ is a 10 character long string which is deterministically generated from the
|
872
|
+
# +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
|
673
873
|
#
|
674
874
|
# ====== Creating a simple foreign key
|
675
875
|
#
|
@@ -677,7 +877,7 @@ module ActiveRecord
|
|
677
877
|
#
|
678
878
|
# generates:
|
679
879
|
#
|
680
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
880
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
681
881
|
#
|
682
882
|
# ====== Creating a foreign key on a specific column
|
683
883
|
#
|
@@ -693,7 +893,7 @@ module ActiveRecord
|
|
693
893
|
#
|
694
894
|
# generates:
|
695
895
|
#
|
696
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
896
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
697
897
|
#
|
698
898
|
# The +options+ hash can include the following keys:
|
699
899
|
# [<tt>:column</tt>]
|
@@ -703,28 +903,23 @@ module ActiveRecord
|
|
703
903
|
# [<tt>:name</tt>]
|
704
904
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
705
905
|
# [<tt>:on_delete</tt>]
|
706
|
-
# 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+
|
707
907
|
# [<tt>:on_update</tt>]
|
708
|
-
# 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+
|
709
909
|
def add_foreign_key(from_table, to_table, options = {})
|
710
910
|
return unless supports_foreign_keys?
|
711
911
|
|
712
|
-
options
|
713
|
-
|
714
|
-
options = {
|
715
|
-
column: options[:column],
|
716
|
-
primary_key: options[:primary_key],
|
717
|
-
name: foreign_key_name(from_table, options),
|
718
|
-
on_delete: options[:on_delete],
|
719
|
-
on_update: options[:on_update]
|
720
|
-
}
|
912
|
+
options = foreign_key_options(from_table, to_table, options)
|
721
913
|
at = create_alter_table from_table
|
722
914
|
at.add_foreign_key to_table, options
|
723
915
|
|
724
916
|
execute schema_creation.accept(at)
|
725
917
|
end
|
726
918
|
|
727
|
-
# 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.
|
728
923
|
#
|
729
924
|
# Removes the foreign key on +accounts.branch_id+.
|
730
925
|
#
|
@@ -738,24 +933,11 @@ module ActiveRecord
|
|
738
933
|
#
|
739
934
|
# remove_foreign_key :accounts, name: :special_fk_name
|
740
935
|
#
|
936
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
741
937
|
def remove_foreign_key(from_table, options_or_to_table = {})
|
742
938
|
return unless supports_foreign_keys?
|
743
939
|
|
744
|
-
|
745
|
-
options = options_or_to_table
|
746
|
-
else
|
747
|
-
options = { column: foreign_key_column_for(options_or_to_table) }
|
748
|
-
end
|
749
|
-
|
750
|
-
fk_name_to_delete = options.fetch(:name) do
|
751
|
-
fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
|
752
|
-
|
753
|
-
if fk_to_delete
|
754
|
-
fk_to_delete.name
|
755
|
-
else
|
756
|
-
raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
|
757
|
-
end
|
758
|
-
end
|
940
|
+
fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
|
759
941
|
|
760
942
|
at = create_alter_table from_table
|
761
943
|
at.drop_foreign_key fk_name_to_delete
|
@@ -763,16 +945,63 @@ module ActiveRecord
|
|
763
945
|
execute schema_creation.accept(at)
|
764
946
|
end
|
765
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
|
+
|
766
973
|
def foreign_key_column_for(table_name) # :nodoc:
|
767
|
-
|
974
|
+
prefix = Base.table_name_prefix
|
975
|
+
suffix = Base.table_name_suffix
|
976
|
+
name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
977
|
+
"#{name.singularize}_id"
|
978
|
+
end
|
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
|
768
985
|
end
|
769
986
|
|
770
987
|
def dump_schema_information #:nodoc:
|
988
|
+
versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
|
989
|
+
insert_versions_sql(versions)
|
990
|
+
end
|
991
|
+
|
992
|
+
def insert_versions_sql(versions) # :nodoc:
|
771
993
|
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
|
772
994
|
|
773
|
-
|
774
|
-
"INSERT INTO #{sm_table} (version) VALUES
|
775
|
-
|
995
|
+
if supports_multi_insert?
|
996
|
+
sql = "INSERT INTO #{sm_table} (version) VALUES "
|
997
|
+
sql << versions.map {|v| "('#{v}')" }.join(', ')
|
998
|
+
sql << ";\n\n"
|
999
|
+
sql
|
1000
|
+
else
|
1001
|
+
versions.map { |version|
|
1002
|
+
"INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
|
1003
|
+
}.join "\n\n"
|
1004
|
+
end
|
776
1005
|
end
|
777
1006
|
|
778
1007
|
# Should not be called normally, but this operation is non-destructive.
|
@@ -781,12 +1010,20 @@ module ActiveRecord
|
|
781
1010
|
ActiveRecord::SchemaMigration.create_table
|
782
1011
|
end
|
783
1012
|
|
784
|
-
def
|
1013
|
+
def initialize_internal_metadata_table
|
1014
|
+
ActiveRecord::InternalMetadata.create_table
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1018
|
+
{ primary_key: true }
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def assume_migrated_upto_version(version, migrations_paths)
|
785
1022
|
migrations_paths = Array(migrations_paths)
|
786
1023
|
version = version.to_i
|
787
1024
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
788
1025
|
|
789
|
-
migrated = select_values("SELECT version FROM #{sm_table}").map
|
1026
|
+
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
790
1027
|
paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
|
791
1028
|
versions = Dir[*paths].map do |filename|
|
792
1029
|
filename.split('/').last.split('_').first.to_i
|
@@ -796,14 +1033,12 @@ module ActiveRecord
|
|
796
1033
|
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
|
797
1034
|
end
|
798
1035
|
|
799
|
-
|
800
|
-
|
801
|
-
if
|
802
|
-
raise "Duplicate migration #{
|
803
|
-
elsif v < version
|
804
|
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
|
805
|
-
inserted << v
|
1036
|
+
inserting = (versions - migrated).select {|v| v < version}
|
1037
|
+
if inserting.any?
|
1038
|
+
if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
|
1039
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
806
1040
|
end
|
1041
|
+
execute insert_versions_sql(inserting)
|
807
1042
|
end
|
808
1043
|
end
|
809
1044
|
|
@@ -824,6 +1059,12 @@ module ActiveRecord
|
|
824
1059
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
825
1060
|
end
|
826
1061
|
|
1062
|
+
elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
|
1063
|
+
if (0..6) === precision
|
1064
|
+
column_type_sql << "(#{precision})"
|
1065
|
+
else
|
1066
|
+
raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
1067
|
+
end
|
827
1068
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
828
1069
|
column_type_sql << "(#{limit})"
|
829
1070
|
end
|
@@ -835,22 +1076,23 @@ module ActiveRecord
|
|
835
1076
|
end
|
836
1077
|
|
837
1078
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
838
|
-
#
|
1079
|
+
# PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
|
839
1080
|
# require the order columns appear in the SELECT.
|
840
1081
|
#
|
841
1082
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
842
|
-
|
1083
|
+
#
|
1084
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
843
1085
|
columns
|
844
1086
|
end
|
845
1087
|
|
846
|
-
include TimestampDefaultDeprecation
|
847
1088
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
848
|
-
# Additional options (like
|
1089
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
849
1090
|
#
|
850
|
-
# add_timestamps(:suppliers, null:
|
1091
|
+
# add_timestamps(:suppliers, null: true)
|
851
1092
|
#
|
852
1093
|
def add_timestamps(table_name, options = {})
|
853
|
-
|
1094
|
+
options[:null] = false if options[:null].nil?
|
1095
|
+
|
854
1096
|
add_column table_name, :created_at, :datetime, options
|
855
1097
|
add_column table_name, :updated_at, :datetime, options
|
856
1098
|
end
|
@@ -868,15 +1110,19 @@ module ActiveRecord
|
|
868
1110
|
Table.new(table_name, base)
|
869
1111
|
end
|
870
1112
|
|
871
|
-
def add_index_options(table_name, column_name,
|
872
|
-
|
873
|
-
|
1113
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
1114
|
+
if column_name.is_a?(String) && /\W/ === column_name
|
1115
|
+
column_names = column_name
|
1116
|
+
else
|
1117
|
+
column_names = Array(column_name)
|
1118
|
+
end
|
874
1119
|
|
875
1120
|
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
876
1121
|
|
877
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
878
1122
|
index_type = options[:type].to_s if options.key?(:type)
|
1123
|
+
index_type ||= options[:unique] ? "UNIQUE" : ""
|
879
1124
|
index_name = options[:name].to_s if options.key?(:name)
|
1125
|
+
index_name ||= index_name(table_name, index_name_options(column_names))
|
880
1126
|
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
881
1127
|
|
882
1128
|
if options.key?(:algorithm)
|
@@ -894,12 +1140,26 @@ module ActiveRecord
|
|
894
1140
|
if index_name.length > max_index_length
|
895
1141
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
|
896
1142
|
end
|
897
|
-
if
|
1143
|
+
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
|
898
1144
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
899
1145
|
end
|
900
1146
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
901
1147
|
|
902
|
-
[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"
|
903
1163
|
end
|
904
1164
|
|
905
1165
|
protected
|
@@ -918,6 +1178,8 @@ module ActiveRecord
|
|
918
1178
|
|
919
1179
|
# Overridden by the MySQL adapter for supporting index lengths
|
920
1180
|
def quoted_columns_for_index(column_names, options = {})
|
1181
|
+
return [column_names] if column_names.is_a?(String)
|
1182
|
+
|
921
1183
|
option_strings = Hash[column_names.map {|name| [name, '']}]
|
922
1184
|
|
923
1185
|
# add index sort order if supported
|
@@ -928,26 +1190,38 @@ module ActiveRecord
|
|
928
1190
|
column_names.map {|name| quote_column_name(name) + option_strings[name]}
|
929
1191
|
end
|
930
1192
|
|
931
|
-
def options_include_default?(options)
|
932
|
-
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
933
|
-
end
|
934
|
-
|
935
1193
|
def index_name_for_remove(table_name, options = {})
|
936
|
-
|
1194
|
+
return options[:name] if can_remove_index_by_name?(options)
|
937
1195
|
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
options_without_column.delete :column
|
942
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
1196
|
+
# if the adapter doesn't support the indexes call the best we can do
|
1197
|
+
# is return the default index name for the options provided
|
1198
|
+
return index_name(table_name, options) unless respond_to?(:indexes)
|
943
1199
|
|
944
|
-
|
945
|
-
|
1200
|
+
checks = []
|
1201
|
+
|
1202
|
+
if options.is_a?(Hash)
|
1203
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1204
|
+
column_names = Array(options[:column]).map(&:to_s)
|
1205
|
+
else
|
1206
|
+
column_names = Array(options).map(&:to_s)
|
1207
|
+
end
|
946
1208
|
|
947
|
-
|
1209
|
+
if column_names.any?
|
1210
|
+
checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
|
948
1211
|
end
|
949
1212
|
|
950
|
-
|
1213
|
+
raise ArgumentError "No name or columns specified" if checks.none?
|
1214
|
+
|
1215
|
+
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
1216
|
+
|
1217
|
+
if matching_indexes.count > 1
|
1218
|
+
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1219
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1220
|
+
elsif matching_indexes.none?
|
1221
|
+
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1222
|
+
else
|
1223
|
+
matching_indexes.first.name
|
1224
|
+
end
|
951
1225
|
end
|
952
1226
|
|
953
1227
|
def rename_table_indexes(table_name, new_name)
|
@@ -973,19 +1247,47 @@ module ActiveRecord
|
|
973
1247
|
end
|
974
1248
|
|
975
1249
|
private
|
976
|
-
def create_table_definition(
|
977
|
-
TableDefinition.new
|
1250
|
+
def create_table_definition(*args)
|
1251
|
+
TableDefinition.new(*args)
|
978
1252
|
end
|
979
1253
|
|
980
1254
|
def create_alter_table(name)
|
981
|
-
AlterTable.new create_table_definition(name
|
1255
|
+
AlterTable.new create_table_definition(name)
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
def index_name_options(column_names) # :nodoc:
|
1259
|
+
if column_names.is_a?(String)
|
1260
|
+
column_names = column_names.scan(/\w+/).join('_')
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
{ column: column_names }
|
982
1264
|
end
|
983
1265
|
|
984
1266
|
def foreign_key_name(table_name, options) # :nodoc:
|
1267
|
+
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1268
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
985
1269
|
options.fetch(:name) do
|
986
|
-
"fk_rails_#{
|
1270
|
+
"fk_rails_#{hashed_identifier}"
|
987
1271
|
end
|
988
1272
|
end
|
1273
|
+
|
1274
|
+
def validate_index_length!(table_name, new_name) # :nodoc:
|
1275
|
+
if new_name.length > allowed_index_name_length
|
1276
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
def extract_new_default_value(default_or_changes)
|
1281
|
+
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1282
|
+
default_or_changes[:to]
|
1283
|
+
else
|
1284
|
+
default_or_changes
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
def can_remove_index_by_name?(options)
|
1289
|
+
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1290
|
+
end
|
989
1291
|
end
|
990
1292
|
end
|
991
1293
|
end
|