activerecord 4.2.11.1 → 5.2.4.5
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 +594 -1620
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -49
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- 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/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- 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/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/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/migration/join_table"
|
4
|
+
require "active_support/core_ext/string/access"
|
5
|
+
require "digest/sha2"
|
4
6
|
|
5
7
|
module ActiveRecord
|
6
8
|
module ConnectionAdapters # :nodoc:
|
@@ -14,15 +16,26 @@ module ActiveRecord
|
|
14
16
|
{}
|
15
17
|
end
|
16
18
|
|
19
|
+
def table_options(table_name)
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the table comment that's stored in database metadata.
|
24
|
+
def table_comment(table_name)
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
17
28
|
# Truncates a table alias according to the limits of the current adapter.
|
18
29
|
def table_alias_for(table_name)
|
19
|
-
table_name[0...table_alias_length].tr(
|
30
|
+
table_name[0...table_alias_length].tr(".", "_")
|
20
31
|
end
|
21
32
|
|
22
33
|
# Returns the relation names useable to back Active Record models.
|
23
|
-
# For most adapters this means all tables and views.
|
34
|
+
# For most adapters this means all #tables and #views.
|
24
35
|
def data_sources
|
25
|
-
|
36
|
+
query_values(data_source_sql, "SCHEMA")
|
37
|
+
rescue NotImplementedError
|
38
|
+
tables | views
|
26
39
|
end
|
27
40
|
|
28
41
|
# Checks to see if the data source +name+ exists on the database.
|
@@ -30,19 +43,45 @@ module ActiveRecord
|
|
30
43
|
# data_source_exists?(:ebooks)
|
31
44
|
#
|
32
45
|
def data_source_exists?(name)
|
46
|
+
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
|
47
|
+
rescue NotImplementedError
|
33
48
|
data_sources.include?(name.to_s)
|
34
49
|
end
|
35
50
|
|
51
|
+
# Returns an array of table names defined in the database.
|
52
|
+
def tables
|
53
|
+
query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
|
54
|
+
end
|
55
|
+
|
36
56
|
# Checks to see if the table +table_name+ exists on the database.
|
37
57
|
#
|
38
58
|
# table_exists?(:developers)
|
39
59
|
#
|
40
60
|
def table_exists?(table_name)
|
61
|
+
query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
|
62
|
+
rescue NotImplementedError
|
41
63
|
tables.include?(table_name.to_s)
|
42
64
|
end
|
43
65
|
|
66
|
+
# Returns an array of view names defined in the database.
|
67
|
+
def views
|
68
|
+
query_values(data_source_sql(type: "VIEW"), "SCHEMA")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks to see if the view +view_name+ exists on the database.
|
72
|
+
#
|
73
|
+
# view_exists?(:ebooks)
|
74
|
+
#
|
75
|
+
def view_exists?(view_name)
|
76
|
+
query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
|
77
|
+
rescue NotImplementedError
|
78
|
+
views.include?(view_name.to_s)
|
79
|
+
end
|
80
|
+
|
44
81
|
# Returns an array of indexes for the given table.
|
45
|
-
|
82
|
+
def indexes(table_name)
|
83
|
+
raise NotImplementedError, "#indexes is not implemented"
|
84
|
+
end
|
46
85
|
|
47
86
|
# Checks to see if an index exists on a table for a given index definition.
|
48
87
|
#
|
@@ -60,18 +99,21 @@ module ActiveRecord
|
|
60
99
|
#
|
61
100
|
def index_exists?(table_name, column_name, options = {})
|
62
101
|
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
102
|
checks = []
|
65
|
-
checks << lambda { |i| i.
|
66
|
-
checks << lambda { |i| i.columns == column_names }
|
103
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
67
104
|
checks << lambda { |i| i.unique } if options[:unique]
|
105
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
68
106
|
|
69
107
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
70
108
|
end
|
71
109
|
|
72
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
73
|
-
|
74
|
-
|
110
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
111
|
+
def columns(table_name)
|
112
|
+
table_name = table_name.to_s
|
113
|
+
column_definitions(table_name).map do |field|
|
114
|
+
new_column_from_field(table_name, field)
|
115
|
+
end
|
116
|
+
end
|
75
117
|
|
76
118
|
# Checks to see if a column exists in a given table.
|
77
119
|
#
|
@@ -89,19 +131,27 @@ module ActiveRecord
|
|
89
131
|
#
|
90
132
|
def column_exists?(table_name, column_name, type = nil, options = {})
|
91
133
|
column_name = column_name.to_s
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
134
|
+
checks = []
|
135
|
+
checks << lambda { |c| c.name == column_name }
|
136
|
+
checks << lambda { |c| c.type == type } if type
|
137
|
+
column_options_keys.each do |attr|
|
138
|
+
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
139
|
+
end
|
140
|
+
|
141
|
+
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns just a table's primary key
|
145
|
+
def primary_key(table_name)
|
146
|
+
pk = primary_keys(table_name)
|
147
|
+
pk = pk.first unless pk.size > 1
|
148
|
+
pk
|
99
149
|
end
|
100
150
|
|
101
151
|
# Creates a new table with the name +table_name+. +table_name+ may either
|
102
152
|
# be a String or a Symbol.
|
103
153
|
#
|
104
|
-
# There are two ways to work with
|
154
|
+
# There are two ways to work with #create_table. You can use the block
|
105
155
|
# form or the regular form, like this:
|
106
156
|
#
|
107
157
|
# === Block form
|
@@ -133,13 +183,18 @@ module ActiveRecord
|
|
133
183
|
# The +options+ hash can include the following keys:
|
134
184
|
# [<tt>:id</tt>]
|
135
185
|
# Whether to automatically add a primary key column. Defaults to true.
|
136
|
-
# Join tables for
|
186
|
+
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
187
|
+
#
|
188
|
+
# A Symbol can be used to specify the type of the generated primary key column.
|
137
189
|
# [<tt>:primary_key</tt>]
|
138
190
|
# The name of the primary key, if one is to be added automatically.
|
139
|
-
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
191
|
+
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
192
|
+
#
|
193
|
+
# If an array is passed, a composite primary key will be created.
|
140
194
|
#
|
141
195
|
# Note that Active Record models will automatically detect their
|
142
|
-
# primary key. This can be avoided by using
|
196
|
+
# primary key. This can be avoided by using
|
197
|
+
# {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
|
143
198
|
# to define the key explicitly.
|
144
199
|
#
|
145
200
|
# [<tt>:options</tt>]
|
@@ -161,7 +216,7 @@ module ActiveRecord
|
|
161
216
|
# generates:
|
162
217
|
#
|
163
218
|
# CREATE TABLE suppliers (
|
164
|
-
# id
|
219
|
+
# id bigint auto_increment PRIMARY KEY
|
165
220
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
166
221
|
#
|
167
222
|
# ====== Rename the primary key column
|
@@ -173,22 +228,52 @@ module ActiveRecord
|
|
173
228
|
# generates:
|
174
229
|
#
|
175
230
|
# CREATE TABLE objects (
|
176
|
-
# guid
|
231
|
+
# guid bigint auto_increment PRIMARY KEY,
|
177
232
|
# name varchar(80)
|
178
233
|
# )
|
179
234
|
#
|
235
|
+
# ====== Change the primary key column type
|
236
|
+
#
|
237
|
+
# create_table(:tags, id: :string) do |t|
|
238
|
+
# t.column :label, :string
|
239
|
+
# end
|
240
|
+
#
|
241
|
+
# generates:
|
242
|
+
#
|
243
|
+
# CREATE TABLE tags (
|
244
|
+
# id varchar PRIMARY KEY,
|
245
|
+
# label varchar
|
246
|
+
# )
|
247
|
+
#
|
248
|
+
# ====== Create a composite primary key
|
249
|
+
#
|
250
|
+
# create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
|
251
|
+
# t.belongs_to :product
|
252
|
+
# t.belongs_to :client
|
253
|
+
# end
|
254
|
+
#
|
255
|
+
# generates:
|
256
|
+
#
|
257
|
+
# CREATE TABLE order (
|
258
|
+
# product_id bigint NOT NULL,
|
259
|
+
# client_id bigint NOT NULL
|
260
|
+
# );
|
261
|
+
#
|
262
|
+
# ALTER TABLE ONLY "orders"
|
263
|
+
# ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
|
264
|
+
#
|
180
265
|
# ====== Do not add a primary key column
|
181
266
|
#
|
182
267
|
# create_table(:categories_suppliers, id: false) do |t|
|
183
|
-
# t.column :category_id, :
|
184
|
-
# t.column :supplier_id, :
|
268
|
+
# t.column :category_id, :bigint
|
269
|
+
# t.column :supplier_id, :bigint
|
185
270
|
# end
|
186
271
|
#
|
187
272
|
# generates:
|
188
273
|
#
|
189
274
|
# CREATE TABLE categories_suppliers (
|
190
|
-
# category_id
|
191
|
-
# supplier_id
|
275
|
+
# category_id bigint,
|
276
|
+
# supplier_id bigint
|
192
277
|
# )
|
193
278
|
#
|
194
279
|
# ====== Create a temporary table based on a query
|
@@ -202,33 +287,41 @@ module ActiveRecord
|
|
202
287
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
203
288
|
#
|
204
289
|
# 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]
|
290
|
+
def create_table(table_name, comment: nil, **options)
|
291
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
207
292
|
|
208
293
|
if options[:id] != false && !options[:as]
|
209
294
|
pk = options.fetch(:primary_key) do
|
210
295
|
Base.get_primary_key table_name.to_s.singularize
|
211
296
|
end
|
212
297
|
|
213
|
-
|
298
|
+
if pk.is_a?(Array)
|
299
|
+
td.primary_keys pk
|
300
|
+
else
|
301
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options
|
302
|
+
end
|
214
303
|
end
|
215
304
|
|
216
305
|
yield td if block_given?
|
217
306
|
|
218
|
-
if options[:force]
|
219
|
-
drop_table(table_name, options)
|
307
|
+
if options[:force]
|
308
|
+
drop_table(table_name, options.merge(if_exists: true))
|
220
309
|
end
|
221
310
|
|
222
311
|
result = execute schema_creation.accept td
|
223
312
|
|
224
313
|
unless supports_indexes_in_create?
|
225
|
-
td.indexes.
|
314
|
+
td.indexes.each do |column_name, index_options|
|
226
315
|
add_index(table_name, column_name, index_options)
|
227
316
|
end
|
228
317
|
end
|
229
318
|
|
230
|
-
|
231
|
-
|
319
|
+
if supports_comments? && !supports_comments_in_create?
|
320
|
+
change_table_comment(table_name, comment) if comment.present?
|
321
|
+
|
322
|
+
td.columns.each do |column|
|
323
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
324
|
+
end
|
232
325
|
end
|
233
326
|
|
234
327
|
result
|
@@ -240,9 +333,9 @@ module ActiveRecord
|
|
240
333
|
# # Creates a table called 'assemblies_parts' with no id.
|
241
334
|
# create_join_table(:assemblies, :parts)
|
242
335
|
#
|
243
|
-
# You can pass
|
336
|
+
# You can pass an +options+ hash which can include the following keys:
|
244
337
|
# [<tt>:table_name</tt>]
|
245
|
-
# Sets the table name overriding the default
|
338
|
+
# Sets the table name, overriding the default.
|
246
339
|
# [<tt>:column_options</tt>]
|
247
340
|
# Any extra options you want appended to the columns definition.
|
248
341
|
# [<tt>:options</tt>]
|
@@ -253,7 +346,7 @@ module ActiveRecord
|
|
253
346
|
# Set to true to drop the table before creating it.
|
254
347
|
# Defaults to false.
|
255
348
|
#
|
256
|
-
# Note that
|
349
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
257
350
|
# its block form to do so yourself:
|
258
351
|
#
|
259
352
|
# create_join_table :products, :categories do |t|
|
@@ -268,31 +361,30 @@ module ActiveRecord
|
|
268
361
|
# generates:
|
269
362
|
#
|
270
363
|
# CREATE TABLE assemblies_parts (
|
271
|
-
# assembly_id
|
272
|
-
# part_id
|
364
|
+
# assembly_id bigint NOT NULL,
|
365
|
+
# part_id bigint NOT NULL,
|
273
366
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
274
367
|
#
|
275
|
-
def create_join_table(table_1, table_2,
|
368
|
+
def create_join_table(table_1, table_2, column_options: {}, **options)
|
276
369
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
277
370
|
|
278
|
-
column_options
|
279
|
-
column_options.reverse_merge!(null: false)
|
371
|
+
column_options.reverse_merge!(null: false, index: false)
|
280
372
|
|
281
|
-
|
373
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
282
374
|
|
283
375
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
284
|
-
td.
|
285
|
-
td.
|
376
|
+
td.references t1_ref, column_options
|
377
|
+
td.references t2_ref, column_options
|
286
378
|
yield td if block_given?
|
287
379
|
end
|
288
380
|
end
|
289
381
|
|
290
382
|
# Drops the join table specified by the given arguments.
|
291
|
-
# See
|
383
|
+
# See #create_join_table for details.
|
292
384
|
#
|
293
385
|
# Although this command ignores the block if one is given, it can be helpful
|
294
386
|
# 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.
|
387
|
+
# In that case, the block will be used by #create_join_table.
|
296
388
|
def drop_join_table(table_1, table_2, options = {})
|
297
389
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
298
390
|
drop_table(join_table_name)
|
@@ -310,10 +402,12 @@ module ActiveRecord
|
|
310
402
|
# [<tt>:bulk</tt>]
|
311
403
|
# Set this to true to make this a bulk alter query, such as
|
312
404
|
#
|
313
|
-
# ALTER TABLE `users` ADD COLUMN age INT
|
405
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
314
406
|
#
|
315
407
|
# Defaults to false.
|
316
408
|
#
|
409
|
+
# Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
|
410
|
+
#
|
317
411
|
# ====== Add a column
|
318
412
|
#
|
319
413
|
# change_table(:suppliers) do |t|
|
@@ -338,7 +432,7 @@ module ActiveRecord
|
|
338
432
|
# t.references :company
|
339
433
|
# end
|
340
434
|
#
|
341
|
-
# Creates a <tt>company_id(
|
435
|
+
# Creates a <tt>company_id(bigint)</tt> column.
|
342
436
|
#
|
343
437
|
# ====== Add a polymorphic foreign key column
|
344
438
|
#
|
@@ -346,7 +440,7 @@ module ActiveRecord
|
|
346
440
|
# t.belongs_to :company, polymorphic: true
|
347
441
|
# end
|
348
442
|
#
|
349
|
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(
|
443
|
+
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
|
350
444
|
#
|
351
445
|
# ====== Remove a column
|
352
446
|
#
|
@@ -367,7 +461,7 @@ module ActiveRecord
|
|
367
461
|
# t.remove_index :company_id
|
368
462
|
# end
|
369
463
|
#
|
370
|
-
# See also Table for details on all of the various column
|
464
|
+
# See also Table for details on all of the various column transformations.
|
371
465
|
def change_table(table_name, options = {})
|
372
466
|
if supports_bulk_alter? && options[:bulk]
|
373
467
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
@@ -391,16 +485,96 @@ module ActiveRecord
|
|
391
485
|
# [<tt>:force</tt>]
|
392
486
|
# Set to +:cascade+ to drop dependent objects as well.
|
393
487
|
# Defaults to false.
|
488
|
+
# [<tt>:if_exists</tt>]
|
489
|
+
# Set to +true+ to only drop the table if it exists.
|
490
|
+
# Defaults to false.
|
394
491
|
#
|
395
492
|
# Although this command ignores most +options+ and the block if one is given,
|
396
493
|
# 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.
|
494
|
+
# In that case, +options+ and the block will be used by #create_table.
|
398
495
|
def drop_table(table_name, options = {})
|
399
|
-
execute "DROP TABLE #{quote_table_name(table_name)}"
|
496
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
400
497
|
end
|
401
498
|
|
402
|
-
#
|
403
|
-
#
|
499
|
+
# Add a new +type+ column named +column_name+ to +table_name+.
|
500
|
+
#
|
501
|
+
# The +type+ parameter is normally one of the migrations native types,
|
502
|
+
# which is one of the following:
|
503
|
+
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
|
504
|
+
# <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
|
505
|
+
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
|
506
|
+
# <tt>:binary</tt>, <tt>:boolean</tt>.
|
507
|
+
#
|
508
|
+
# You may use a type not in this list as long as it is supported by your
|
509
|
+
# database (for example, "polygon" in MySQL), but this will not be database
|
510
|
+
# agnostic and should usually be avoided.
|
511
|
+
#
|
512
|
+
# Available options are (none of these exists by default):
|
513
|
+
# * <tt>:limit</tt> -
|
514
|
+
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
|
515
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
|
516
|
+
# This option is ignored by some backends.
|
517
|
+
# * <tt>:default</tt> -
|
518
|
+
# The column's default value. Use +nil+ for +NULL+.
|
519
|
+
# * <tt>:null</tt> -
|
520
|
+
# Allows or disallows +NULL+ values in the column.
|
521
|
+
# * <tt>:precision</tt> -
|
522
|
+
# Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
523
|
+
# * <tt>:scale</tt> -
|
524
|
+
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
525
|
+
# * <tt>:comment</tt> -
|
526
|
+
# Specifies the comment for the column. This option is ignored by some backends.
|
527
|
+
#
|
528
|
+
# Note: The precision is the total number of significant digits,
|
529
|
+
# and the scale is the number of digits that can be stored following
|
530
|
+
# the decimal point. For example, the number 123.45 has a precision of 5
|
531
|
+
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
|
532
|
+
# range from -999.99 to 999.99.
|
533
|
+
#
|
534
|
+
# Please be aware of different RDBMS implementations behavior with
|
535
|
+
# <tt>:decimal</tt> columns:
|
536
|
+
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
|
537
|
+
# <tt>:precision</tt>, and makes no comments about the requirements of
|
538
|
+
# <tt>:precision</tt>.
|
539
|
+
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
|
540
|
+
# Default is (10,0).
|
541
|
+
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
|
542
|
+
# <tt>:scale</tt> [0..infinity]. No default.
|
543
|
+
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
|
544
|
+
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
545
|
+
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
546
|
+
# Default is (38,0).
|
547
|
+
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
548
|
+
# Default unknown.
|
549
|
+
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
550
|
+
# Default (38,0).
|
551
|
+
#
|
552
|
+
# == Examples
|
553
|
+
#
|
554
|
+
# add_column(:users, :picture, :binary, limit: 2.megabytes)
|
555
|
+
# # ALTER TABLE "users" ADD "picture" blob(2097152)
|
556
|
+
#
|
557
|
+
# add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
|
558
|
+
# # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
|
559
|
+
#
|
560
|
+
# add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
|
561
|
+
# # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
|
562
|
+
#
|
563
|
+
# add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
|
564
|
+
# # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
|
565
|
+
#
|
566
|
+
# # While :scale defaults to zero on most databases, it
|
567
|
+
# # probably wouldn't hurt to include it.
|
568
|
+
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
|
569
|
+
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
|
570
|
+
#
|
571
|
+
# # Defines a column that stores an array of a type.
|
572
|
+
# add_column(:users, :skills, :text, array: true)
|
573
|
+
# # ALTER TABLE "users" ADD "skills" text[]
|
574
|
+
#
|
575
|
+
# # Defines a column with a database-specific type.
|
576
|
+
# add_column(:shapes, :triangle, 'polygon')
|
577
|
+
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
404
578
|
def add_column(table_name, column_name, type, options = {})
|
405
579
|
at = create_alter_table table_name
|
406
580
|
at.add_column(column_name, type, options)
|
@@ -424,9 +598,9 @@ module ActiveRecord
|
|
424
598
|
#
|
425
599
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
426
600
|
# to provide these in a migration's +change+ method so it can be reverted.
|
427
|
-
# In that case, +type+ and +options+ will be used by add_column.
|
601
|
+
# In that case, +type+ and +options+ will be used by #add_column.
|
428
602
|
def remove_column(table_name, column_name, type = nil, options = {})
|
429
|
-
execute "ALTER TABLE #{quote_table_name(table_name)}
|
603
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
|
430
604
|
end
|
431
605
|
|
432
606
|
# Changes the column's definition according to the new options.
|
@@ -448,11 +622,16 @@ module ActiveRecord
|
|
448
622
|
#
|
449
623
|
# change_column_default(:users, :email, nil)
|
450
624
|
#
|
451
|
-
|
625
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
626
|
+
# reversible in migration:
|
627
|
+
#
|
628
|
+
# change_column_default(:posts, :state, from: nil, to: "draft")
|
629
|
+
#
|
630
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
452
631
|
raise NotImplementedError, "change_column_default is not implemented"
|
453
632
|
end
|
454
633
|
|
455
|
-
# Sets or removes a
|
634
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
456
635
|
# indicates whether the value can be +NULL+. For example
|
457
636
|
#
|
458
637
|
# change_column_null(:users, :nickname, false)
|
@@ -464,7 +643,7 @@ module ActiveRecord
|
|
464
643
|
# allows them to be +NULL+ (drops the constraint).
|
465
644
|
#
|
466
645
|
# The method accepts an optional fourth argument to replace existing
|
467
|
-
#
|
646
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
468
647
|
# constraint if needed, since otherwise those rows would not be valid.
|
469
648
|
#
|
470
649
|
# Please note the fourth argument does not set a column's default.
|
@@ -518,6 +697,8 @@ module ActiveRecord
|
|
518
697
|
#
|
519
698
|
# CREATE INDEX by_name ON accounts(name(10))
|
520
699
|
#
|
700
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
701
|
+
#
|
521
702
|
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
522
703
|
#
|
523
704
|
# generates:
|
@@ -534,7 +715,7 @@ module ActiveRecord
|
|
534
715
|
#
|
535
716
|
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
|
536
717
|
#
|
537
|
-
# Note: MySQL
|
718
|
+
# Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
|
538
719
|
#
|
539
720
|
# ====== Creating a partial index
|
540
721
|
#
|
@@ -557,6 +738,19 @@ module ActiveRecord
|
|
557
738
|
#
|
558
739
|
# Note: only supported by PostgreSQL and MySQL
|
559
740
|
#
|
741
|
+
# ====== Creating an index with a specific operator class
|
742
|
+
#
|
743
|
+
# add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
|
744
|
+
# # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
|
745
|
+
#
|
746
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
|
747
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
|
748
|
+
#
|
749
|
+
# add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
|
750
|
+
# # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
|
751
|
+
#
|
752
|
+
# Note: only supported by PostgreSQL
|
753
|
+
#
|
560
754
|
# ====== Creating an index with a specific type
|
561
755
|
#
|
562
756
|
# add_index(:developers, :name, type: :fulltext)
|
@@ -565,7 +759,7 @@ module ActiveRecord
|
|
565
759
|
#
|
566
760
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
567
761
|
#
|
568
|
-
# Note: only supported by MySQL.
|
762
|
+
# Note: only supported by MySQL.
|
569
763
|
def add_index(table_name, column_name, options = {})
|
570
764
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
571
765
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -573,15 +767,15 @@ module ActiveRecord
|
|
573
767
|
|
574
768
|
# Removes the given index from the table.
|
575
769
|
#
|
576
|
-
# Removes the +
|
770
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
577
771
|
#
|
578
|
-
# remove_index :accounts, :
|
772
|
+
# remove_index :accounts, :branch_id
|
579
773
|
#
|
580
|
-
# Removes the index
|
774
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
581
775
|
#
|
582
776
|
# remove_index :accounts, column: :branch_id
|
583
777
|
#
|
584
|
-
# Removes the index
|
778
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
585
779
|
#
|
586
780
|
# remove_index :accounts, column: [:branch_id, :party_id]
|
587
781
|
#
|
@@ -590,10 +784,7 @@ module ActiveRecord
|
|
590
784
|
# remove_index :accounts, name: :by_branch_party
|
591
785
|
#
|
592
786
|
def remove_index(table_name, options = {})
|
593
|
-
|
594
|
-
end
|
595
|
-
|
596
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
787
|
+
index_name = index_name_for_remove(table_name, options)
|
597
788
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
598
789
|
end
|
599
790
|
|
@@ -606,7 +797,7 @@ module ActiveRecord
|
|
606
797
|
def rename_index(table_name, old_name, new_name)
|
607
798
|
validate_index_length!(table_name, new_name)
|
608
799
|
|
609
|
-
# this is a naive implementation; some DBs may support this more efficiently (
|
800
|
+
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
610
801
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
611
802
|
return unless old_index_def
|
612
803
|
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
@@ -623,36 +814,35 @@ module ActiveRecord
|
|
623
814
|
raise ArgumentError, "You must specify the index name"
|
624
815
|
end
|
625
816
|
else
|
626
|
-
index_name(table_name,
|
817
|
+
index_name(table_name, index_name_options(options))
|
627
818
|
end
|
628
819
|
end
|
629
820
|
|
630
821
|
# Verifies the existence of an index with a given name.
|
631
|
-
|
632
|
-
# The default argument is returned if the underlying implementation does not define the indexes method,
|
633
|
-
# as there's no way to determine the correct answer in that case.
|
634
|
-
def index_name_exists?(table_name, index_name, default)
|
635
|
-
return default unless respond_to?(:indexes)
|
822
|
+
def index_name_exists?(table_name, index_name)
|
636
823
|
index_name = index_name.to_s
|
637
824
|
indexes(table_name).detect { |i| i.name == index_name }
|
638
825
|
end
|
639
826
|
|
640
|
-
# Adds a reference. The reference column is
|
827
|
+
# Adds a reference. The reference column is a bigint by default,
|
641
828
|
# the <tt>:type</tt> option can be used to specify a different type.
|
642
829
|
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
643
|
-
#
|
830
|
+
# #add_reference and #add_belongs_to are acceptable.
|
644
831
|
#
|
645
832
|
# The +options+ hash can include the following keys:
|
646
833
|
# [<tt>:type</tt>]
|
647
|
-
# The reference column type. Defaults to +:
|
834
|
+
# The reference column type. Defaults to +:bigint+.
|
648
835
|
# [<tt>:index</tt>]
|
649
|
-
# Add an appropriate index. Defaults to
|
836
|
+
# Add an appropriate index. Defaults to true.
|
837
|
+
# See #add_index for usage of this option.
|
650
838
|
# [<tt>:foreign_key</tt>]
|
651
|
-
# Add an appropriate foreign key. Defaults to false.
|
839
|
+
# Add an appropriate foreign key constraint. Defaults to false.
|
652
840
|
# [<tt>:polymorphic</tt>]
|
653
|
-
#
|
841
|
+
# Whether an additional +_type+ column should be added. Defaults to false.
|
842
|
+
# [<tt>:null</tt>]
|
843
|
+
# Whether the column allows nulls. Defaults to true.
|
654
844
|
#
|
655
|
-
# ====== Create a user_id
|
845
|
+
# ====== Create a user_id bigint column
|
656
846
|
#
|
657
847
|
# add_reference(:products, :user)
|
658
848
|
#
|
@@ -664,28 +854,29 @@ module ActiveRecord
|
|
664
854
|
#
|
665
855
|
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
666
856
|
#
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
857
|
+
# ====== Create a supplier_id column with a unique index
|
858
|
+
#
|
859
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
860
|
+
#
|
861
|
+
# ====== Create a supplier_id column with a named index
|
862
|
+
#
|
863
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
864
|
+
#
|
865
|
+
# ====== Create a supplier_id column and appropriate foreign key
|
866
|
+
#
|
867
|
+
# add_reference(:products, :supplier, foreign_key: true)
|
868
|
+
#
|
869
|
+
# ====== Create a supplier_id column and a foreign key to the firms table
|
870
|
+
#
|
871
|
+
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
872
|
+
#
|
873
|
+
def add_reference(table_name, ref_name, **options)
|
874
|
+
ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
|
684
875
|
end
|
685
876
|
alias :add_belongs_to :add_reference
|
686
877
|
|
687
878
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
688
|
-
#
|
879
|
+
# #remove_reference and #remove_belongs_to are acceptable.
|
689
880
|
#
|
690
881
|
# ====== Remove the reference
|
691
882
|
#
|
@@ -699,19 +890,25 @@ module ActiveRecord
|
|
699
890
|
#
|
700
891
|
# remove_reference(:products, :user, index: true, foreign_key: true)
|
701
892
|
#
|
702
|
-
def remove_reference(table_name, ref_name,
|
703
|
-
if
|
704
|
-
|
705
|
-
|
893
|
+
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
894
|
+
if foreign_key
|
895
|
+
reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
|
896
|
+
if foreign_key.is_a?(Hash)
|
897
|
+
foreign_key_options = foreign_key
|
898
|
+
else
|
899
|
+
foreign_key_options = { to_table: reference_name }
|
900
|
+
end
|
901
|
+
foreign_key_options[:column] ||= "#{ref_name}_id"
|
902
|
+
remove_foreign_key(table_name, foreign_key_options)
|
706
903
|
end
|
707
904
|
|
708
905
|
remove_column(table_name, "#{ref_name}_id")
|
709
|
-
remove_column(table_name, "#{ref_name}_type") if
|
906
|
+
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
710
907
|
end
|
711
908
|
alias :remove_belongs_to :remove_reference
|
712
909
|
|
713
910
|
# Returns an array of foreign keys for the given table.
|
714
|
-
# The foreign keys are represented as
|
911
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
715
912
|
def foreign_keys(table_name)
|
716
913
|
raise NotImplementedError, "foreign_keys is not implemented"
|
717
914
|
end
|
@@ -729,11 +926,11 @@ module ActiveRecord
|
|
729
926
|
#
|
730
927
|
# generates:
|
731
928
|
#
|
732
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
929
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
733
930
|
#
|
734
931
|
# ====== Creating a foreign key on a specific column
|
735
932
|
#
|
736
|
-
# add_foreign_key :articles, :users, column: :author_id, primary_key:
|
933
|
+
# add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
|
737
934
|
#
|
738
935
|
# generates:
|
739
936
|
#
|
@@ -745,7 +942,7 @@ module ActiveRecord
|
|
745
942
|
#
|
746
943
|
# generates:
|
747
944
|
#
|
748
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
945
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
749
946
|
#
|
750
947
|
# The +options+ hash can include the following keys:
|
751
948
|
# [<tt>:column</tt>]
|
@@ -755,28 +952,25 @@ module ActiveRecord
|
|
755
952
|
# [<tt>:name</tt>]
|
756
953
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
757
954
|
# [<tt>:on_delete</tt>]
|
758
|
-
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade
|
955
|
+
# Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
759
956
|
# [<tt>:on_update</tt>]
|
760
|
-
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade
|
957
|
+
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
958
|
+
# [<tt>:validate</tt>]
|
959
|
+
# (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
761
960
|
def add_foreign_key(from_table, to_table, options = {})
|
762
961
|
return unless supports_foreign_keys?
|
763
962
|
|
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
|
-
}
|
963
|
+
options = foreign_key_options(from_table, to_table, options)
|
773
964
|
at = create_alter_table from_table
|
774
965
|
at.add_foreign_key to_table, options
|
775
966
|
|
776
967
|
execute schema_creation.accept(at)
|
777
968
|
end
|
778
969
|
|
779
|
-
# Removes the given foreign key from the table.
|
970
|
+
# Removes the given foreign key from the table. Any option parameters provided
|
971
|
+
# will be used to re-add the foreign key in case of a migration rollback.
|
972
|
+
# It is recommended that you provide any options used when creating the foreign
|
973
|
+
# key so that the migration can be reverted properly.
|
780
974
|
#
|
781
975
|
# Removes the foreign key on +accounts.branch_id+.
|
782
976
|
#
|
@@ -790,24 +984,11 @@ module ActiveRecord
|
|
790
984
|
#
|
791
985
|
# remove_foreign_key :accounts, name: :special_fk_name
|
792
986
|
#
|
987
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
793
988
|
def remove_foreign_key(from_table, options_or_to_table = {})
|
794
989
|
return unless supports_foreign_keys?
|
795
990
|
|
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
|
991
|
+
fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
|
811
992
|
|
812
993
|
at = create_alter_table from_table
|
813
994
|
at.drop_foreign_key fk_name_to_delete
|
@@ -815,6 +996,21 @@ module ActiveRecord
|
|
815
996
|
execute schema_creation.accept(at)
|
816
997
|
end
|
817
998
|
|
999
|
+
# Checks to see if a foreign key exists on a table for a given foreign key definition.
|
1000
|
+
#
|
1001
|
+
# # Checks to see if a foreign key exists.
|
1002
|
+
# foreign_key_exists?(:accounts, :branches)
|
1003
|
+
#
|
1004
|
+
# # Checks to see if a foreign key on a specified column exists.
|
1005
|
+
# foreign_key_exists?(:accounts, column: :owner_id)
|
1006
|
+
#
|
1007
|
+
# # Checks to see if a foreign key with a custom name exists.
|
1008
|
+
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1009
|
+
#
|
1010
|
+
def foreign_key_exists?(from_table, options_or_to_table = {})
|
1011
|
+
foreign_key_for(from_table, options_or_to_table).present?
|
1012
|
+
end
|
1013
|
+
|
818
1014
|
def foreign_key_column_for(table_name) # :nodoc:
|
819
1015
|
prefix = Base.table_name_prefix
|
820
1016
|
suffix = Base.table_name_suffix
|
@@ -822,47 +1018,54 @@ module ActiveRecord
|
|
822
1018
|
"#{name.singularize}_id"
|
823
1019
|
end
|
824
1020
|
|
825
|
-
def
|
826
|
-
|
1021
|
+
def foreign_key_options(from_table, to_table, options) # :nodoc:
|
1022
|
+
options = options.dup
|
1023
|
+
options[:column] ||= foreign_key_column_for(to_table)
|
1024
|
+
options[:name] ||= foreign_key_name(from_table, options)
|
1025
|
+
options
|
1026
|
+
end
|
827
1027
|
|
828
|
-
|
829
|
-
|
830
|
-
|
1028
|
+
def dump_schema_information #:nodoc:
|
1029
|
+
versions = ActiveRecord::SchemaMigration.all_versions
|
1030
|
+
insert_versions_sql(versions) if versions.any?
|
831
1031
|
end
|
832
1032
|
|
833
|
-
|
834
|
-
|
835
|
-
def initialize_schema_migrations_table
|
836
|
-
ActiveRecord::SchemaMigration.create_table
|
1033
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1034
|
+
{ primary_key: true }
|
837
1035
|
end
|
838
1036
|
|
839
|
-
def assume_migrated_upto_version(version, migrations_paths
|
1037
|
+
def assume_migrated_upto_version(version, migrations_paths)
|
840
1038
|
migrations_paths = Array(migrations_paths)
|
841
1039
|
version = version.to_i
|
842
|
-
sm_table = quote_table_name(ActiveRecord::
|
1040
|
+
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
|
843
1041
|
|
844
|
-
migrated =
|
845
|
-
versions =
|
846
|
-
|
1042
|
+
migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
|
1043
|
+
versions = migration_context.migration_files.map do |file|
|
1044
|
+
migration_context.parse_migration_filename(file).first.to_i
|
847
1045
|
end
|
848
1046
|
|
849
1047
|
unless migrated.include?(version)
|
850
|
-
execute "INSERT INTO #{sm_table} (version) VALUES (
|
1048
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
851
1049
|
end
|
852
1050
|
|
853
|
-
|
854
|
-
|
855
|
-
if
|
856
|
-
raise "Duplicate migration #{
|
857
|
-
|
858
|
-
|
859
|
-
|
1051
|
+
inserting = (versions - migrated).select { |v| v < version }
|
1052
|
+
if inserting.any?
|
1053
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1054
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1055
|
+
end
|
1056
|
+
if supports_multi_insert?
|
1057
|
+
execute insert_versions_sql(inserting)
|
1058
|
+
else
|
1059
|
+
inserting.each do |v|
|
1060
|
+
execute insert_versions_sql(v)
|
1061
|
+
end
|
860
1062
|
end
|
861
1063
|
end
|
862
1064
|
end
|
863
1065
|
|
864
|
-
def type_to_sql(type, limit
|
865
|
-
|
1066
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
|
1067
|
+
type = type.to_sym if type
|
1068
|
+
if native = native_database_types[type]
|
866
1069
|
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
867
1070
|
|
868
1071
|
if type == :decimal # ignore limit, use precision and scale
|
@@ -878,6 +1081,12 @@ module ActiveRecord
|
|
878
1081
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
879
1082
|
end
|
880
1083
|
|
1084
|
+
elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
|
1085
|
+
if (0..6) === precision
|
1086
|
+
column_type_sql << "(#{precision})"
|
1087
|
+
else
|
1088
|
+
raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
1089
|
+
end
|
881
1090
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
882
1091
|
column_type_sql << "(#{limit})"
|
883
1092
|
end
|
@@ -889,7 +1098,7 @@ module ActiveRecord
|
|
889
1098
|
end
|
890
1099
|
|
891
1100
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
892
|
-
# PostgreSQL, MySQL, and Oracle
|
1101
|
+
# PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
|
893
1102
|
# require the order columns appear in the SELECT.
|
894
1103
|
#
|
895
1104
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
@@ -898,14 +1107,14 @@ module ActiveRecord
|
|
898
1107
|
columns
|
899
1108
|
end
|
900
1109
|
|
901
|
-
include TimestampDefaultDeprecation
|
902
1110
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
903
|
-
# Additional options (like
|
1111
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
904
1112
|
#
|
905
|
-
# add_timestamps(:suppliers, null:
|
1113
|
+
# add_timestamps(:suppliers, null: true)
|
906
1114
|
#
|
907
1115
|
def add_timestamps(table_name, options = {})
|
908
|
-
|
1116
|
+
options[:null] = false if options[:null].nil?
|
1117
|
+
|
909
1118
|
add_column table_name, :created_at, :datetime, options
|
910
1119
|
add_column table_name, :updated_at, :datetime, options
|
911
1120
|
end
|
@@ -923,16 +1132,15 @@ module ActiveRecord
|
|
923
1132
|
Table.new(table_name, base)
|
924
1133
|
end
|
925
1134
|
|
926
|
-
def add_index_options(table_name, column_name,
|
927
|
-
column_names =
|
928
|
-
index_name = index_name(table_name, column: column_names)
|
1135
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
1136
|
+
column_names = index_column_names(column_name)
|
929
1137
|
|
930
|
-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
1138
|
+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
|
931
1139
|
|
932
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
933
1140
|
index_type = options[:type].to_s if options.key?(:type)
|
1141
|
+
index_type ||= options[:unique] ? "UNIQUE" : ""
|
934
1142
|
index_name = options[:name].to_s if options.key?(:name)
|
935
|
-
|
1143
|
+
index_name ||= index_name(table_name, column_names)
|
936
1144
|
|
937
1145
|
if options.key?(:algorithm)
|
938
1146
|
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
@@ -946,63 +1154,99 @@ module ActiveRecord
|
|
946
1154
|
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
947
1155
|
end
|
948
1156
|
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
|
1157
|
+
validate_index_length!(table_name, index_name, options.fetch(:internal, false))
|
1158
|
+
|
1159
|
+
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
|
953
1160
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
954
1161
|
end
|
955
1162
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
956
1163
|
|
957
|
-
[index_name, index_type, index_columns, index_options, algorithm, using]
|
1164
|
+
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
958
1165
|
end
|
959
1166
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
1167
|
+
def options_include_default?(options)
|
1168
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
# Changes the comment for a table or removes it if +nil+.
|
1172
|
+
def change_table_comment(table_name, comment)
|
1173
|
+
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
# Changes the comment for a column or removes it if +nil+.
|
1177
|
+
def change_column_comment(table_name, column_name, comment)
|
1178
|
+
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1179
|
+
end
|
970
1180
|
|
971
|
-
|
1181
|
+
def create_schema_dumper(options) # :nodoc:
|
1182
|
+
SchemaDumper.create(self, options)
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
private
|
1186
|
+
def column_options_keys
|
1187
|
+
[:limit, :precision, :scale, :default, :null, :collation, :comment]
|
972
1188
|
end
|
973
1189
|
|
974
|
-
|
975
|
-
|
976
|
-
|
1190
|
+
def add_index_sort_order(quoted_columns, **options)
|
1191
|
+
orders = options_for_index_columns(options[:order])
|
1192
|
+
quoted_columns.each do |name, column|
|
1193
|
+
column << " #{orders[name].upcase}" if orders[name].present?
|
1194
|
+
end
|
1195
|
+
end
|
977
1196
|
|
978
|
-
|
1197
|
+
def options_for_index_columns(options)
|
1198
|
+
if options.is_a?(Hash)
|
1199
|
+
options.symbolize_keys
|
1200
|
+
else
|
1201
|
+
Hash.new { |hash, column| hash[column] = options }
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Overridden by the MySQL adapter for supporting index lengths and by
|
1206
|
+
# the PostgreSQL adapter for supporting operator classes.
|
1207
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
979
1208
|
if supports_index_sort_order?
|
980
|
-
|
1209
|
+
quoted_columns = add_index_sort_order(quoted_columns, options)
|
981
1210
|
end
|
982
1211
|
|
983
|
-
|
1212
|
+
quoted_columns
|
984
1213
|
end
|
985
1214
|
|
986
|
-
def
|
987
|
-
|
1215
|
+
def quoted_columns_for_index(column_names, **options)
|
1216
|
+
return [column_names] if column_names.is_a?(String)
|
1217
|
+
|
1218
|
+
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
|
1219
|
+
add_options_for_index_columns(quoted_columns, options).values
|
988
1220
|
end
|
989
1221
|
|
990
1222
|
def index_name_for_remove(table_name, options = {})
|
991
|
-
|
1223
|
+
return options[:name] if can_remove_index_by_name?(options)
|
992
1224
|
|
993
|
-
|
994
|
-
if options.is_a?(Hash) && options.has_key?(:name)
|
995
|
-
options_without_column = options.dup
|
996
|
-
options_without_column.delete :column
|
997
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
1225
|
+
checks = []
|
998
1226
|
|
999
|
-
|
1000
|
-
|
1227
|
+
if options.is_a?(Hash)
|
1228
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1229
|
+
column_names = index_column_names(options[:column])
|
1230
|
+
else
|
1231
|
+
column_names = index_column_names(options)
|
1232
|
+
end
|
1001
1233
|
|
1002
|
-
|
1234
|
+
if column_names.present?
|
1235
|
+
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1003
1236
|
end
|
1004
1237
|
|
1005
|
-
|
1238
|
+
raise ArgumentError, "No name or columns specified" if checks.none?
|
1239
|
+
|
1240
|
+
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
1241
|
+
|
1242
|
+
if matching_indexes.count > 1
|
1243
|
+
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
1244
|
+
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
1245
|
+
elsif matching_indexes.none?
|
1246
|
+
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
1247
|
+
else
|
1248
|
+
matching_indexes.first.name
|
1249
|
+
end
|
1006
1250
|
end
|
1007
1251
|
|
1008
1252
|
def rename_table_indexes(table_name, new_name)
|
@@ -1027,28 +1271,126 @@ module ActiveRecord
|
|
1027
1271
|
end
|
1028
1272
|
end
|
1029
1273
|
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
end
|
1274
|
+
def schema_creation
|
1275
|
+
SchemaCreation.new(self)
|
1276
|
+
end
|
1034
1277
|
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1278
|
+
def create_table_definition(*args)
|
1279
|
+
TableDefinition.new(*args)
|
1280
|
+
end
|
1038
1281
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1042
|
-
options.fetch(:name) do
|
1043
|
-
"fk_rails_#{hashed_identifier}"
|
1282
|
+
def create_alter_table(name)
|
1283
|
+
AlterTable.new create_table_definition(name)
|
1044
1284
|
end
|
1045
|
-
end
|
1046
1285
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1286
|
+
def fetch_type_metadata(sql_type)
|
1287
|
+
cast_type = lookup_cast_type(sql_type)
|
1288
|
+
SqlTypeMetadata.new(
|
1289
|
+
sql_type: sql_type,
|
1290
|
+
type: cast_type.type,
|
1291
|
+
limit: cast_type.limit,
|
1292
|
+
precision: cast_type.precision,
|
1293
|
+
scale: cast_type.scale,
|
1294
|
+
)
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def index_column_names(column_names)
|
1298
|
+
if column_names.is_a?(String) && /\W/.match?(column_names)
|
1299
|
+
column_names
|
1300
|
+
else
|
1301
|
+
Array(column_names)
|
1302
|
+
end
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
def index_name_options(column_names)
|
1306
|
+
if column_names.is_a?(String) && /\W/.match?(column_names)
|
1307
|
+
column_names = column_names.scan(/\w+/).join("_")
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
{ column: column_names }
|
1311
|
+
end
|
1312
|
+
|
1313
|
+
def foreign_key_name(table_name, options)
|
1314
|
+
options.fetch(:name) do
|
1315
|
+
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1316
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1317
|
+
|
1318
|
+
"fk_rails_#{hashed_identifier}"
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
def foreign_key_for(from_table, options_or_to_table = {})
|
1323
|
+
return unless supports_foreign_keys?
|
1324
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
def foreign_key_for!(from_table, options_or_to_table = {})
|
1328
|
+
foreign_key_for(from_table, options_or_to_table) || \
|
1329
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
def extract_foreign_key_action(specifier)
|
1333
|
+
case specifier
|
1334
|
+
when "CASCADE"; :cascade
|
1335
|
+
when "SET NULL"; :nullify
|
1336
|
+
when "RESTRICT"; :restrict
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1341
|
+
max_index_length = internal ? index_name_length : allowed_index_name_length
|
1342
|
+
|
1343
|
+
if new_name.length > max_index_length
|
1344
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
1345
|
+
end
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
def extract_new_default_value(default_or_changes)
|
1349
|
+
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
|
1350
|
+
default_or_changes[:to]
|
1351
|
+
else
|
1352
|
+
default_or_changes
|
1353
|
+
end
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
def can_remove_index_by_name?(options)
|
1357
|
+
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
def add_column_for_alter(table_name, column_name, type, options = {})
|
1361
|
+
td = create_table_definition(table_name)
|
1362
|
+
cd = td.new_column_definition(column_name, type, options)
|
1363
|
+
schema_creation.accept(AddColumnDefinition.new(cd))
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def remove_column_for_alter(table_name, column_name, type = nil, options = {})
|
1367
|
+
"DROP COLUMN #{quote_column_name(column_name)}"
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
def remove_columns_for_alter(table_name, *column_names)
|
1371
|
+
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
def insert_versions_sql(versions)
|
1375
|
+
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
|
1376
|
+
|
1377
|
+
if versions.is_a?(Array)
|
1378
|
+
sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
|
1379
|
+
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1380
|
+
sql << ";\n\n"
|
1381
|
+
sql
|
1382
|
+
else
|
1383
|
+
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
|
1384
|
+
end
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
def data_source_sql(name = nil, type: nil)
|
1388
|
+
raise NotImplementedError
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
def quoted_scope(name = nil, type: nil)
|
1392
|
+
raise NotImplementedError
|
1050
1393
|
end
|
1051
|
-
end
|
1052
1394
|
end
|
1053
1395
|
end
|
1054
1396
|
end
|