activerecord 6.0.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -25
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +171 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- 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 +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
|
6
6
|
module SchemaStatements
|
|
7
7
|
# Drops the database specified on the +name+ attribute
|
|
8
8
|
# and creates it again using the provided +options+.
|
|
9
|
-
def recreate_database(name, options = {})
|
|
9
|
+
def recreate_database(name, options = {}) # :nodoc:
|
|
10
10
|
drop_database(name)
|
|
11
11
|
create_database(name, options)
|
|
12
12
|
end
|
|
@@ -50,11 +50,12 @@ module ActiveRecord
|
|
|
50
50
|
#
|
|
51
51
|
# Example:
|
|
52
52
|
# drop_database 'matt_development'
|
|
53
|
-
def drop_database(name)
|
|
53
|
+
def drop_database(name) # :nodoc:
|
|
54
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
def drop_table(table_name, options
|
|
57
|
+
def drop_table(table_name, **options) # :nodoc:
|
|
58
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
58
59
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
|
59
60
|
end
|
|
60
61
|
|
|
@@ -73,11 +74,11 @@ module ActiveRecord
|
|
|
73
74
|
FROM pg_class t
|
|
74
75
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
|
75
76
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
|
76
|
-
LEFT JOIN pg_namespace n ON n.oid =
|
|
77
|
-
WHERE i.relkind
|
|
77
|
+
LEFT JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
78
|
+
WHERE i.relkind IN ('i', 'I')
|
|
78
79
|
AND i.relname = #{index[:name]}
|
|
79
80
|
AND t.relname = #{table[:name]}
|
|
80
|
-
AND n.nspname = #{
|
|
81
|
+
AND n.nspname = #{table[:schema]}
|
|
81
82
|
SQL
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -87,12 +88,12 @@ module ActiveRecord
|
|
|
87
88
|
|
|
88
89
|
result = query(<<~SQL, "SCHEMA")
|
|
89
90
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
|
90
|
-
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
|
91
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment, d.indisvalid
|
|
91
92
|
FROM pg_class t
|
|
92
93
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
|
93
94
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
|
94
|
-
LEFT JOIN pg_namespace n ON n.oid =
|
|
95
|
-
WHERE i.relkind
|
|
95
|
+
LEFT JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
96
|
+
WHERE i.relkind IN ('i', 'I')
|
|
96
97
|
AND d.indisprimary = 'f'
|
|
97
98
|
AND t.relname = #{scope[:name]}
|
|
98
99
|
AND n.nspname = #{scope[:schema]}
|
|
@@ -106,25 +107,24 @@ module ActiveRecord
|
|
|
106
107
|
inddef = row[3]
|
|
107
108
|
oid = row[4]
|
|
108
109
|
comment = row[5]
|
|
109
|
-
|
|
110
|
-
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
|
110
|
+
valid = row[6]
|
|
111
|
+
using, expressions, include, nulls_not_distinct, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: INCLUDE \((.+?)\))?( NULLS NOT DISTINCT)?(?: WHERE (.+))?\z/m).flatten
|
|
111
112
|
|
|
112
113
|
orders = {}
|
|
113
114
|
opclasses = {}
|
|
115
|
+
include_columns = include ? include.split(",").map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) } : []
|
|
114
116
|
|
|
115
117
|
if indkey.include?(0)
|
|
116
118
|
columns = expressions
|
|
117
119
|
else
|
|
118
|
-
columns =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
AND a.attnum IN (#{indkey.join(",")})
|
|
123
|
-
SQL
|
|
120
|
+
columns = column_names_from_column_numbers(oid, indkey)
|
|
121
|
+
|
|
122
|
+
# prevent INCLUDE columns from being matched
|
|
123
|
+
columns.reject! { |c| include_columns.include?(c) }
|
|
124
124
|
|
|
125
125
|
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
|
126
126
|
# and non-default opclasses
|
|
127
|
-
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
|
127
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops(_\w+)?)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
|
128
128
|
opclasses[column] = opclass.to_sym if opclass
|
|
129
129
|
if nulls
|
|
130
130
|
orders[column] = [desc, nulls].compact.join(" ")
|
|
@@ -143,7 +143,10 @@ module ActiveRecord
|
|
|
143
143
|
opclasses: opclasses,
|
|
144
144
|
where: where,
|
|
145
145
|
using: using.to_sym,
|
|
146
|
-
|
|
146
|
+
include: include_columns.presence,
|
|
147
|
+
nulls_not_distinct: nulls_not_distinct.present?,
|
|
148
|
+
comment: comment.presence,
|
|
149
|
+
valid: valid
|
|
147
150
|
)
|
|
148
151
|
end
|
|
149
152
|
end
|
|
@@ -206,12 +209,20 @@ module ActiveRecord
|
|
|
206
209
|
end
|
|
207
210
|
|
|
208
211
|
# Creates a schema for the given schema name.
|
|
209
|
-
def create_schema(schema_name)
|
|
210
|
-
|
|
212
|
+
def create_schema(schema_name, force: nil, if_not_exists: nil)
|
|
213
|
+
if force && if_not_exists
|
|
214
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
if force
|
|
218
|
+
drop_schema(schema_name, if_exists: true)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
execute("CREATE SCHEMA#{' IF NOT EXISTS' if if_not_exists} #{quote_schema_name(schema_name)}")
|
|
211
222
|
end
|
|
212
223
|
|
|
213
224
|
# Drops the schema for the given schema name.
|
|
214
|
-
def drop_schema(schema_name, options
|
|
225
|
+
def drop_schema(schema_name, **options)
|
|
215
226
|
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
|
216
227
|
end
|
|
217
228
|
|
|
@@ -222,7 +233,7 @@ module ActiveRecord
|
|
|
222
233
|
# This should be not be called manually but set in database.yml.
|
|
223
234
|
def schema_search_path=(schema_csv)
|
|
224
235
|
if schema_csv
|
|
225
|
-
|
|
236
|
+
internal_execute("SET search_path TO #{schema_csv}")
|
|
226
237
|
@schema_search_path = schema_csv
|
|
227
238
|
end
|
|
228
239
|
end
|
|
@@ -239,11 +250,13 @@ module ActiveRecord
|
|
|
239
250
|
|
|
240
251
|
# Set the client message level.
|
|
241
252
|
def client_min_messages=(level)
|
|
242
|
-
|
|
253
|
+
internal_execute("SET client_min_messages TO '#{level}'")
|
|
243
254
|
end
|
|
244
255
|
|
|
245
256
|
# Returns the sequence name for a table's primary key or some other specified key.
|
|
246
|
-
def default_sequence_name(table_name, pk = "id")
|
|
257
|
+
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
|
258
|
+
return nil if pk.is_a?(Array)
|
|
259
|
+
|
|
247
260
|
result = serial_sequence(table_name, pk)
|
|
248
261
|
return nil unless result
|
|
249
262
|
Utils.extract_schema_qualified_name(result).to_s
|
|
@@ -256,7 +269,7 @@ module ActiveRecord
|
|
|
256
269
|
end
|
|
257
270
|
|
|
258
271
|
# Sets the sequence of a table's primary key to the specified value.
|
|
259
|
-
def set_pk_sequence!(table, value)
|
|
272
|
+
def set_pk_sequence!(table, value) # :nodoc:
|
|
260
273
|
pk, sequence = pk_and_sequence_for(table)
|
|
261
274
|
|
|
262
275
|
if pk
|
|
@@ -271,7 +284,7 @@ module ActiveRecord
|
|
|
271
284
|
end
|
|
272
285
|
|
|
273
286
|
# Resets the sequence of a table's primary key to the maximum value.
|
|
274
|
-
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
|
287
|
+
def reset_pk_sequence!(table, pk = nil, sequence = nil) # :nodoc:
|
|
275
288
|
unless pk && sequence
|
|
276
289
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
|
277
290
|
|
|
@@ -287,19 +300,19 @@ module ActiveRecord
|
|
|
287
300
|
quoted_sequence = quote_table_name(sequence)
|
|
288
301
|
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
|
289
302
|
if max_pk.nil?
|
|
290
|
-
if database_version >=
|
|
303
|
+
if database_version >= 10_00_00
|
|
291
304
|
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
|
292
305
|
else
|
|
293
306
|
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
|
294
307
|
end
|
|
295
308
|
end
|
|
296
309
|
|
|
297
|
-
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk
|
|
310
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk || minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
|
298
311
|
end
|
|
299
312
|
end
|
|
300
313
|
|
|
301
314
|
# Returns a table's primary key and belonging sequence.
|
|
302
|
-
def pk_and_sequence_for(table)
|
|
315
|
+
def pk_and_sequence_for(table) # :nodoc:
|
|
303
316
|
# First try looking for a sequence with a dependency on the
|
|
304
317
|
# given table's primary key.
|
|
305
318
|
result = query(<<~SQL, "SCHEMA")[0]
|
|
@@ -338,7 +351,7 @@ module ActiveRecord
|
|
|
338
351
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
|
339
352
|
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
|
340
353
|
AND cons.contype = 'p'
|
|
341
|
-
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
|
354
|
+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid'
|
|
342
355
|
SQL
|
|
343
356
|
end
|
|
344
357
|
|
|
@@ -374,47 +387,78 @@ module ActiveRecord
|
|
|
374
387
|
#
|
|
375
388
|
# Example:
|
|
376
389
|
# rename_table('octopuses', 'octopi')
|
|
377
|
-
def rename_table(table_name, new_name)
|
|
390
|
+
def rename_table(table_name, new_name, **options)
|
|
391
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
|
378
392
|
clear_cache!
|
|
393
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
394
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
379
395
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
380
396
|
pk, seq = pk_and_sequence_for(new_name)
|
|
381
397
|
if pk
|
|
382
|
-
|
|
383
|
-
|
|
398
|
+
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
|
399
|
+
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
|
400
|
+
max_pkey_prefix = max_identifier_length - "_pkey".size
|
|
401
|
+
idx = "#{table_name[0, max_pkey_prefix]}_pkey"
|
|
402
|
+
new_idx = "#{new_name[0, max_pkey_prefix]}_pkey"
|
|
384
403
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
|
385
|
-
|
|
386
|
-
|
|
404
|
+
|
|
405
|
+
# PostgreSQL automatically creates a sequence for PRIMARY KEY with name consisting of
|
|
406
|
+
# truncated table name and "#{primary_key}_seq" suffix fitting into max_identifier_length number of characters.
|
|
407
|
+
max_seq_prefix = max_identifier_length - "_#{pk}_seq".size
|
|
408
|
+
if seq && seq.identifier == "#{table_name[0, max_seq_prefix]}_#{pk}_seq"
|
|
409
|
+
new_seq = "#{new_name[0, max_seq_prefix]}_#{pk}_seq"
|
|
387
410
|
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
|
388
411
|
end
|
|
389
412
|
end
|
|
390
|
-
rename_table_indexes(table_name, new_name)
|
|
413
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
391
414
|
end
|
|
392
415
|
|
|
393
|
-
def add_column(table_name, column_name, type, options
|
|
416
|
+
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
394
417
|
clear_cache!
|
|
395
418
|
super
|
|
396
419
|
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
|
397
420
|
end
|
|
398
421
|
|
|
399
|
-
def change_column(table_name, column_name, type, options
|
|
422
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
|
400
423
|
clear_cache!
|
|
401
|
-
sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
|
|
424
|
+
sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
|
|
402
425
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
|
|
403
426
|
procs.each(&:call)
|
|
404
427
|
end
|
|
405
428
|
|
|
429
|
+
# Builds a ChangeColumnDefinition object.
|
|
430
|
+
#
|
|
431
|
+
# This definition object contains information about the column change that would occur
|
|
432
|
+
# if the same arguments were passed to #change_column. See #change_column for information about
|
|
433
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
434
|
+
def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
|
|
435
|
+
td = create_table_definition(table_name)
|
|
436
|
+
cd = td.new_column_definition(column_name, type, **options)
|
|
437
|
+
ChangeColumnDefinition.new(cd, column_name)
|
|
438
|
+
end
|
|
439
|
+
|
|
406
440
|
# Changes the default value of a table column.
|
|
407
441
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
|
408
442
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
|
409
443
|
end
|
|
410
444
|
|
|
411
|
-
def
|
|
445
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
|
446
|
+
column = column_for(table_name, column_name)
|
|
447
|
+
return unless column
|
|
448
|
+
|
|
449
|
+
default = extract_new_default_value(default_or_changes)
|
|
450
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
|
454
|
+
validate_change_column_null_argument!(null)
|
|
455
|
+
|
|
412
456
|
clear_cache!
|
|
413
457
|
unless null || default.nil?
|
|
414
458
|
column = column_for(table_name, column_name)
|
|
415
459
|
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column
|
|
416
460
|
end
|
|
417
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{
|
|
461
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
|
418
462
|
end
|
|
419
463
|
|
|
420
464
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
|
@@ -432,23 +476,30 @@ module ActiveRecord
|
|
|
432
476
|
end
|
|
433
477
|
|
|
434
478
|
# Renames a column in a table.
|
|
435
|
-
def rename_column(table_name, column_name, new_column_name)
|
|
479
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
|
436
480
|
clear_cache!
|
|
437
|
-
execute
|
|
481
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
|
438
482
|
rename_column_indexes(table_name, column_name, new_column_name)
|
|
439
483
|
end
|
|
440
484
|
|
|
441
|
-
def add_index(table_name, column_name, options
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
485
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
|
486
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
487
|
+
result = execute schema_creation.accept(create_index)
|
|
488
|
+
|
|
489
|
+
index = create_index.index
|
|
490
|
+
execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
|
|
491
|
+
result
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
|
495
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
496
|
+
CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
|
446
497
|
end
|
|
447
498
|
|
|
448
|
-
def remove_index(table_name,
|
|
499
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
|
449
500
|
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
|
450
501
|
|
|
451
|
-
if options.
|
|
502
|
+
if options.key?(:name)
|
|
452
503
|
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
|
453
504
|
|
|
454
505
|
options[:name] = provided_index.identifier
|
|
@@ -459,14 +510,11 @@ module ActiveRecord
|
|
|
459
510
|
end
|
|
460
511
|
end
|
|
461
512
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
end
|
|
468
|
-
end
|
|
469
|
-
execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
|
|
513
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
|
514
|
+
|
|
515
|
+
index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, column_name, options))
|
|
516
|
+
|
|
517
|
+
execute "DROP INDEX #{index_algorithm(options[:algorithm])} #{quote_table_name(index_to_remove)}"
|
|
470
518
|
end
|
|
471
519
|
|
|
472
520
|
# Renames an index of a table. Raises error if length of new
|
|
@@ -474,13 +522,25 @@ module ActiveRecord
|
|
|
474
522
|
def rename_index(table_name, old_name, new_name)
|
|
475
523
|
validate_index_length!(table_name, new_name)
|
|
476
524
|
|
|
477
|
-
|
|
525
|
+
schema, = extract_schema_qualified_name(table_name)
|
|
526
|
+
execute "ALTER INDEX #{quote_table_name(schema) + '.' if schema}#{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def index_name(table_name, options) # :nodoc:
|
|
530
|
+
_schema, table_name = extract_schema_qualified_name(table_name.to_s)
|
|
531
|
+
super
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def add_foreign_key(from_table, to_table, **options)
|
|
535
|
+
assert_valid_deferrable(options[:deferrable])
|
|
536
|
+
|
|
537
|
+
super
|
|
478
538
|
end
|
|
479
539
|
|
|
480
540
|
def foreign_keys(table_name)
|
|
481
541
|
scope = quoted_scope(table_name)
|
|
482
|
-
fk_info =
|
|
483
|
-
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
|
|
542
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
543
|
+
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred, c.conkey, c.confkey, c.conrelid, c.confrelid
|
|
484
544
|
FROM pg_constraint c
|
|
485
545
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
|
486
546
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
|
@@ -494,17 +554,31 @@ module ActiveRecord
|
|
|
494
554
|
SQL
|
|
495
555
|
|
|
496
556
|
fk_info.map do |row|
|
|
557
|
+
to_table = Utils.unquote_identifier(row["to_table"])
|
|
558
|
+
conkey = row["conkey"].scan(/\d+/).map(&:to_i)
|
|
559
|
+
confkey = row["confkey"].scan(/\d+/).map(&:to_i)
|
|
560
|
+
|
|
561
|
+
if conkey.size > 1
|
|
562
|
+
column = column_names_from_column_numbers(row["conrelid"], conkey)
|
|
563
|
+
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
|
|
564
|
+
else
|
|
565
|
+
column = Utils.unquote_identifier(row["column"])
|
|
566
|
+
primary_key = row["primary_key"]
|
|
567
|
+
end
|
|
568
|
+
|
|
497
569
|
options = {
|
|
498
|
-
column:
|
|
570
|
+
column: column,
|
|
499
571
|
name: row["name"],
|
|
500
|
-
primary_key:
|
|
572
|
+
primary_key: primary_key
|
|
501
573
|
}
|
|
502
574
|
|
|
503
575
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
|
504
576
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
|
577
|
+
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
|
|
578
|
+
|
|
505
579
|
options[:validate] = row["valid"]
|
|
506
580
|
|
|
507
|
-
ForeignKeyDefinition.new(table_name,
|
|
581
|
+
ForeignKeyDefinition.new(table_name, to_table, options)
|
|
508
582
|
end
|
|
509
583
|
end
|
|
510
584
|
|
|
@@ -516,8 +590,201 @@ module ActiveRecord
|
|
|
516
590
|
query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
|
|
517
591
|
end
|
|
518
592
|
|
|
593
|
+
def check_constraints(table_name) # :nodoc:
|
|
594
|
+
scope = quoted_scope(table_name)
|
|
595
|
+
|
|
596
|
+
check_info = internal_exec_query(<<-SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
597
|
+
SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
|
|
598
|
+
FROM pg_constraint c
|
|
599
|
+
JOIN pg_class t ON c.conrelid = t.oid
|
|
600
|
+
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
601
|
+
WHERE c.contype = 'c'
|
|
602
|
+
AND t.relname = #{scope[:name]}
|
|
603
|
+
AND n.nspname = #{scope[:schema]}
|
|
604
|
+
SQL
|
|
605
|
+
|
|
606
|
+
check_info.map do |row|
|
|
607
|
+
options = {
|
|
608
|
+
name: row["conname"],
|
|
609
|
+
validate: row["valid"]
|
|
610
|
+
}
|
|
611
|
+
expression = row["constraintdef"][/CHECK \((.+)\)/m, 1]
|
|
612
|
+
|
|
613
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# Returns an array of exclusion constraints for the given table.
|
|
618
|
+
# The exclusion constraints are represented as ExclusionConstraintDefinition objects.
|
|
619
|
+
def exclusion_constraints(table_name)
|
|
620
|
+
scope = quoted_scope(table_name)
|
|
621
|
+
|
|
622
|
+
exclusion_info = internal_exec_query(<<-SQL, "SCHEMA")
|
|
623
|
+
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.condeferrable, c.condeferred
|
|
624
|
+
FROM pg_constraint c
|
|
625
|
+
JOIN pg_class t ON c.conrelid = t.oid
|
|
626
|
+
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
627
|
+
WHERE c.contype = 'x'
|
|
628
|
+
AND t.relname = #{scope[:name]}
|
|
629
|
+
AND n.nspname = #{scope[:schema]}
|
|
630
|
+
SQL
|
|
631
|
+
|
|
632
|
+
exclusion_info.map do |row|
|
|
633
|
+
method_and_elements, predicate = row["constraintdef"].split(" WHERE ")
|
|
634
|
+
method_and_elements_parts = method_and_elements.match(/EXCLUDE(?: USING (?<using>\S+))? \((?<expression>.+)\)/)
|
|
635
|
+
predicate.remove!(/ DEFERRABLE(?: INITIALLY (?:IMMEDIATE|DEFERRED))?/) if predicate
|
|
636
|
+
predicate = predicate.from(2).to(-3) if predicate # strip 2 opening and closing parentheses
|
|
637
|
+
|
|
638
|
+
deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
|
|
639
|
+
|
|
640
|
+
options = {
|
|
641
|
+
name: row["conname"],
|
|
642
|
+
using: method_and_elements_parts["using"].to_sym,
|
|
643
|
+
where: predicate,
|
|
644
|
+
deferrable: deferrable
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
ExclusionConstraintDefinition.new(table_name, method_and_elements_parts["expression"], options)
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# Returns an array of unique constraints for the given table.
|
|
652
|
+
# The unique constraints are represented as UniqueConstraintDefinition objects.
|
|
653
|
+
def unique_constraints(table_name)
|
|
654
|
+
scope = quoted_scope(table_name)
|
|
655
|
+
|
|
656
|
+
unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
657
|
+
SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred
|
|
658
|
+
FROM pg_constraint c
|
|
659
|
+
JOIN pg_class t ON c.conrelid = t.oid
|
|
660
|
+
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
661
|
+
WHERE c.contype = 'u'
|
|
662
|
+
AND t.relname = #{scope[:name]}
|
|
663
|
+
AND n.nspname = #{scope[:schema]}
|
|
664
|
+
SQL
|
|
665
|
+
|
|
666
|
+
unique_info.map do |row|
|
|
667
|
+
conkey = row["conkey"].delete("{}").split(",").map(&:to_i)
|
|
668
|
+
columns = column_names_from_column_numbers(row["conrelid"], conkey)
|
|
669
|
+
|
|
670
|
+
deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
|
|
671
|
+
|
|
672
|
+
options = {
|
|
673
|
+
name: row["conname"],
|
|
674
|
+
deferrable: deferrable
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
UniqueConstraintDefinition.new(table_name, columns, options)
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Adds a new exclusion constraint to the table. +expression+ is a String
|
|
682
|
+
# representation of a list of exclusion elements and operators.
|
|
683
|
+
#
|
|
684
|
+
# add_exclusion_constraint :products, "price WITH =, availability_range WITH &&", using: :gist, name: "price_check"
|
|
685
|
+
#
|
|
686
|
+
# generates:
|
|
687
|
+
#
|
|
688
|
+
# ALTER TABLE "products" ADD CONSTRAINT price_check EXCLUDE USING gist (price WITH =, availability_range WITH &&)
|
|
689
|
+
#
|
|
690
|
+
# The +options+ hash can include the following keys:
|
|
691
|
+
# [<tt>:name</tt>]
|
|
692
|
+
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
|
693
|
+
# [<tt>:deferrable</tt>]
|
|
694
|
+
# Specify whether or not the exclusion constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
|
695
|
+
# [<tt>:using</tt>]
|
|
696
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
|
697
|
+
# [<tt>:where</tt>]
|
|
698
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
|
699
|
+
def add_exclusion_constraint(table_name, expression, **options)
|
|
700
|
+
options = exclusion_constraint_options(table_name, expression, options)
|
|
701
|
+
at = create_alter_table(table_name)
|
|
702
|
+
at.add_exclusion_constraint(expression, options)
|
|
703
|
+
|
|
704
|
+
execute schema_creation.accept(at)
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def exclusion_constraint_options(table_name, expression, options) # :nodoc:
|
|
708
|
+
assert_valid_deferrable(options[:deferrable])
|
|
709
|
+
|
|
710
|
+
options = options.dup
|
|
711
|
+
options[:name] ||= exclusion_constraint_name(table_name, expression: expression, **options)
|
|
712
|
+
options
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# Removes the given exclusion constraint from the table.
|
|
716
|
+
#
|
|
717
|
+
# remove_exclusion_constraint :products, name: "price_check"
|
|
718
|
+
#
|
|
719
|
+
# The +expression+ parameter will be ignored if present. It can be helpful
|
|
720
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
|
721
|
+
# In that case, +expression+ will be used by #add_exclusion_constraint.
|
|
722
|
+
def remove_exclusion_constraint(table_name, expression = nil, **options)
|
|
723
|
+
excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
|
|
724
|
+
|
|
725
|
+
at = create_alter_table(table_name)
|
|
726
|
+
at.drop_exclusion_constraint(excl_name_to_delete)
|
|
727
|
+
|
|
728
|
+
execute schema_creation.accept(at)
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
# Adds a new unique constraint to the table.
|
|
732
|
+
#
|
|
733
|
+
# add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position"
|
|
734
|
+
#
|
|
735
|
+
# generates:
|
|
736
|
+
#
|
|
737
|
+
# ALTER TABLE "sections" ADD CONSTRAINT unique_position UNIQUE (position) DEFERRABLE INITIALLY DEFERRED
|
|
738
|
+
#
|
|
739
|
+
# If you want to change an existing unique index to deferrable, you can use :using_index to create deferrable unique constraints.
|
|
740
|
+
#
|
|
741
|
+
# add_unique_constraint :sections, deferrable: :deferred, name: "unique_position", using_index: "index_sections_on_position"
|
|
742
|
+
#
|
|
743
|
+
# The +options+ hash can include the following keys:
|
|
744
|
+
# [<tt>:name</tt>]
|
|
745
|
+
# The constraint name. Defaults to <tt>uniq_rails_<identifier></tt>.
|
|
746
|
+
# [<tt>:deferrable</tt>]
|
|
747
|
+
# Specify whether or not the unique constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
|
748
|
+
# [<tt>:using_index</tt>]
|
|
749
|
+
# To specify an existing unique index name. Defaults to +nil+.
|
|
750
|
+
def add_unique_constraint(table_name, column_name = nil, **options)
|
|
751
|
+
options = unique_constraint_options(table_name, column_name, options)
|
|
752
|
+
at = create_alter_table(table_name)
|
|
753
|
+
at.add_unique_constraint(column_name, options)
|
|
754
|
+
|
|
755
|
+
execute schema_creation.accept(at)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
def unique_constraint_options(table_name, column_name, options) # :nodoc:
|
|
759
|
+
assert_valid_deferrable(options[:deferrable])
|
|
760
|
+
|
|
761
|
+
if column_name && options[:using_index]
|
|
762
|
+
raise ArgumentError, "Cannot specify both column_name and :using_index options."
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
options = options.dup
|
|
766
|
+
options[:name] ||= unique_constraint_name(table_name, column: column_name, **options)
|
|
767
|
+
options
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# Removes the given unique constraint from the table.
|
|
771
|
+
#
|
|
772
|
+
# remove_unique_constraint :sections, name: "unique_position"
|
|
773
|
+
#
|
|
774
|
+
# The +column_name+ parameter will be ignored if present. It can be helpful
|
|
775
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
|
776
|
+
# In that case, +column_name+ will be used by #add_unique_constraint.
|
|
777
|
+
def remove_unique_constraint(table_name, column_name = nil, **options)
|
|
778
|
+
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
|
779
|
+
|
|
780
|
+
at = create_alter_table(table_name)
|
|
781
|
+
at.drop_unique_constraint(unique_name_to_delete)
|
|
782
|
+
|
|
783
|
+
execute schema_creation.accept(at)
|
|
784
|
+
end
|
|
785
|
+
|
|
519
786
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
|
520
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
|
|
787
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, enum_type: nil, **) # :nodoc:
|
|
521
788
|
sql = \
|
|
522
789
|
case type.to_s
|
|
523
790
|
when "binary"
|
|
@@ -541,6 +808,10 @@ module ActiveRecord
|
|
|
541
808
|
when 5..8; "bigint"
|
|
542
809
|
else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
|
|
543
810
|
end
|
|
811
|
+
when "enum"
|
|
812
|
+
raise ArgumentError, "enum_type is required for enums" if enum_type.nil?
|
|
813
|
+
|
|
814
|
+
enum_type
|
|
544
815
|
else
|
|
545
816
|
super
|
|
546
817
|
end
|
|
@@ -551,14 +822,14 @@ module ActiveRecord
|
|
|
551
822
|
|
|
552
823
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
|
553
824
|
# requires that the ORDER BY include the distinct column.
|
|
554
|
-
def columns_for_distinct(columns, orders)
|
|
555
|
-
order_columns = orders.
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
825
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
|
826
|
+
order_columns = orders.compact_blank.map { |s|
|
|
827
|
+
# Convert Arel node to string
|
|
828
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
|
829
|
+
# Remove any ASC/DESC modifiers
|
|
830
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
|
831
|
+
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
|
|
832
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
|
562
833
|
|
|
563
834
|
(order_columns << super).join(", ")
|
|
564
835
|
end
|
|
@@ -577,8 +848,6 @@ module ActiveRecord
|
|
|
577
848
|
#
|
|
578
849
|
# validate_constraint :accounts, :constraint_name
|
|
579
850
|
def validate_constraint(table_name, constraint_name)
|
|
580
|
-
return unless supports_validate_constraints?
|
|
581
|
-
|
|
582
851
|
at = create_alter_table table_name
|
|
583
852
|
at.validate_constraint constraint_name
|
|
584
853
|
|
|
@@ -601,31 +870,66 @@ module ActiveRecord
|
|
|
601
870
|
#
|
|
602
871
|
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
|
603
872
|
def validate_foreign_key(from_table, to_table = nil, **options)
|
|
604
|
-
return unless supports_validate_constraints?
|
|
605
|
-
|
|
606
873
|
fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
|
|
607
874
|
|
|
608
875
|
validate_constraint from_table, fk_name_to_validate
|
|
609
876
|
end
|
|
610
877
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
878
|
+
# Validates the given check constraint.
|
|
879
|
+
#
|
|
880
|
+
# validate_check_constraint :products, name: "price_check"
|
|
881
|
+
#
|
|
882
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
883
|
+
def validate_check_constraint(table_name, **options)
|
|
884
|
+
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
|
885
|
+
|
|
886
|
+
validate_constraint table_name, chk_name_to_validate
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def foreign_key_column_for(table_name, column_name) # :nodoc:
|
|
890
|
+
_schema, table_name = extract_schema_qualified_name(table_name)
|
|
891
|
+
super
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def add_index_options(table_name, column_name, **options) # :nodoc:
|
|
895
|
+
if (where = options[:where]) && table_exists?(table_name) && column_exists?(table_name, where)
|
|
896
|
+
options[:where] = quote_column_name(where)
|
|
897
|
+
end
|
|
898
|
+
super
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
def quoted_include_columns_for_index(column_names) # :nodoc:
|
|
902
|
+
return quote_column_name(column_names) if column_names.is_a?(Symbol)
|
|
903
|
+
|
|
904
|
+
quoted_columns = column_names.each_with_object({}) do |name, result|
|
|
905
|
+
result[name.to_sym] = quote_column_name(name).dup
|
|
614
906
|
end
|
|
907
|
+
add_options_for_index_columns(quoted_columns).values.join(", ")
|
|
908
|
+
end
|
|
615
909
|
|
|
616
|
-
|
|
617
|
-
|
|
910
|
+
def schema_creation # :nodoc:
|
|
911
|
+
PostgreSQL::SchemaCreation.new(self)
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
private
|
|
915
|
+
def create_table_definition(name, **options)
|
|
916
|
+
PostgreSQL::TableDefinition.new(self, name, **options)
|
|
618
917
|
end
|
|
619
918
|
|
|
620
919
|
def create_alter_table(name)
|
|
621
920
|
PostgreSQL::AlterTable.new create_table_definition(name)
|
|
622
921
|
end
|
|
623
922
|
|
|
624
|
-
def new_column_from_field(table_name, field)
|
|
625
|
-
column_name, type, default, notnull, oid, fmod, collation, comment = field
|
|
923
|
+
def new_column_from_field(table_name, field, _definitions)
|
|
924
|
+
column_name, type, default, notnull, oid, fmod, collation, comment, identity, attgenerated = field
|
|
626
925
|
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
|
627
926
|
default_value = extract_value_from_default(default)
|
|
628
|
-
|
|
927
|
+
|
|
928
|
+
if attgenerated.present?
|
|
929
|
+
default_function = default
|
|
930
|
+
else
|
|
931
|
+
default_function = extract_default_function(default_value, default)
|
|
932
|
+
end
|
|
629
933
|
|
|
630
934
|
if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
|
|
631
935
|
serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
|
|
@@ -639,7 +943,9 @@ module ActiveRecord
|
|
|
639
943
|
default_function,
|
|
640
944
|
collation: collation,
|
|
641
945
|
comment: comment.presence,
|
|
642
|
-
serial: serial
|
|
946
|
+
serial: serial,
|
|
947
|
+
identity: identity.presence,
|
|
948
|
+
generated: attgenerated
|
|
643
949
|
)
|
|
644
950
|
end
|
|
645
951
|
|
|
@@ -679,62 +985,91 @@ module ActiveRecord
|
|
|
679
985
|
end
|
|
680
986
|
end
|
|
681
987
|
|
|
682
|
-
def
|
|
988
|
+
def assert_valid_deferrable(deferrable)
|
|
989
|
+
return if !deferrable || %i(immediate deferred).include?(deferrable)
|
|
990
|
+
|
|
991
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
def extract_constraint_deferrable(deferrable, deferred)
|
|
995
|
+
deferrable && (deferred ? :deferred : :immediate)
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
def reference_name_for_table(table_name)
|
|
999
|
+
_schema, table_name = extract_schema_qualified_name(table_name.to_s)
|
|
1000
|
+
table_name.singularize
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
|
683
1004
|
return super unless options.key?(:comment)
|
|
684
1005
|
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
|
685
1006
|
end
|
|
686
1007
|
|
|
687
|
-
def change_column_for_alter(table_name, column_name, type, options
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
|
|
1008
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
|
1009
|
+
change_col_def = build_change_column_definition(table_name, column_name, type, **options)
|
|
1010
|
+
sqls = [schema_creation.accept(change_col_def)]
|
|
691
1011
|
sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
|
|
692
1012
|
sqls
|
|
693
1013
|
end
|
|
694
1014
|
|
|
695
|
-
def
|
|
696
|
-
column = column_for(table_name, column_name)
|
|
697
|
-
return unless column
|
|
698
|
-
|
|
699
|
-
default = extract_new_default_value(default_or_changes)
|
|
700
|
-
alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
|
|
1015
|
+
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
|
701
1016
|
if default.nil?
|
|
702
|
-
|
|
703
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
|
704
|
-
alter_column_query % "DROP DEFAULT"
|
|
1017
|
+
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
|
705
1018
|
else
|
|
706
|
-
|
|
1019
|
+
Proc.new { change_column_null(table_name, column_name, null, default) }
|
|
707
1020
|
end
|
|
708
1021
|
end
|
|
709
1022
|
|
|
710
|
-
def
|
|
711
|
-
|
|
1023
|
+
def add_index_opclass(quoted_columns, **options)
|
|
1024
|
+
opclasses = options_for_index_columns(options[:opclass])
|
|
1025
|
+
quoted_columns.each do |name, column|
|
|
1026
|
+
column << " #{opclasses[name]}" if opclasses[name].present?
|
|
1027
|
+
end
|
|
712
1028
|
end
|
|
713
1029
|
|
|
714
|
-
def
|
|
715
|
-
|
|
1030
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
|
1031
|
+
quoted_columns = add_index_opclass(quoted_columns, **options)
|
|
1032
|
+
super
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
def exclusion_constraint_name(table_name, **options)
|
|
1036
|
+
options.fetch(:name) do
|
|
1037
|
+
expression = options.fetch(:expression)
|
|
1038
|
+
identifier = "#{table_name}_#{expression}_excl"
|
|
1039
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
|
716
1040
|
|
|
717
|
-
|
|
718
|
-
options[:precision] = 6
|
|
1041
|
+
"excl_rails_#{hashed_identifier}"
|
|
719
1042
|
end
|
|
1043
|
+
end
|
|
720
1044
|
|
|
721
|
-
|
|
1045
|
+
def exclusion_constraint_for(table_name, **options)
|
|
1046
|
+
excl_name = exclusion_constraint_name(table_name, **options)
|
|
1047
|
+
exclusion_constraints(table_name).detect { |excl| excl.name == excl_name }
|
|
722
1048
|
end
|
|
723
1049
|
|
|
724
|
-
def
|
|
725
|
-
|
|
1050
|
+
def exclusion_constraint_for!(table_name, expression: nil, **options)
|
|
1051
|
+
exclusion_constraint_for(table_name, expression: expression, **options) ||
|
|
1052
|
+
raise(ArgumentError, "Table '#{table_name}' has no exclusion constraint for #{expression || options}")
|
|
726
1053
|
end
|
|
727
1054
|
|
|
728
|
-
def
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
1055
|
+
def unique_constraint_name(table_name, **options)
|
|
1056
|
+
options.fetch(:name) do
|
|
1057
|
+
column_or_index = Array(options[:column] || options[:using_index]).map(&:to_s)
|
|
1058
|
+
identifier = "#{table_name}_#{column_or_index * '_and_'}_unique"
|
|
1059
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
|
1060
|
+
|
|
1061
|
+
"uniq_rails_#{hashed_identifier}"
|
|
732
1062
|
end
|
|
733
1063
|
end
|
|
734
1064
|
|
|
735
|
-
def
|
|
736
|
-
|
|
737
|
-
|
|
1065
|
+
def unique_constraint_for(table_name, **options)
|
|
1066
|
+
name = unique_constraint_name(table_name, **options) unless options.key?(:column)
|
|
1067
|
+
unique_constraints(table_name).detect { |unique_constraint| unique_constraint.defined_for?(name: name, **options) }
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
def unique_constraint_for!(table_name, column: nil, **options)
|
|
1071
|
+
unique_constraint_for(table_name, column: column, **options) ||
|
|
1072
|
+
raise(ArgumentError, "Table '#{table_name}' has no unique constraint for #{column || options}")
|
|
738
1073
|
end
|
|
739
1074
|
|
|
740
1075
|
def data_source_sql(name = nil, type: nil)
|
|
@@ -770,6 +1105,15 @@ module ActiveRecord
|
|
|
770
1105
|
name = Utils.extract_schema_qualified_name(string.to_s)
|
|
771
1106
|
[name.schema, name.identifier]
|
|
772
1107
|
end
|
|
1108
|
+
|
|
1109
|
+
def column_names_from_column_numbers(table_oid, column_numbers)
|
|
1110
|
+
Hash[query(<<~SQL, "SCHEMA")].values_at(*column_numbers).compact
|
|
1111
|
+
SELECT a.attnum, a.attname
|
|
1112
|
+
FROM pg_attribute a
|
|
1113
|
+
WHERE a.attrelid = #{table_oid}
|
|
1114
|
+
AND a.attnum IN (#{column_numbers.join(", ")})
|
|
1115
|
+
SQL
|
|
1116
|
+
end
|
|
773
1117
|
end
|
|
774
1118
|
end
|
|
775
1119
|
end
|