activerecord 5.2.8.1 → 6.1.6.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 +4 -4
- data/CHANGELOG.md +1255 -596
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +9 -8
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +100 -41
- data/lib/active_record/associations/association_scope.rb +23 -21
- data/lib/active_record/associations/belongs_to_association.rb +55 -48
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
- data/lib/active_record/associations/builder/association.rb +45 -22
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +8 -17
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +44 -34
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +24 -18
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +69 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +47 -34
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +137 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +46 -9
- data/lib/active_record/autosave_association.rb +57 -42
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
- data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
- data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +293 -33
- data/lib/active_record/core.rb +333 -98
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +273 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +108 -36
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +200 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +28 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +37 -23
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +35 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +145 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +206 -157
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +116 -59
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +411 -80
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +109 -93
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -90
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +64 -39
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +62 -45
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +476 -187
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +115 -62
- data/lib/active_record/relation.rb +379 -115
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +4 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +277 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +62 -118
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +116 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_record/migration/join_table"
|
4
3
|
require "active_support/core_ext/string/access"
|
5
4
|
require "digest/sha2"
|
6
5
|
|
@@ -97,10 +96,14 @@ module ActiveRecord
|
|
97
96
|
# # Check an index with a custom name exists
|
98
97
|
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
|
99
98
|
#
|
100
|
-
def index_exists?(table_name, column_name, options
|
101
|
-
column_names = Array(column_name).map(&:to_s)
|
99
|
+
def index_exists?(table_name, column_name, **options)
|
102
100
|
checks = []
|
103
|
-
|
101
|
+
|
102
|
+
if column_name.present?
|
103
|
+
column_names = Array(column_name).map(&:to_s)
|
104
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
105
|
+
end
|
106
|
+
|
104
107
|
checks << lambda { |i| i.unique } if options[:unique]
|
105
108
|
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
106
109
|
|
@@ -129,11 +132,11 @@ module ActiveRecord
|
|
129
132
|
# column_exists?(:suppliers, :name, :string, null: false)
|
130
133
|
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
|
131
134
|
#
|
132
|
-
def column_exists?(table_name, column_name, type = nil, options
|
135
|
+
def column_exists?(table_name, column_name, type = nil, **options)
|
133
136
|
column_name = column_name.to_s
|
134
137
|
checks = []
|
135
138
|
checks << lambda { |c| c.name == column_name }
|
136
|
-
checks << lambda { |c| c.type == type } if type
|
139
|
+
checks << lambda { |c| c.type == type.to_sym rescue nil } if type
|
137
140
|
column_options_keys.each do |attr|
|
138
141
|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
139
142
|
end
|
@@ -205,19 +208,22 @@ module ActiveRecord
|
|
205
208
|
# Set to true to drop the table before creating it.
|
206
209
|
# Set to +:cascade+ to drop dependent objects as well.
|
207
210
|
# Defaults to false.
|
211
|
+
# [<tt>:if_not_exists</tt>]
|
212
|
+
# Set to true to avoid raising an error when the table already exists.
|
213
|
+
# Defaults to false.
|
208
214
|
# [<tt>:as</tt>]
|
209
215
|
# SQL to use to generate the table. When this option is used, the block is
|
210
216
|
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
211
217
|
#
|
212
218
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
213
219
|
#
|
214
|
-
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=
|
220
|
+
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
|
215
221
|
#
|
216
222
|
# generates:
|
217
223
|
#
|
218
224
|
# CREATE TABLE suppliers (
|
219
225
|
# id bigint auto_increment PRIMARY KEY
|
220
|
-
# ) ENGINE=InnoDB DEFAULT CHARSET=
|
226
|
+
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
221
227
|
#
|
222
228
|
# ====== Rename the primary key column
|
223
229
|
#
|
@@ -287,37 +293,44 @@ module ActiveRecord
|
|
287
293
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
288
294
|
#
|
289
295
|
# See also TableDefinition#column for details on how to create columns.
|
290
|
-
def create_table(table_name,
|
291
|
-
td = create_table_definition
|
296
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
297
|
+
td = create_table_definition(table_name, **extract_table_options!(options))
|
292
298
|
|
293
|
-
if
|
294
|
-
pk =
|
295
|
-
|
299
|
+
if id && !td.as
|
300
|
+
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
301
|
+
|
302
|
+
if id.is_a?(Hash)
|
303
|
+
options.merge!(id.except(:type))
|
304
|
+
id = id.fetch(:type, :primary_key)
|
296
305
|
end
|
297
306
|
|
298
307
|
if pk.is_a?(Array)
|
299
308
|
td.primary_keys pk
|
300
309
|
else
|
301
|
-
td.primary_key pk,
|
310
|
+
td.primary_key pk, id, **options
|
302
311
|
end
|
303
312
|
end
|
304
313
|
|
305
314
|
yield td if block_given?
|
306
315
|
|
307
|
-
if
|
308
|
-
drop_table(table_name,
|
316
|
+
if force
|
317
|
+
drop_table(table_name, force: force, if_exists: true)
|
318
|
+
else
|
319
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
309
320
|
end
|
310
321
|
|
311
322
|
result = execute schema_creation.accept td
|
312
323
|
|
313
324
|
unless supports_indexes_in_create?
|
314
325
|
td.indexes.each do |column_name, index_options|
|
315
|
-
add_index(table_name, column_name, index_options)
|
326
|
+
add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
|
316
327
|
end
|
317
328
|
end
|
318
329
|
|
319
330
|
if supports_comments? && !supports_comments_in_create?
|
320
|
-
|
331
|
+
if table_comment = td.comment.presence
|
332
|
+
change_table_comment(table_name, table_comment)
|
333
|
+
end
|
321
334
|
|
322
335
|
td.columns.each do |column|
|
323
336
|
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
@@ -372,9 +385,9 @@ module ActiveRecord
|
|
372
385
|
|
373
386
|
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
|
374
387
|
|
375
|
-
create_table(join_table_name, options.merge!(id: false)) do |td|
|
376
|
-
td.references t1_ref, column_options
|
377
|
-
td.references t2_ref, column_options
|
388
|
+
create_table(join_table_name, **options.merge!(id: false)) do |td|
|
389
|
+
td.references t1_ref, **column_options
|
390
|
+
td.references t2_ref, **column_options
|
378
391
|
yield td if block_given?
|
379
392
|
end
|
380
393
|
end
|
@@ -385,7 +398,7 @@ module ActiveRecord
|
|
385
398
|
# Although this command ignores the block if one is given, it can be helpful
|
386
399
|
# to provide one in a migration's +change+ method so it can be reverted.
|
387
400
|
# In that case, the block will be used by #create_join_table.
|
388
|
-
def drop_join_table(table_1, table_2, options
|
401
|
+
def drop_join_table(table_1, table_2, **options)
|
389
402
|
join_table_name = find_join_table_name(table_1, table_2, options)
|
390
403
|
drop_table(join_table_name)
|
391
404
|
end
|
@@ -414,6 +427,12 @@ module ActiveRecord
|
|
414
427
|
# t.column :name, :string, limit: 60
|
415
428
|
# end
|
416
429
|
#
|
430
|
+
# ====== Change type of a column
|
431
|
+
#
|
432
|
+
# change_table(:suppliers) do |t|
|
433
|
+
# t.change :metadata, :json
|
434
|
+
# end
|
435
|
+
#
|
417
436
|
# ====== Add 2 integer columns
|
418
437
|
#
|
419
438
|
# change_table(:suppliers) do |t|
|
@@ -462,7 +481,7 @@ module ActiveRecord
|
|
462
481
|
# end
|
463
482
|
#
|
464
483
|
# See also Table for details on all of the various column transformations.
|
465
|
-
def change_table(table_name, options
|
484
|
+
def change_table(table_name, **options)
|
466
485
|
if supports_bulk_alter? && options[:bulk]
|
467
486
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
468
487
|
yield update_table_definition(table_name, recorder)
|
@@ -492,7 +511,8 @@ module ActiveRecord
|
|
492
511
|
# Although this command ignores most +options+ and the block if one is given,
|
493
512
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
494
513
|
# In that case, +options+ and the block will be used by #create_table.
|
495
|
-
def drop_table(table_name, options
|
514
|
+
def drop_table(table_name, **options)
|
515
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
496
516
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
497
517
|
end
|
498
518
|
|
@@ -512,18 +532,25 @@ module ActiveRecord
|
|
512
532
|
# Available options are (none of these exists by default):
|
513
533
|
# * <tt>:limit</tt> -
|
514
534
|
# 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
|
535
|
+
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
|
516
536
|
# This option is ignored by some backends.
|
517
537
|
# * <tt>:default</tt> -
|
518
538
|
# The column's default value. Use +nil+ for +NULL+.
|
519
539
|
# * <tt>:null</tt> -
|
520
540
|
# Allows or disallows +NULL+ values in the column.
|
521
541
|
# * <tt>:precision</tt> -
|
522
|
-
# Specifies the precision for the <tt>:decimal</tt
|
542
|
+
# Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
|
543
|
+
# <tt>:datetime</tt>, and <tt>:time</tt> columns.
|
523
544
|
# * <tt>:scale</tt> -
|
524
545
|
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
|
546
|
+
# * <tt>:collation</tt> -
|
547
|
+
# Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
|
548
|
+
# column will have the same collation as the table.
|
525
549
|
# * <tt>:comment</tt> -
|
526
550
|
# Specifies the comment for the column. This option is ignored by some backends.
|
551
|
+
# * <tt>:if_not_exists</tt> -
|
552
|
+
# Specifies if the column already exists to not try to re-add it. This will avoid
|
553
|
+
# duplicate column errors.
|
527
554
|
#
|
528
555
|
# Note: The precision is the total number of significant digits,
|
529
556
|
# and the scale is the number of digits that can be stored following
|
@@ -544,8 +571,6 @@ module ActiveRecord
|
|
544
571
|
# but the maximum supported <tt>:precision</tt> is 16. No default.
|
545
572
|
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
|
546
573
|
# Default is (38,0).
|
547
|
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
|
548
|
-
# Default unknown.
|
549
574
|
# * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
|
550
575
|
# Default (38,0).
|
551
576
|
#
|
@@ -575,20 +600,37 @@ module ActiveRecord
|
|
575
600
|
# # Defines a column with a database-specific type.
|
576
601
|
# add_column(:shapes, :triangle, 'polygon')
|
577
602
|
# # ALTER TABLE "shapes" ADD "triangle" polygon
|
578
|
-
|
603
|
+
#
|
604
|
+
# # Ignores the method call if the column exists
|
605
|
+
# add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
|
606
|
+
def add_column(table_name, column_name, type, **options)
|
607
|
+
return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
|
608
|
+
|
579
609
|
at = create_alter_table table_name
|
580
|
-
at.add_column(column_name, type, options)
|
610
|
+
at.add_column(column_name, type, **options)
|
581
611
|
execute schema_creation.accept at
|
582
612
|
end
|
583
613
|
|
614
|
+
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
|
615
|
+
column_names.each do |column_name|
|
616
|
+
add_column(table_name, column_name, type, **options)
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
584
620
|
# Removes the given columns from the table definition.
|
585
621
|
#
|
586
622
|
# remove_columns(:suppliers, :qualification, :experience)
|
587
623
|
#
|
588
|
-
|
589
|
-
|
624
|
+
# +type+ and other column options can be passed to make migration reversible.
|
625
|
+
#
|
626
|
+
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
|
627
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
628
|
+
if column_names.empty?
|
629
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
|
630
|
+
end
|
631
|
+
|
590
632
|
column_names.each do |column_name|
|
591
|
-
remove_column(table_name, column_name)
|
633
|
+
remove_column(table_name, column_name, type, **options)
|
592
634
|
end
|
593
635
|
end
|
594
636
|
|
@@ -599,8 +641,17 @@ module ActiveRecord
|
|
599
641
|
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
|
600
642
|
# to provide these in a migration's +change+ method so it can be reverted.
|
601
643
|
# In that case, +type+ and +options+ will be used by #add_column.
|
602
|
-
|
603
|
-
|
644
|
+
# Indexes on the column are automatically removed.
|
645
|
+
#
|
646
|
+
# If the options provided include an +if_exists+ key, it will be used to check if the
|
647
|
+
# column does not exist. This will silently ignore the migration rather than raising
|
648
|
+
# if the column was already used.
|
649
|
+
#
|
650
|
+
# remove_column(:suppliers, :qualification, if_exists: true)
|
651
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
652
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
653
|
+
|
654
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
|
604
655
|
end
|
605
656
|
|
606
657
|
# Changes the column's definition according to the new options.
|
@@ -609,7 +660,7 @@ module ActiveRecord
|
|
609
660
|
# change_column(:suppliers, :name, :string, limit: 80)
|
610
661
|
# change_column(:accounts, :description, :text)
|
611
662
|
#
|
612
|
-
def change_column(table_name, column_name, type, options
|
663
|
+
def change_column(table_name, column_name, type, **options)
|
613
664
|
raise NotImplementedError, "change_column is not implemented"
|
614
665
|
end
|
615
666
|
|
@@ -671,7 +722,17 @@ module ActiveRecord
|
|
671
722
|
#
|
672
723
|
# generates:
|
673
724
|
#
|
674
|
-
# CREATE INDEX
|
725
|
+
# CREATE INDEX index_suppliers_on_name ON suppliers(name)
|
726
|
+
#
|
727
|
+
# ====== Creating a index which already exists
|
728
|
+
#
|
729
|
+
# add_index(:suppliers, :name, if_not_exists: true)
|
730
|
+
#
|
731
|
+
# generates:
|
732
|
+
#
|
733
|
+
# CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
|
734
|
+
#
|
735
|
+
# Note: Not supported by MySQL.
|
675
736
|
#
|
676
737
|
# ====== Creating a unique index
|
677
738
|
#
|
@@ -679,7 +740,7 @@ module ActiveRecord
|
|
679
740
|
#
|
680
741
|
# generates:
|
681
742
|
#
|
682
|
-
# CREATE UNIQUE INDEX
|
743
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
|
683
744
|
#
|
684
745
|
# ====== Creating a named index
|
685
746
|
#
|
@@ -709,7 +770,7 @@ module ActiveRecord
|
|
709
770
|
#
|
710
771
|
# ====== Creating an index with a sort order (desc or asc, asc is the default)
|
711
772
|
#
|
712
|
-
# add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
|
773
|
+
# add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
|
713
774
|
#
|
714
775
|
# generates:
|
715
776
|
#
|
@@ -725,7 +786,7 @@ module ActiveRecord
|
|
725
786
|
#
|
726
787
|
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
727
788
|
#
|
728
|
-
# Note: Partial indexes are only supported for PostgreSQL and SQLite
|
789
|
+
# Note: Partial indexes are only supported for PostgreSQL and SQLite.
|
729
790
|
#
|
730
791
|
# ====== Creating an index with a specific method
|
731
792
|
#
|
@@ -760,9 +821,22 @@ module ActiveRecord
|
|
760
821
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
761
822
|
#
|
762
823
|
# Note: only supported by MySQL.
|
763
|
-
|
764
|
-
|
765
|
-
|
824
|
+
#
|
825
|
+
# ====== Creating an index with a specific algorithm
|
826
|
+
#
|
827
|
+
# add_index(:developers, :name, algorithm: :concurrently)
|
828
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
829
|
+
#
|
830
|
+
# Note: only supported by PostgreSQL.
|
831
|
+
#
|
832
|
+
# Concurrently adding an index is not supported in a transaction.
|
833
|
+
#
|
834
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
835
|
+
def add_index(table_name, column_name, **options)
|
836
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
837
|
+
|
838
|
+
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
839
|
+
execute schema_creation.accept(create_index)
|
766
840
|
end
|
767
841
|
|
768
842
|
# Removes the given index from the table.
|
@@ -783,8 +857,29 @@ module ActiveRecord
|
|
783
857
|
#
|
784
858
|
# remove_index :accounts, name: :by_branch_party
|
785
859
|
#
|
786
|
-
|
787
|
-
|
860
|
+
# Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
|
861
|
+
#
|
862
|
+
# remove_index :accounts, :branch_id, name: :by_branch_party
|
863
|
+
#
|
864
|
+
# Checks if the index exists before trying to remove it. Will silently ignore indexes that
|
865
|
+
# don't exist.
|
866
|
+
#
|
867
|
+
# remove_index :accounts, if_exists: true
|
868
|
+
#
|
869
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
870
|
+
#
|
871
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
872
|
+
#
|
873
|
+
# Note: only supported by PostgreSQL.
|
874
|
+
#
|
875
|
+
# Concurrently removing an index is not supported in a transaction.
|
876
|
+
#
|
877
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
878
|
+
def remove_index(table_name, column_name = nil, **options)
|
879
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
880
|
+
|
881
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
882
|
+
|
788
883
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
789
884
|
end
|
790
885
|
|
@@ -795,6 +890,8 @@ module ActiveRecord
|
|
795
890
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
796
891
|
#
|
797
892
|
def rename_index(table_name, old_name, new_name)
|
893
|
+
old_name = old_name.to_s
|
894
|
+
new_name = new_name.to_s
|
798
895
|
validate_index_length!(table_name, new_name)
|
799
896
|
|
800
897
|
# this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
|
@@ -836,23 +933,25 @@ module ActiveRecord
|
|
836
933
|
# Add an appropriate index. Defaults to true.
|
837
934
|
# See #add_index for usage of this option.
|
838
935
|
# [<tt>:foreign_key</tt>]
|
839
|
-
# Add an appropriate foreign key constraint. Defaults to false
|
936
|
+
# Add an appropriate foreign key constraint. Defaults to false, pass true
|
937
|
+
# to add. In case the join table can't be inferred from the association
|
938
|
+
# pass <tt>:to_table</tt> with the appropriate table name.
|
840
939
|
# [<tt>:polymorphic</tt>]
|
841
940
|
# Whether an additional +_type+ column should be added. Defaults to false.
|
842
941
|
# [<tt>:null</tt>]
|
843
942
|
# Whether the column allows nulls. Defaults to true.
|
844
943
|
#
|
845
|
-
# ====== Create a user_id bigint column
|
944
|
+
# ====== Create a user_id bigint column without an index
|
846
945
|
#
|
847
|
-
# add_reference(:products, :user)
|
946
|
+
# add_reference(:products, :user, index: false)
|
848
947
|
#
|
849
948
|
# ====== Create a user_id string column
|
850
949
|
#
|
851
950
|
# add_reference(:products, :user, type: :string)
|
852
951
|
#
|
853
|
-
# ====== Create supplier_id, supplier_type columns
|
952
|
+
# ====== Create supplier_id, supplier_type columns
|
854
953
|
#
|
855
|
-
# add_reference(:products, :supplier, polymorphic: true
|
954
|
+
# add_reference(:products, :supplier, polymorphic: true)
|
856
955
|
#
|
857
956
|
# ====== Create a supplier_id column with a unique index
|
858
957
|
#
|
@@ -868,10 +967,10 @@ module ActiveRecord
|
|
868
967
|
#
|
869
968
|
# ====== Create a supplier_id column and a foreign key to the firms table
|
870
969
|
#
|
871
|
-
# add_reference(:products, :supplier, foreign_key: {to_table: :firms})
|
970
|
+
# add_reference(:products, :supplier, foreign_key: { to_table: :firms })
|
872
971
|
#
|
873
972
|
def add_reference(table_name, ref_name, **options)
|
874
|
-
ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
|
973
|
+
ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
|
875
974
|
end
|
876
975
|
alias :add_belongs_to :add_reference
|
877
976
|
|
@@ -880,7 +979,7 @@ module ActiveRecord
|
|
880
979
|
#
|
881
980
|
# ====== Remove the reference
|
882
981
|
#
|
883
|
-
# remove_reference(:products, :user, index:
|
982
|
+
# remove_reference(:products, :user, index: false)
|
884
983
|
#
|
885
984
|
# ====== Remove polymorphic reference
|
886
985
|
#
|
@@ -888,7 +987,7 @@ module ActiveRecord
|
|
888
987
|
#
|
889
988
|
# ====== Remove the reference with a foreign key
|
890
989
|
#
|
891
|
-
# remove_reference(:products, :user,
|
990
|
+
# remove_reference(:products, :user, foreign_key: true)
|
892
991
|
#
|
893
992
|
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
894
993
|
if foreign_key
|
@@ -899,7 +998,7 @@ module ActiveRecord
|
|
899
998
|
foreign_key_options = { to_table: reference_name }
|
900
999
|
end
|
901
1000
|
foreign_key_options[:column] ||= "#{ref_name}_id"
|
902
|
-
remove_foreign_key(table_name, foreign_key_options)
|
1001
|
+
remove_foreign_key(table_name, **foreign_key_options)
|
903
1002
|
end
|
904
1003
|
|
905
1004
|
remove_column(table_name, "#{ref_name}_id")
|
@@ -956,8 +1055,8 @@ module ActiveRecord
|
|
956
1055
|
# [<tt>:on_update</tt>]
|
957
1056
|
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
958
1057
|
# [<tt>:validate</tt>]
|
959
|
-
# (
|
960
|
-
def add_foreign_key(from_table, to_table, options
|
1058
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1059
|
+
def add_foreign_key(from_table, to_table, **options)
|
961
1060
|
return unless supports_foreign_keys?
|
962
1061
|
|
963
1062
|
options = foreign_key_options(from_table, to_table, options)
|
@@ -980,15 +1079,22 @@ module ActiveRecord
|
|
980
1079
|
#
|
981
1080
|
# remove_foreign_key :accounts, column: :owner_id
|
982
1081
|
#
|
1082
|
+
# Removes the foreign key on +accounts.owner_id+.
|
1083
|
+
#
|
1084
|
+
# remove_foreign_key :accounts, to_table: :owners
|
1085
|
+
#
|
983
1086
|
# Removes the foreign key named +special_fk_name+ on the +accounts+ table.
|
984
1087
|
#
|
985
1088
|
# remove_foreign_key :accounts, name: :special_fk_name
|
986
1089
|
#
|
987
|
-
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
988
|
-
|
1090
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
|
1091
|
+
# with an addition of
|
1092
|
+
# [<tt>:to_table</tt>]
|
1093
|
+
# The name of the table that contains the referenced primary key.
|
1094
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
989
1095
|
return unless supports_foreign_keys?
|
990
1096
|
|
991
|
-
fk_name_to_delete = foreign_key_for!(from_table,
|
1097
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
992
1098
|
|
993
1099
|
at = create_alter_table from_table
|
994
1100
|
at.drop_foreign_key fk_name_to_delete
|
@@ -1007,14 +1113,12 @@ module ActiveRecord
|
|
1007
1113
|
# # Checks to see if a foreign key with a custom name exists.
|
1008
1114
|
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1009
1115
|
#
|
1010
|
-
def foreign_key_exists?(from_table,
|
1011
|
-
foreign_key_for(from_table,
|
1116
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1117
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1012
1118
|
end
|
1013
1119
|
|
1014
1120
|
def foreign_key_column_for(table_name) # :nodoc:
|
1015
|
-
|
1016
|
-
suffix = Base.table_name_suffix
|
1017
|
-
name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1121
|
+
name = strip_table_name_prefix_and_suffix(table_name)
|
1018
1122
|
"#{name.singularize}_id"
|
1019
1123
|
end
|
1020
1124
|
|
@@ -1025,8 +1129,62 @@ module ActiveRecord
|
|
1025
1129
|
options
|
1026
1130
|
end
|
1027
1131
|
|
1028
|
-
|
1029
|
-
|
1132
|
+
# Returns an array of check constraints for the given table.
|
1133
|
+
# The check constraints are represented as CheckConstraintDefinition objects.
|
1134
|
+
def check_constraints(table_name)
|
1135
|
+
raise NotImplementedError
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# Adds a new check constraint to the table. +expression+ is a String
|
1139
|
+
# representation of verifiable boolean condition.
|
1140
|
+
#
|
1141
|
+
# add_check_constraint :products, "price > 0", name: "price_check"
|
1142
|
+
#
|
1143
|
+
# generates:
|
1144
|
+
#
|
1145
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
|
1146
|
+
#
|
1147
|
+
# The +options+ hash can include the following keys:
|
1148
|
+
# [<tt>:name</tt>]
|
1149
|
+
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
|
1150
|
+
# [<tt>:validate</tt>]
|
1151
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
1152
|
+
def add_check_constraint(table_name, expression, **options)
|
1153
|
+
return unless supports_check_constraints?
|
1154
|
+
|
1155
|
+
options = check_constraint_options(table_name, expression, options)
|
1156
|
+
at = create_alter_table(table_name)
|
1157
|
+
at.add_check_constraint(expression, options)
|
1158
|
+
|
1159
|
+
execute schema_creation.accept(at)
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
def check_constraint_options(table_name, expression, options) # :nodoc:
|
1163
|
+
options = options.dup
|
1164
|
+
options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
|
1165
|
+
options
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
# Removes the given check constraint from the table.
|
1169
|
+
#
|
1170
|
+
# remove_check_constraint :products, name: "price_check"
|
1171
|
+
#
|
1172
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
1173
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
1174
|
+
# In that case, +expression+ will be used by #add_check_constraint.
|
1175
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
1176
|
+
return unless supports_check_constraints?
|
1177
|
+
|
1178
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
1179
|
+
|
1180
|
+
at = create_alter_table(table_name)
|
1181
|
+
at.drop_check_constraint(chk_name_to_delete)
|
1182
|
+
|
1183
|
+
execute schema_creation.accept(at)
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
def dump_schema_information # :nodoc:
|
1187
|
+
versions = schema_migration.all_versions
|
1030
1188
|
insert_versions_sql(versions) if versions.any?
|
1031
1189
|
end
|
1032
1190
|
|
@@ -1034,15 +1192,12 @@ module ActiveRecord
|
|
1034
1192
|
{ primary_key: true }
|
1035
1193
|
end
|
1036
1194
|
|
1037
|
-
def assume_migrated_upto_version(version
|
1038
|
-
migrations_paths = Array(migrations_paths)
|
1195
|
+
def assume_migrated_upto_version(version)
|
1039
1196
|
version = version.to_i
|
1040
|
-
sm_table = quote_table_name(
|
1197
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1041
1198
|
|
1042
|
-
migrated =
|
1043
|
-
versions = migration_context.
|
1044
|
-
migration_context.parse_migration_filename(file).first.to_i
|
1045
|
-
end
|
1199
|
+
migrated = migration_context.get_all_versions
|
1200
|
+
versions = migration_context.migrations.map(&:version)
|
1046
1201
|
|
1047
1202
|
unless migrated.include?(version)
|
1048
1203
|
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
|
@@ -1053,13 +1208,7 @@ module ActiveRecord
|
|
1053
1208
|
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
1054
1209
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1055
1210
|
end
|
1056
|
-
|
1057
|
-
execute insert_versions_sql(inserting)
|
1058
|
-
else
|
1059
|
-
inserting.each do |v|
|
1060
|
-
execute insert_versions_sql(v)
|
1061
|
-
end
|
1062
|
-
end
|
1211
|
+
execute insert_versions_sql(inserting)
|
1063
1212
|
end
|
1064
1213
|
end
|
1065
1214
|
|
@@ -1085,7 +1234,7 @@ module ActiveRecord
|
|
1085
1234
|
if (0..6) === precision
|
1086
1235
|
column_type_sql << "(#{precision})"
|
1087
1236
|
else
|
1088
|
-
raise
|
1237
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1089
1238
|
end
|
1090
1239
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
1091
1240
|
column_type_sql << "(#{limit})"
|
@@ -1112,18 +1261,22 @@ module ActiveRecord
|
|
1112
1261
|
#
|
1113
1262
|
# add_timestamps(:suppliers, null: true)
|
1114
1263
|
#
|
1115
|
-
def add_timestamps(table_name, options
|
1264
|
+
def add_timestamps(table_name, **options)
|
1116
1265
|
options[:null] = false if options[:null].nil?
|
1117
1266
|
|
1118
|
-
|
1119
|
-
|
1267
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1268
|
+
options[:precision] = 6
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
add_column table_name, :created_at, :datetime, **options
|
1272
|
+
add_column table_name, :updated_at, :datetime, **options
|
1120
1273
|
end
|
1121
1274
|
|
1122
1275
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
1123
1276
|
#
|
1124
1277
|
# remove_timestamps(:suppliers)
|
1125
1278
|
#
|
1126
|
-
def remove_timestamps(table_name, options
|
1279
|
+
def remove_timestamps(table_name, **options)
|
1127
1280
|
remove_column table_name, :updated_at
|
1128
1281
|
remove_column table_name, :created_at
|
1129
1282
|
end
|
@@ -1132,36 +1285,43 @@ module ActiveRecord
|
|
1132
1285
|
Table.new(table_name, base)
|
1133
1286
|
end
|
1134
1287
|
|
1135
|
-
def add_index_options(table_name, column_name,
|
1136
|
-
|
1288
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
1289
|
+
options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
|
1137
1290
|
|
1138
|
-
|
1291
|
+
column_names = index_column_names(column_name)
|
1139
1292
|
|
1140
|
-
|
1141
|
-
index_type ||= options[:unique] ? "UNIQUE" : ""
|
1142
|
-
index_name = options[:name].to_s if options.key?(:name)
|
1293
|
+
index_name = name&.to_s
|
1143
1294
|
index_name ||= index_name(table_name, column_names)
|
1144
1295
|
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1296
|
+
validate_index_length!(table_name, index_name, internal)
|
1297
|
+
|
1298
|
+
index = IndexDefinition.new(
|
1299
|
+
table_name, index_name,
|
1300
|
+
options[:unique],
|
1301
|
+
column_names,
|
1302
|
+
lengths: options[:length] || {},
|
1303
|
+
orders: options[:order] || {},
|
1304
|
+
opclasses: options[:opclass] || {},
|
1305
|
+
where: options[:where],
|
1306
|
+
type: options[:type],
|
1307
|
+
using: options[:using],
|
1308
|
+
comment: options[:comment]
|
1309
|
+
)
|
1310
|
+
|
1311
|
+
[index, index_algorithm(options[:algorithm]), if_not_exists]
|
1312
|
+
end
|
1156
1313
|
|
1157
|
-
|
1314
|
+
def index_algorithm(algorithm) # :nodoc:
|
1315
|
+
index_algorithms.fetch(algorithm) do
|
1316
|
+
raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
|
1317
|
+
end if algorithm
|
1318
|
+
end
|
1158
1319
|
|
1159
|
-
|
1160
|
-
|
1320
|
+
def quoted_columns_for_index(column_names, options) # :nodoc:
|
1321
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
1322
|
+
result[name.to_sym] = quote_column_name(name).dup
|
1161
1323
|
end
|
1162
|
-
|
1163
|
-
|
1164
|
-
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
1324
|
+
add_options_for_index_columns(quoted_columns, **options).values.join(", ")
|
1165
1325
|
end
|
1166
1326
|
|
1167
1327
|
def options_include_default?(options)
|
@@ -1169,12 +1329,22 @@ module ActiveRecord
|
|
1169
1329
|
end
|
1170
1330
|
|
1171
1331
|
# Changes the comment for a table or removes it if +nil+.
|
1172
|
-
|
1332
|
+
#
|
1333
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1334
|
+
# reversible in migration:
|
1335
|
+
#
|
1336
|
+
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
|
1337
|
+
def change_table_comment(table_name, comment_or_changes)
|
1173
1338
|
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1174
1339
|
end
|
1175
1340
|
|
1176
1341
|
# Changes the comment for a column or removes it if +nil+.
|
1177
|
-
|
1342
|
+
#
|
1343
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1344
|
+
# reversible in migration:
|
1345
|
+
#
|
1346
|
+
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
|
1347
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
1178
1348
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1179
1349
|
end
|
1180
1350
|
|
@@ -1206,31 +1376,26 @@ module ActiveRecord
|
|
1206
1376
|
# the PostgreSQL adapter for supporting operator classes.
|
1207
1377
|
def add_options_for_index_columns(quoted_columns, **options)
|
1208
1378
|
if supports_index_sort_order?
|
1209
|
-
quoted_columns = add_index_sort_order(quoted_columns, options)
|
1379
|
+
quoted_columns = add_index_sort_order(quoted_columns, **options)
|
1210
1380
|
end
|
1211
1381
|
|
1212
1382
|
quoted_columns
|
1213
1383
|
end
|
1214
1384
|
|
1215
|
-
def
|
1216
|
-
return [
|
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
|
1220
|
-
end
|
1221
|
-
|
1222
|
-
def index_name_for_remove(table_name, options = {})
|
1223
|
-
return options[:name] if can_remove_index_by_name?(options)
|
1385
|
+
def index_name_for_remove(table_name, column_name, options)
|
1386
|
+
return options[:name] if can_remove_index_by_name?(column_name, options)
|
1224
1387
|
|
1225
1388
|
checks = []
|
1226
1389
|
|
1227
|
-
if options.is_a?(
|
1228
|
-
|
1229
|
-
column_names =
|
1390
|
+
if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
|
1391
|
+
options[:name] = index_name(table_name, column_name)
|
1392
|
+
column_names = []
|
1230
1393
|
else
|
1231
|
-
column_names = index_column_names(options)
|
1394
|
+
column_names = index_column_names(column_name || options[:column])
|
1232
1395
|
end
|
1233
1396
|
|
1397
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1398
|
+
|
1234
1399
|
if column_names.present?
|
1235
1400
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1236
1401
|
end
|
@@ -1275,14 +1440,18 @@ module ActiveRecord
|
|
1275
1440
|
SchemaCreation.new(self)
|
1276
1441
|
end
|
1277
1442
|
|
1278
|
-
def create_table_definition(
|
1279
|
-
TableDefinition.new(
|
1443
|
+
def create_table_definition(name, **options)
|
1444
|
+
TableDefinition.new(self, name, **options)
|
1280
1445
|
end
|
1281
1446
|
|
1282
1447
|
def create_alter_table(name)
|
1283
1448
|
AlterTable.new create_table_definition(name)
|
1284
1449
|
end
|
1285
1450
|
|
1451
|
+
def extract_table_options!(options)
|
1452
|
+
options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
|
1453
|
+
end
|
1454
|
+
|
1286
1455
|
def fetch_type_metadata(sql_type)
|
1287
1456
|
cast_type = lookup_cast_type(sql_type)
|
1288
1457
|
SqlTypeMetadata.new(
|
@@ -1310,6 +1479,12 @@ module ActiveRecord
|
|
1310
1479
|
{ column: column_names }
|
1311
1480
|
end
|
1312
1481
|
|
1482
|
+
def strip_table_name_prefix_and_suffix(table_name)
|
1483
|
+
prefix = Base.table_name_prefix
|
1484
|
+
suffix = Base.table_name_suffix
|
1485
|
+
table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
|
1486
|
+
end
|
1487
|
+
|
1313
1488
|
def foreign_key_name(table_name, options)
|
1314
1489
|
options.fetch(:name) do
|
1315
1490
|
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
@@ -1319,14 +1494,14 @@ module ActiveRecord
|
|
1319
1494
|
end
|
1320
1495
|
end
|
1321
1496
|
|
1322
|
-
def foreign_key_for(from_table,
|
1497
|
+
def foreign_key_for(from_table, **options)
|
1323
1498
|
return unless supports_foreign_keys?
|
1324
|
-
foreign_keys(from_table).detect { |fk| fk.defined_for?
|
1499
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
|
1325
1500
|
end
|
1326
1501
|
|
1327
|
-
def foreign_key_for!(from_table,
|
1328
|
-
foreign_key_for(from_table,
|
1329
|
-
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{
|
1502
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
1503
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
1504
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
1330
1505
|
end
|
1331
1506
|
|
1332
1507
|
def extract_foreign_key_action(specifier)
|
@@ -1337,11 +1512,30 @@ module ActiveRecord
|
|
1337
1512
|
end
|
1338
1513
|
end
|
1339
1514
|
|
1340
|
-
def
|
1341
|
-
|
1515
|
+
def check_constraint_name(table_name, **options)
|
1516
|
+
options.fetch(:name) do
|
1517
|
+
expression = options.fetch(:expression)
|
1518
|
+
identifier = "#{table_name}_#{expression}_chk"
|
1519
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
1342
1520
|
|
1343
|
-
|
1344
|
-
|
1521
|
+
"chk_rails_#{hashed_identifier}"
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
def check_constraint_for(table_name, **options)
|
1526
|
+
return unless supports_check_constraints?
|
1527
|
+
chk_name = check_constraint_name(table_name, **options)
|
1528
|
+
check_constraints(table_name).detect { |chk| chk.name == chk_name }
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
def check_constraint_for!(table_name, expression: nil, **options)
|
1532
|
+
check_constraint_for(table_name, expression: expression, **options) ||
|
1533
|
+
raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
1537
|
+
if new_name.length > index_name_length
|
1538
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
1345
1539
|
end
|
1346
1540
|
end
|
1347
1541
|
|
@@ -1352,30 +1546,77 @@ module ActiveRecord
|
|
1352
1546
|
default_or_changes
|
1353
1547
|
end
|
1354
1548
|
end
|
1549
|
+
alias :extract_new_comment_value :extract_new_default_value
|
1355
1550
|
|
1356
|
-
def can_remove_index_by_name?(options)
|
1357
|
-
|
1551
|
+
def can_remove_index_by_name?(column_name, options)
|
1552
|
+
column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1358
1553
|
end
|
1359
1554
|
|
1360
|
-
def
|
1555
|
+
def bulk_change_table(table_name, operations)
|
1556
|
+
sql_fragments = []
|
1557
|
+
non_combinable_operations = []
|
1558
|
+
|
1559
|
+
operations.each do |command, args|
|
1560
|
+
table, arguments = args.shift, args
|
1561
|
+
method = :"#{command}_for_alter"
|
1562
|
+
|
1563
|
+
if respond_to?(method, true)
|
1564
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1565
|
+
sql_fragments << sqls
|
1566
|
+
non_combinable_operations.concat(procs)
|
1567
|
+
else
|
1568
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1569
|
+
non_combinable_operations.each(&:call)
|
1570
|
+
sql_fragments = []
|
1571
|
+
non_combinable_operations = []
|
1572
|
+
send(command, table, *arguments)
|
1573
|
+
end
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1577
|
+
non_combinable_operations.each(&:call)
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
1361
1581
|
td = create_table_definition(table_name)
|
1362
|
-
cd = td.new_column_definition(column_name, type, options)
|
1582
|
+
cd = td.new_column_definition(column_name, type, **options)
|
1363
1583
|
schema_creation.accept(AddColumnDefinition.new(cd))
|
1364
1584
|
end
|
1365
1585
|
|
1366
|
-
def
|
1586
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
1587
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
def remove_column_for_alter(table_name, column_name, type = nil, **options)
|
1367
1591
|
"DROP COLUMN #{quote_column_name(column_name)}"
|
1368
1592
|
end
|
1369
1593
|
|
1370
|
-
def remove_columns_for_alter(table_name, *column_names)
|
1594
|
+
def remove_columns_for_alter(table_name, *column_names, **options)
|
1371
1595
|
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
1372
1596
|
end
|
1373
1597
|
|
1598
|
+
def add_timestamps_for_alter(table_name, **options)
|
1599
|
+
options[:null] = false if options[:null].nil?
|
1600
|
+
|
1601
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
1602
|
+
options[:precision] = 6
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
[
|
1606
|
+
add_column_for_alter(table_name, :created_at, :datetime, **options),
|
1607
|
+
add_column_for_alter(table_name, :updated_at, :datetime, **options)
|
1608
|
+
]
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
def remove_timestamps_for_alter(table_name, **options)
|
1612
|
+
remove_columns_for_alter(table_name, :updated_at, :created_at)
|
1613
|
+
end
|
1614
|
+
|
1374
1615
|
def insert_versions_sql(versions)
|
1375
|
-
sm_table = quote_table_name(
|
1616
|
+
sm_table = quote_table_name(schema_migration.table_name)
|
1376
1617
|
|
1377
1618
|
if versions.is_a?(Array)
|
1378
|
-
sql = "INSERT INTO #{sm_table} (version) VALUES\n"
|
1619
|
+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
1379
1620
|
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
|
1380
1621
|
sql << ";\n\n"
|
1381
1622
|
sql
|