activerecord 5.2.8.1 → 6.1.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1347 -624
- 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 +16 -7
- 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 +107 -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 +73 -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 +225 -121
- 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 +341 -99
- 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 +4 -4
- 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 +113 -74
- 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 +478 -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 +94 -10
- 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 +291 -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 +118 -32
- 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
|