activerecord 4.2.0 → 5.2.8.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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/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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,4 +1,8 @@
|
|
1
|
-
|
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"
|
2
6
|
|
3
7
|
module ActiveRecord
|
4
8
|
module ConnectionAdapters # :nodoc:
|
@@ -12,9 +16,41 @@ module ActiveRecord
|
|
12
16
|
{}
|
13
17
|
end
|
14
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
|
+
|
15
28
|
# Truncates a table alias according to the limits of the current adapter.
|
16
29
|
def table_alias_for(table_name)
|
17
|
-
table_name[0...table_alias_length].tr(
|
30
|
+
table_name[0...table_alias_length].tr(".", "_")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the relation names useable to back Active Record models.
|
34
|
+
# For most adapters this means all #tables and #views.
|
35
|
+
def data_sources
|
36
|
+
query_values(data_source_sql, "SCHEMA")
|
37
|
+
rescue NotImplementedError
|
38
|
+
tables | views
|
39
|
+
end
|
40
|
+
|
41
|
+
# Checks to see if the data source +name+ exists on the database.
|
42
|
+
#
|
43
|
+
# data_source_exists?(:ebooks)
|
44
|
+
#
|
45
|
+
def data_source_exists?(name)
|
46
|
+
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
|
47
|
+
rescue NotImplementedError
|
48
|
+
data_sources.include?(name.to_s)
|
49
|
+
end
|
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")
|
18
54
|
end
|
19
55
|
|
20
56
|
# Checks to see if the table +table_name+ exists on the database.
|
@@ -22,11 +58,30 @@ module ActiveRecord
|
|
22
58
|
# table_exists?(:developers)
|
23
59
|
#
|
24
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
|
25
63
|
tables.include?(table_name.to_s)
|
26
64
|
end
|
27
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
|
+
|
28
81
|
# Returns an array of indexes for the given table.
|
29
|
-
|
82
|
+
def indexes(table_name)
|
83
|
+
raise NotImplementedError, "#indexes is not implemented"
|
84
|
+
end
|
30
85
|
|
31
86
|
# Checks to see if an index exists on a table for a given index definition.
|
32
87
|
#
|
@@ -44,18 +99,21 @@ module ActiveRecord
|
|
44
99
|
#
|
45
100
|
def index_exists?(table_name, column_name, options = {})
|
46
101
|
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
102
|
checks = []
|
49
|
-
checks << lambda { |i| i.
|
50
|
-
checks << lambda { |i| i.columns == column_names }
|
103
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
51
104
|
checks << lambda { |i| i.unique } if options[:unique]
|
105
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
52
106
|
|
53
107
|
indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
|
54
108
|
end
|
55
109
|
|
56
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
57
|
-
|
58
|
-
|
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
|
59
117
|
|
60
118
|
# Checks to see if a column exists in a given table.
|
61
119
|
#
|
@@ -73,19 +131,27 @@ module ActiveRecord
|
|
73
131
|
#
|
74
132
|
def column_exists?(table_name, column_name, type = nil, options = {})
|
75
133
|
column_name = column_name.to_s
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
83
149
|
end
|
84
150
|
|
85
151
|
# Creates a new table with the name +table_name+. +table_name+ may either
|
86
152
|
# be a String or a Symbol.
|
87
153
|
#
|
88
|
-
# There are two ways to work with
|
154
|
+
# There are two ways to work with #create_table. You can use the block
|
89
155
|
# form or the regular form, like this:
|
90
156
|
#
|
91
157
|
# === Block form
|
@@ -117,13 +183,18 @@ module ActiveRecord
|
|
117
183
|
# The +options+ hash can include the following keys:
|
118
184
|
# [<tt>:id</tt>]
|
119
185
|
# Whether to automatically add a primary key column. Defaults to true.
|
120
|
-
# 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.
|
121
189
|
# [<tt>:primary_key</tt>]
|
122
190
|
# The name of the primary key, if one is to be added automatically.
|
123
|
-
# 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.
|
124
194
|
#
|
125
195
|
# Note that Active Record models will automatically detect their
|
126
|
-
# 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
|
127
198
|
# to define the key explicitly.
|
128
199
|
#
|
129
200
|
# [<tt>:options</tt>]
|
@@ -145,7 +216,7 @@ module ActiveRecord
|
|
145
216
|
# generates:
|
146
217
|
#
|
147
218
|
# CREATE TABLE suppliers (
|
148
|
-
# id
|
219
|
+
# id bigint auto_increment PRIMARY KEY
|
149
220
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
150
221
|
#
|
151
222
|
# ====== Rename the primary key column
|
@@ -157,22 +228,52 @@ module ActiveRecord
|
|
157
228
|
# generates:
|
158
229
|
#
|
159
230
|
# CREATE TABLE objects (
|
160
|
-
# guid
|
231
|
+
# guid bigint auto_increment PRIMARY KEY,
|
161
232
|
# name varchar(80)
|
162
233
|
# )
|
163
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
|
+
#
|
164
265
|
# ====== Do not add a primary key column
|
165
266
|
#
|
166
267
|
# create_table(:categories_suppliers, id: false) do |t|
|
167
|
-
# t.column :category_id, :
|
168
|
-
# t.column :supplier_id, :
|
268
|
+
# t.column :category_id, :bigint
|
269
|
+
# t.column :supplier_id, :bigint
|
169
270
|
# end
|
170
271
|
#
|
171
272
|
# generates:
|
172
273
|
#
|
173
274
|
# CREATE TABLE categories_suppliers (
|
174
|
-
# category_id
|
175
|
-
# supplier_id
|
275
|
+
# category_id bigint,
|
276
|
+
# supplier_id bigint
|
176
277
|
# )
|
177
278
|
#
|
178
279
|
# ====== Create a temporary table based on a query
|
@@ -186,25 +287,43 @@ module ActiveRecord
|
|
186
287
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
187
288
|
#
|
188
289
|
# 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]
|
290
|
+
def create_table(table_name, comment: nil, **options)
|
291
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
191
292
|
|
192
293
|
if options[:id] != false && !options[:as]
|
193
294
|
pk = options.fetch(:primary_key) do
|
194
295
|
Base.get_primary_key table_name.to_s.singularize
|
195
296
|
end
|
196
297
|
|
197
|
-
|
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
|
198
303
|
end
|
199
304
|
|
200
305
|
yield td if block_given?
|
201
306
|
|
202
|
-
if options[:force]
|
203
|
-
drop_table(table_name, options)
|
307
|
+
if options[:force]
|
308
|
+
drop_table(table_name, options.merge(if_exists: true))
|
204
309
|
end
|
205
310
|
|
206
311
|
result = execute schema_creation.accept td
|
207
|
-
|
312
|
+
|
313
|
+
unless supports_indexes_in_create?
|
314
|
+
td.indexes.each do |column_name, index_options|
|
315
|
+
add_index(table_name, column_name, index_options)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
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
|
325
|
+
end
|
326
|
+
|
208
327
|
result
|
209
328
|
end
|
210
329
|
|
@@ -214,9 +333,9 @@ module ActiveRecord
|
|
214
333
|
# # Creates a table called 'assemblies_parts' with no id.
|
215
334
|
# create_join_table(:assemblies, :parts)
|
216
335
|
#
|
217
|
-
# You can pass
|
336
|
+
# You can pass an +options+ hash which can include the following keys:
|
218
337
|
# [<tt>:table_name</tt>]
|
219
|
-
# Sets the table name overriding the default
|
338
|
+
# Sets the table name, overriding the default.
|
220
339
|
# [<tt>:column_options</tt>]
|
221
340
|
# Any extra options you want appended to the columns definition.
|
222
341
|
# [<tt>:options</tt>]
|
@@ -227,7 +346,7 @@ module ActiveRecord
|
|
227
346
|
# Set to true to drop the table before creating it.
|
228
347
|
# Defaults to false.
|
229
348
|
#
|
230
|
-
# Note that
|
349
|
+
# Note that #create_join_table does not create any indices by default; you can use
|
231
350
|
# its block form to do so yourself:
|
232
351
|
#
|
233
352
|
# create_join_table :products, :categories do |t|
|
@@ -242,31 +361,30 @@ module ActiveRecord
|
|
242
361
|
# generates:
|
243
362
|
#
|
244
363
|
# CREATE TABLE assemblies_parts (
|
245
|
-
# assembly_id
|
246
|
-
# part_id
|
364
|
+
# assembly_id bigint NOT NULL,
|
365
|
+
# part_id bigint NOT NULL,
|
247
366
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
248
367
|
#
|
249
|
-
def create_join_table(table_1, table_2,
|
368
|
+
def create_join_table(table_1, table_2, column_options: {}, **options)
|
250
369
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
251
370
|
|
252
|
-
column_options
|
253
|
-
column_options.reverse_merge!(null: false)
|
371
|
+
column_options.reverse_merge!(null: false, index: false)
|
254
372
|
|
255
|
-
|
373
|
+
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
256
374
|
|
257
375
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
258
|
-
td.
|
259
|
-
td.
|
376
|
+
td.references t1_ref, column_options
|
377
|
+
td.references t2_ref, column_options
|
260
378
|
yield td if block_given?
|
261
379
|
end
|
262
380
|
end
|
263
381
|
|
264
382
|
# Drops the join table specified by the given arguments.
|
265
|
-
# See
|
383
|
+
# See #create_join_table for details.
|
266
384
|
#
|
267
385
|
# Although this command ignores the block if one is given, it can be helpful
|
268
386
|
# 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.
|
387
|
+
# In that case, the block will be used by #create_join_table.
|
270
388
|
def drop_join_table(table_1, table_2, options = {})
|
271
389
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
272
390
|
drop_table(join_table_name)
|
@@ -284,10 +402,12 @@ module ActiveRecord
|
|
284
402
|
# [<tt>:bulk</tt>]
|
285
403
|
# Set this to true to make this a bulk alter query, such as
|
286
404
|
#
|
287
|
-
# ALTER TABLE `users` ADD COLUMN age INT
|
405
|
+
# ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
|
288
406
|
#
|
289
407
|
# Defaults to false.
|
290
408
|
#
|
409
|
+
# Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
|
410
|
+
#
|
291
411
|
# ====== Add a column
|
292
412
|
#
|
293
413
|
# change_table(:suppliers) do |t|
|
@@ -312,7 +432,7 @@ module ActiveRecord
|
|
312
432
|
# t.references :company
|
313
433
|
# end
|
314
434
|
#
|
315
|
-
# Creates a <tt>company_id(
|
435
|
+
# Creates a <tt>company_id(bigint)</tt> column.
|
316
436
|
#
|
317
437
|
# ====== Add a polymorphic foreign key column
|
318
438
|
#
|
@@ -320,7 +440,7 @@ module ActiveRecord
|
|
320
440
|
# t.belongs_to :company, polymorphic: true
|
321
441
|
# end
|
322
442
|
#
|
323
|
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(
|
443
|
+
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
|
324
444
|
#
|
325
445
|
# ====== Remove a column
|
326
446
|
#
|
@@ -341,7 +461,7 @@ module ActiveRecord
|
|
341
461
|
# t.remove_index :company_id
|
342
462
|
# end
|
343
463
|
#
|
344
|
-
# See also Table for details on all of the various column
|
464
|
+
# See also Table for details on all of the various column transformations.
|
345
465
|
def change_table(table_name, options = {})
|
346
466
|
if supports_bulk_alter? && options[:bulk]
|
347
467
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
@@ -365,16 +485,96 @@ module ActiveRecord
|
|
365
485
|
# [<tt>:force</tt>]
|
366
486
|
# Set to +:cascade+ to drop dependent objects as well.
|
367
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.
|
368
491
|
#
|
369
492
|
# Although this command ignores most +options+ and the block if one is given,
|
370
493
|
# 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.
|
494
|
+
# In that case, +options+ and the block will be used by #create_table.
|
372
495
|
def drop_table(table_name, options = {})
|
373
|
-
execute "DROP TABLE #{quote_table_name(table_name)}"
|
496
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
374
497
|
end
|
375
498
|
|
376
|
-
#
|
377
|
-
#
|
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
|
378
578
|
def add_column(table_name, column_name, type, options = {})
|
379
579
|
at = create_alter_table table_name
|
380
580
|
at.add_column(column_name, type, options)
|
@@ -398,9 +598,9 @@ module ActiveRecord
|
|
398
598
|
#
|
399
599
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
400
600
|
# to provide these in a migration's +change+ method so it can be reverted.
|
401
|
-
# 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.
|
402
602
|
def remove_column(table_name, column_name, type = nil, options = {})
|
403
|
-
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)}"
|
404
604
|
end
|
405
605
|
|
406
606
|
# Changes the column's definition according to the new options.
|
@@ -422,11 +622,16 @@ module ActiveRecord
|
|
422
622
|
#
|
423
623
|
# change_column_default(:users, :email, nil)
|
424
624
|
#
|
425
|
-
|
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)
|
426
631
|
raise NotImplementedError, "change_column_default is not implemented"
|
427
632
|
end
|
428
633
|
|
429
|
-
# Sets or removes a
|
634
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
|
430
635
|
# indicates whether the value can be +NULL+. For example
|
431
636
|
#
|
432
637
|
# change_column_null(:users, :nickname, false)
|
@@ -438,7 +643,7 @@ module ActiveRecord
|
|
438
643
|
# allows them to be +NULL+ (drops the constraint).
|
439
644
|
#
|
440
645
|
# The method accepts an optional fourth argument to replace existing
|
441
|
-
#
|
646
|
+
# <tt>NULL</tt>s with some other value. Use that one when enabling the
|
442
647
|
# constraint if needed, since otherwise those rows would not be valid.
|
443
648
|
#
|
444
649
|
# Please note the fourth argument does not set a column's default.
|
@@ -492,6 +697,8 @@ module ActiveRecord
|
|
492
697
|
#
|
493
698
|
# CREATE INDEX by_name ON accounts(name(10))
|
494
699
|
#
|
700
|
+
# ====== Creating an index with specific key lengths for multiple keys
|
701
|
+
#
|
495
702
|
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
|
496
703
|
#
|
497
704
|
# generates:
|
@@ -508,7 +715,7 @@ module ActiveRecord
|
|
508
715
|
#
|
509
716
|
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
|
510
717
|
#
|
511
|
-
# Note: MySQL
|
718
|
+
# Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
|
512
719
|
#
|
513
720
|
# ====== Creating a partial index
|
514
721
|
#
|
@@ -518,6 +725,8 @@ module ActiveRecord
|
|
518
725
|
#
|
519
726
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
520
727
|
#
|
728
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
|
729
|
+
#
|
521
730
|
# ====== Creating an index with a specific method
|
522
731
|
#
|
523
732
|
# add_index(:developers, :name, using: 'btree')
|
@@ -529,6 +738,19 @@ module ActiveRecord
|
|
529
738
|
#
|
530
739
|
# Note: only supported by PostgreSQL and MySQL
|
531
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
|
+
#
|
532
754
|
# ====== Creating an index with a specific type
|
533
755
|
#
|
534
756
|
# add_index(:developers, :name, type: :fulltext)
|
@@ -537,7 +759,7 @@ module ActiveRecord
|
|
537
759
|
#
|
538
760
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
539
761
|
#
|
540
|
-
# Note: only supported by MySQL.
|
762
|
+
# Note: only supported by MySQL.
|
541
763
|
def add_index(table_name, column_name, options = {})
|
542
764
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
543
765
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -545,15 +767,15 @@ module ActiveRecord
|
|
545
767
|
|
546
768
|
# Removes the given index from the table.
|
547
769
|
#
|
548
|
-
# Removes the +
|
770
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
549
771
|
#
|
550
|
-
# remove_index :accounts, :
|
772
|
+
# remove_index :accounts, :branch_id
|
551
773
|
#
|
552
|
-
# Removes the index
|
774
|
+
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
553
775
|
#
|
554
776
|
# remove_index :accounts, column: :branch_id
|
555
777
|
#
|
556
|
-
# Removes the index
|
778
|
+
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
557
779
|
#
|
558
780
|
# remove_index :accounts, column: [:branch_id, :party_id]
|
559
781
|
#
|
@@ -562,10 +784,7 @@ module ActiveRecord
|
|
562
784
|
# remove_index :accounts, name: :by_branch_party
|
563
785
|
#
|
564
786
|
def remove_index(table_name, options = {})
|
565
|
-
|
566
|
-
end
|
567
|
-
|
568
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
787
|
+
index_name = index_name_for_remove(table_name, options)
|
569
788
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
570
789
|
end
|
571
790
|
|
@@ -576,10 +795,9 @@ module ActiveRecord
|
|
576
795
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
577
796
|
#
|
578
797
|
def rename_index(table_name, old_name, new_name)
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
798
|
+
validate_index_length!(table_name, new_name)
|
799
|
+
|
800
|
+
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
583
801
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
584
802
|
return unless old_index_def
|
585
803
|
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
@@ -596,26 +814,35 @@ module ActiveRecord
|
|
596
814
|
raise ArgumentError, "You must specify the index name"
|
597
815
|
end
|
598
816
|
else
|
599
|
-
index_name(table_name,
|
817
|
+
index_name(table_name, index_name_options(options))
|
600
818
|
end
|
601
819
|
end
|
602
820
|
|
603
821
|
# Verifies the existence of an index with a given name.
|
604
|
-
|
605
|
-
# The default argument is returned if the underlying implementation does not define the indexes method,
|
606
|
-
# as there's no way to determine the correct answer in that case.
|
607
|
-
def index_name_exists?(table_name, index_name, default)
|
608
|
-
return default unless respond_to?(:indexes)
|
822
|
+
def index_name_exists?(table_name, index_name)
|
609
823
|
index_name = index_name.to_s
|
610
824
|
indexes(table_name).detect { |i| i.name == index_name }
|
611
825
|
end
|
612
826
|
|
613
|
-
# Adds a reference.
|
614
|
-
#
|
615
|
-
# a
|
616
|
-
#
|
827
|
+
# Adds a reference. The reference column is a bigint by default,
|
828
|
+
# the <tt>:type</tt> option can be used to specify a different type.
|
829
|
+
# Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
|
830
|
+
# #add_reference and #add_belongs_to are acceptable.
|
617
831
|
#
|
618
|
-
#
|
832
|
+
# The +options+ hash can include the following keys:
|
833
|
+
# [<tt>:type</tt>]
|
834
|
+
# The reference column type. Defaults to +:bigint+.
|
835
|
+
# [<tt>:index</tt>]
|
836
|
+
# Add an appropriate index. Defaults to true.
|
837
|
+
# See #add_index for usage of this option.
|
838
|
+
# [<tt>:foreign_key</tt>]
|
839
|
+
# Add an appropriate foreign key constraint. Defaults to false.
|
840
|
+
# [<tt>:polymorphic</tt>]
|
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.
|
844
|
+
#
|
845
|
+
# ====== Create a user_id bigint column
|
619
846
|
#
|
620
847
|
# add_reference(:products, :user)
|
621
848
|
#
|
@@ -623,26 +850,33 @@ module ActiveRecord
|
|
623
850
|
#
|
624
851
|
# add_reference(:products, :user, type: :string)
|
625
852
|
#
|
626
|
-
# ====== Create
|
853
|
+
# ====== Create supplier_id, supplier_type columns and appropriate index
|
627
854
|
#
|
628
|
-
#
|
855
|
+
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
629
856
|
#
|
630
|
-
# ====== Create a supplier_id
|
857
|
+
# ====== Create a supplier_id column with a unique index
|
631
858
|
#
|
632
|
-
# add_reference(:products, :supplier,
|
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)
|
633
868
|
#
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
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))
|
641
875
|
end
|
642
876
|
alias :add_belongs_to :add_reference
|
643
877
|
|
644
878
|
# Removes the reference(s). Also removes a +type+ column if one exists.
|
645
|
-
#
|
879
|
+
# #remove_reference and #remove_belongs_to are acceptable.
|
646
880
|
#
|
647
881
|
# ====== Remove the reference
|
648
882
|
#
|
@@ -652,14 +886,29 @@ module ActiveRecord
|
|
652
886
|
#
|
653
887
|
# remove_reference(:products, :supplier, polymorphic: true)
|
654
888
|
#
|
655
|
-
|
889
|
+
# ====== Remove the reference with a foreign key
|
890
|
+
#
|
891
|
+
# remove_reference(:products, :user, index: true, foreign_key: true)
|
892
|
+
#
|
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)
|
903
|
+
end
|
904
|
+
|
656
905
|
remove_column(table_name, "#{ref_name}_id")
|
657
|
-
remove_column(table_name, "#{ref_name}_type") if
|
906
|
+
remove_column(table_name, "#{ref_name}_type") if polymorphic
|
658
907
|
end
|
659
908
|
alias :remove_belongs_to :remove_reference
|
660
909
|
|
661
910
|
# Returns an array of foreign keys for the given table.
|
662
|
-
# The foreign keys are represented as
|
911
|
+
# The foreign keys are represented as ForeignKeyDefinition objects.
|
663
912
|
def foreign_keys(table_name)
|
664
913
|
raise NotImplementedError, "foreign_keys is not implemented"
|
665
914
|
end
|
@@ -668,8 +917,8 @@ module ActiveRecord
|
|
668
917
|
# +to_table+ contains the referenced primary key.
|
669
918
|
#
|
670
919
|
# 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.
|
920
|
+
# +identifier+ is a 10 character long string which is deterministically generated from the
|
921
|
+
# +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
|
673
922
|
#
|
674
923
|
# ====== Creating a simple foreign key
|
675
924
|
#
|
@@ -677,7 +926,7 @@ module ActiveRecord
|
|
677
926
|
#
|
678
927
|
# generates:
|
679
928
|
#
|
680
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
929
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
681
930
|
#
|
682
931
|
# ====== Creating a foreign key on a specific column
|
683
932
|
#
|
@@ -693,7 +942,7 @@ module ActiveRecord
|
|
693
942
|
#
|
694
943
|
# generates:
|
695
944
|
#
|
696
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
945
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
697
946
|
#
|
698
947
|
# The +options+ hash can include the following keys:
|
699
948
|
# [<tt>:column</tt>]
|
@@ -703,28 +952,25 @@ module ActiveRecord
|
|
703
952
|
# [<tt>:name</tt>]
|
704
953
|
# The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
|
705
954
|
# [<tt>:on_delete</tt>]
|
706
|
-
# 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+
|
707
956
|
# [<tt>:on_update</tt>]
|
708
|
-
# 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+.
|
709
960
|
def add_foreign_key(from_table, to_table, options = {})
|
710
961
|
return unless supports_foreign_keys?
|
711
962
|
|
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
|
-
}
|
963
|
+
options = foreign_key_options(from_table, to_table, options)
|
721
964
|
at = create_alter_table from_table
|
722
965
|
at.add_foreign_key to_table, options
|
723
966
|
|
724
967
|
execute schema_creation.accept(at)
|
725
968
|
end
|
726
969
|
|
727
|
-
# 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.
|
728
974
|
#
|
729
975
|
# Removes the foreign key on +accounts.branch_id+.
|
730
976
|
#
|
@@ -738,24 +984,11 @@ module ActiveRecord
|
|
738
984
|
#
|
739
985
|
# remove_foreign_key :accounts, name: :special_fk_name
|
740
986
|
#
|
987
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
741
988
|
def remove_foreign_key(from_table, options_or_to_table = {})
|
742
989
|
return unless supports_foreign_keys?
|
743
990
|
|
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
|
991
|
+
fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
|
759
992
|
|
760
993
|
at = create_alter_table from_table
|
761
994
|
at.drop_foreign_key fk_name_to_delete
|
@@ -763,52 +996,76 @@ module ActiveRecord
|
|
763
996
|
execute schema_creation.accept(at)
|
764
997
|
end
|
765
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
|
+
|
766
1014
|
def foreign_key_column_for(table_name) # :nodoc:
|
767
|
-
|
1015
|
+
prefix = Base.table_name_prefix
|
1016
|
+
suffix = Base.table_name_suffix
|
1017
|
+
name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1018
|
+
"#{name.singularize}_id"
|
768
1019
|
end
|
769
1020
|
|
770
|
-
def
|
771
|
-
|
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
|
772
1027
|
|
773
|
-
|
774
|
-
|
775
|
-
|
1028
|
+
def dump_schema_information #:nodoc:
|
1029
|
+
versions = ActiveRecord::SchemaMigration.all_versions
|
1030
|
+
insert_versions_sql(versions) if versions.any?
|
776
1031
|
end
|
777
1032
|
|
778
|
-
|
779
|
-
|
780
|
-
def initialize_schema_migrations_table
|
781
|
-
ActiveRecord::SchemaMigration.create_table
|
1033
|
+
def internal_string_options_for_primary_key # :nodoc:
|
1034
|
+
{ primary_key: true }
|
782
1035
|
end
|
783
1036
|
|
784
|
-
def assume_migrated_upto_version(version, migrations_paths
|
1037
|
+
def assume_migrated_upto_version(version, migrations_paths)
|
785
1038
|
migrations_paths = Array(migrations_paths)
|
786
1039
|
version = version.to_i
|
787
|
-
sm_table = quote_table_name(ActiveRecord::
|
1040
|
+
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
|
788
1041
|
|
789
|
-
migrated =
|
790
|
-
|
791
|
-
|
792
|
-
filename.split('/').last.split('_').first.to_i
|
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
|
793
1045
|
end
|
794
1046
|
|
795
1047
|
unless migrated.include?(version)
|
796
|
-
execute "INSERT INTO #{sm_table} (version) VALUES (
|
1048
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
797
1049
|
end
|
798
1050
|
|
799
|
-
|
800
|
-
|
801
|
-
if
|
802
|
-
raise "Duplicate migration #{
|
803
|
-
|
804
|
-
|
805
|
-
|
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
|
806
1062
|
end
|
807
1063
|
end
|
808
1064
|
end
|
809
1065
|
|
810
|
-
def type_to_sql(type, limit
|
811
|
-
|
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]
|
812
1069
|
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
813
1070
|
|
814
1071
|
if type == :decimal # ignore limit, use precision and scale
|
@@ -824,6 +1081,12 @@ module ActiveRecord
|
|
824
1081
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
|
825
1082
|
end
|
826
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
|
827
1090
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
828
1091
|
column_type_sql << "(#{limit})"
|
829
1092
|
end
|
@@ -835,22 +1098,23 @@ module ActiveRecord
|
|
835
1098
|
end
|
836
1099
|
|
837
1100
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
838
|
-
#
|
1101
|
+
# PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
|
839
1102
|
# require the order columns appear in the SELECT.
|
840
1103
|
#
|
841
1104
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
842
|
-
|
1105
|
+
#
|
1106
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
843
1107
|
columns
|
844
1108
|
end
|
845
1109
|
|
846
|
-
include TimestampDefaultDeprecation
|
847
1110
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
848
|
-
# Additional options (like
|
1111
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
849
1112
|
#
|
850
|
-
# add_timestamps(:suppliers, null:
|
1113
|
+
# add_timestamps(:suppliers, null: true)
|
851
1114
|
#
|
852
1115
|
def add_timestamps(table_name, options = {})
|
853
|
-
|
1116
|
+
options[:null] = false if options[:null].nil?
|
1117
|
+
|
854
1118
|
add_column table_name, :created_at, :datetime, options
|
855
1119
|
add_column table_name, :updated_at, :datetime, options
|
856
1120
|
end
|
@@ -868,16 +1132,15 @@ module ActiveRecord
|
|
868
1132
|
Table.new(table_name, base)
|
869
1133
|
end
|
870
1134
|
|
871
|
-
def add_index_options(table_name, column_name,
|
872
|
-
column_names =
|
873
|
-
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)
|
874
1137
|
|
875
|
-
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)
|
876
1139
|
|
877
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
878
1140
|
index_type = options[:type].to_s if options.key?(:type)
|
1141
|
+
index_type ||= options[:unique] ? "UNIQUE" : ""
|
879
1142
|
index_name = options[:name].to_s if options.key?(:name)
|
880
|
-
|
1143
|
+
index_name ||= index_name(table_name, column_names)
|
881
1144
|
|
882
1145
|
if options.key?(:algorithm)
|
883
1146
|
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
@@ -891,63 +1154,99 @@ module ActiveRecord
|
|
891
1154
|
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
892
1155
|
end
|
893
1156
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
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)
|
898
1160
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
899
1161
|
end
|
900
1162
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
901
1163
|
|
902
|
-
[index_name, index_type, index_columns, index_options, algorithm, using]
|
1164
|
+
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
903
1165
|
end
|
904
1166
|
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
case order
|
909
|
-
when Hash
|
910
|
-
column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
|
911
|
-
when String
|
912
|
-
column_names.each {|name| option_strings[name] += " #{order.upcase}"}
|
913
|
-
end
|
914
|
-
end
|
1167
|
+
def options_include_default?(options)
|
1168
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1169
|
+
end
|
915
1170
|
|
916
|
-
|
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
|
1180
|
+
|
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]
|
1188
|
+
end
|
1189
|
+
|
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
|
917
1195
|
end
|
918
1196
|
|
919
|
-
|
920
|
-
|
921
|
-
|
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
|
922
1204
|
|
923
|
-
|
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)
|
924
1208
|
if supports_index_sort_order?
|
925
|
-
|
1209
|
+
quoted_columns = add_index_sort_order(quoted_columns, options)
|
926
1210
|
end
|
927
1211
|
|
928
|
-
|
1212
|
+
quoted_columns
|
929
1213
|
end
|
930
1214
|
|
931
|
-
def
|
932
|
-
|
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
|
933
1220
|
end
|
934
1221
|
|
935
1222
|
def index_name_for_remove(table_name, options = {})
|
936
|
-
|
1223
|
+
return options[:name] if can_remove_index_by_name?(options)
|
937
1224
|
|
938
|
-
|
939
|
-
if options.is_a?(Hash) && options.has_key?(:name)
|
940
|
-
options_without_column = options.dup
|
941
|
-
options_without_column.delete :column
|
942
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
1225
|
+
checks = []
|
943
1226
|
|
944
|
-
|
945
|
-
|
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
|
946
1233
|
|
947
|
-
|
1234
|
+
if column_names.present?
|
1235
|
+
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
948
1236
|
end
|
949
1237
|
|
950
|
-
|
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
|
951
1250
|
end
|
952
1251
|
|
953
1252
|
def rename_table_indexes(table_name, new_name)
|
@@ -972,20 +1271,126 @@ module ActiveRecord
|
|
972
1271
|
end
|
973
1272
|
end
|
974
1273
|
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
end
|
1274
|
+
def schema_creation
|
1275
|
+
SchemaCreation.new(self)
|
1276
|
+
end
|
979
1277
|
|
980
|
-
|
981
|
-
|
982
|
-
|
1278
|
+
def create_table_definition(*args)
|
1279
|
+
TableDefinition.new(*args)
|
1280
|
+
end
|
983
1281
|
|
984
|
-
|
985
|
-
|
986
|
-
|
1282
|
+
def create_alter_table(name)
|
1283
|
+
AlterTable.new create_table_definition(name)
|
1284
|
+
end
|
1285
|
+
|
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
|
987
1393
|
end
|
988
|
-
end
|
989
1394
|
end
|
990
1395
|
end
|
991
1396
|
end
|