activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- 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 +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- 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 +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- 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 +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- 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 +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- 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 +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 +54 -16
- 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/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 +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- 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 +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +155 -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 +160 -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 +42 -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_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- 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 +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- 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 +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -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 +9 -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 +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -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 +48 -0
- data/lib/arel/delete_manager.rb +32 -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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -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 +44 -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/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -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 +46 -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 +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -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 +55 -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 +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
- 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 +162 -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
data/lib/active_record/enum.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/hash/slice"
|
3
4
|
require "active_support/core_ext/object/deep_dup"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -7,7 +8,7 @@ module ActiveRecord
|
|
7
8
|
# but can be queried by name. Example:
|
8
9
|
#
|
9
10
|
# class Conversation < ActiveRecord::Base
|
10
|
-
# enum status
|
11
|
+
# enum :status, [ :active, :archived ]
|
11
12
|
# end
|
12
13
|
#
|
13
14
|
# # conversation.update! status: 0
|
@@ -31,7 +32,9 @@ module ActiveRecord
|
|
31
32
|
# as well. With the above example:
|
32
33
|
#
|
33
34
|
# Conversation.active
|
35
|
+
# Conversation.not_active
|
34
36
|
# Conversation.archived
|
37
|
+
# Conversation.not_archived
|
35
38
|
#
|
36
39
|
# Of course, you can also query them directly if the scopes don't fit your
|
37
40
|
# needs:
|
@@ -39,19 +42,33 @@ module ActiveRecord
|
|
39
42
|
# Conversation.where(status: [:active, :archived])
|
40
43
|
# Conversation.where.not(status: :active)
|
41
44
|
#
|
42
|
-
#
|
45
|
+
# Defining scopes can be disabled by setting +:scopes+ to +false+.
|
43
46
|
#
|
44
|
-
#
|
45
|
-
#
|
47
|
+
# class Conversation < ActiveRecord::Base
|
48
|
+
# enum :status, [ :active, :archived ], scopes: false
|
46
49
|
# end
|
47
50
|
#
|
48
|
-
#
|
51
|
+
# You can set the default enum value by setting +:default+, like:
|
52
|
+
#
|
53
|
+
# class Conversation < ActiveRecord::Base
|
54
|
+
# enum :status, [ :active, :archived ], default: :active
|
55
|
+
# end
|
49
56
|
#
|
50
|
-
#
|
57
|
+
# conversation = Conversation.new
|
58
|
+
# conversation.status # => "active"
|
59
|
+
#
|
60
|
+
# It's possible to explicitly map the relation between attribute and
|
51
61
|
# database integer with a hash:
|
52
62
|
#
|
53
63
|
# class Conversation < ActiveRecord::Base
|
54
|
-
# enum status
|
64
|
+
# enum :status, active: 0, archived: 1
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Finally it's also possible to use a string column to persist the enumerated value.
|
68
|
+
# Note that this will likely lead to slower database queries:
|
69
|
+
#
|
70
|
+
# class Conversation < ActiveRecord::Base
|
71
|
+
# enum :status, active: "active", archived: "archived"
|
55
72
|
# end
|
56
73
|
#
|
57
74
|
# Note that when an array is used, the implicit mapping from the values to database
|
@@ -76,14 +93,14 @@ module ActiveRecord
|
|
76
93
|
#
|
77
94
|
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
78
95
|
#
|
79
|
-
# You can use the +:
|
96
|
+
# You can use the +:prefix+ or +:suffix+ options when you need to define
|
80
97
|
# multiple enums with same values. If the passed value is +true+, the methods
|
81
98
|
# are prefixed/suffixed with the name of the enum. It is also possible to
|
82
99
|
# supply a custom value:
|
83
100
|
#
|
84
101
|
# class Conversation < ActiveRecord::Base
|
85
|
-
# enum status
|
86
|
-
# enum comments_status
|
102
|
+
# enum :status, [ :active, :archived ], suffix: true
|
103
|
+
# enum :comments_status, [ :active, :inactive ], prefix: :comments
|
87
104
|
# end
|
88
105
|
#
|
89
106
|
# With the above example, the bang and predicate methods along with the
|
@@ -94,7 +111,6 @@ module ActiveRecord
|
|
94
111
|
#
|
95
112
|
# conversation.comments_inactive!
|
96
113
|
# conversation.comments_active? # => false
|
97
|
-
|
98
114
|
module Enum
|
99
115
|
def self.extended(base) # :nodoc:
|
100
116
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
@@ -115,24 +131,25 @@ module ActiveRecord
|
|
115
131
|
end
|
116
132
|
|
117
133
|
def cast(value)
|
118
|
-
return if value.blank?
|
119
|
-
|
120
134
|
if mapping.has_key?(value)
|
121
135
|
value.to_s
|
122
136
|
elsif mapping.has_value?(value)
|
123
137
|
mapping.key(value)
|
124
138
|
else
|
125
|
-
|
139
|
+
value.presence
|
126
140
|
end
|
127
141
|
end
|
128
142
|
|
129
143
|
def deserialize(value)
|
130
|
-
return if value.nil?
|
131
144
|
mapping.key(subtype.deserialize(value))
|
132
145
|
end
|
133
146
|
|
134
147
|
def serialize(value)
|
135
|
-
mapping.fetch(value, value)
|
148
|
+
subtype.serialize(mapping.fetch(value, value))
|
149
|
+
end
|
150
|
+
|
151
|
+
def serializable?(value, &block)
|
152
|
+
subtype.serializable?(mapping.fetch(value, value), &block)
|
136
153
|
end
|
137
154
|
|
138
155
|
def assert_valid_value(value)
|
@@ -141,83 +158,132 @@ module ActiveRecord
|
|
141
158
|
end
|
142
159
|
end
|
143
160
|
|
144
|
-
|
145
|
-
|
146
|
-
|
161
|
+
attr_reader :subtype
|
162
|
+
|
163
|
+
private
|
164
|
+
attr_reader :name, :mapping
|
165
|
+
end
|
166
|
+
|
167
|
+
def enum(name = nil, values = nil, **options)
|
168
|
+
if name
|
169
|
+
values, options = options, {} unless values
|
170
|
+
return _enum(name, values, **options)
|
171
|
+
end
|
172
|
+
|
173
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
|
174
|
+
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
147
175
|
|
148
|
-
|
176
|
+
definitions.each { |name, values| _enum(name, values, **options) }
|
149
177
|
end
|
150
178
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
enum_suffix = definitions.delete(:_suffix)
|
155
|
-
definitions.each do |name, values|
|
179
|
+
private
|
180
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, **options)
|
181
|
+
assert_valid_enum_definition_values(values)
|
156
182
|
# statuses = { }
|
157
183
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
158
184
|
name = name.to_s
|
159
185
|
|
160
186
|
# def self.statuses() statuses end
|
161
187
|
detect_enum_conflict!(name, name.pluralize, true)
|
162
|
-
singleton_class.
|
188
|
+
singleton_class.define_method(name.pluralize) { enum_values }
|
163
189
|
defined_enums[name] = enum_values
|
164
190
|
|
165
191
|
detect_enum_conflict!(name, name)
|
166
192
|
detect_enum_conflict!(name, "#{name}=")
|
167
193
|
|
168
|
-
|
169
|
-
|
170
|
-
EnumType.new(
|
194
|
+
attribute(name, **options) do |subtype|
|
195
|
+
subtype = subtype.subtype if EnumType === subtype
|
196
|
+
EnumType.new(name, enum_values, subtype)
|
171
197
|
end
|
172
198
|
|
199
|
+
value_method_names = []
|
173
200
|
_enum_methods_module.module_eval do
|
201
|
+
prefix = if prefix
|
202
|
+
prefix == true ? "#{name}_" : "#{prefix}_"
|
203
|
+
end
|
204
|
+
|
205
|
+
suffix = if suffix
|
206
|
+
suffix == true ? "_#{name}" : "_#{suffix}"
|
207
|
+
end
|
208
|
+
|
174
209
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
175
210
|
pairs.each do |label, value|
|
176
|
-
if enum_prefix == true
|
177
|
-
prefix = "#{name}_"
|
178
|
-
elsif enum_prefix
|
179
|
-
prefix = "#{enum_prefix}_"
|
180
|
-
end
|
181
|
-
if enum_suffix == true
|
182
|
-
suffix = "_#{name}"
|
183
|
-
elsif enum_suffix
|
184
|
-
suffix = "_#{enum_suffix}"
|
185
|
-
end
|
186
|
-
|
187
|
-
value_method_name = "#{prefix}#{label}#{suffix}"
|
188
211
|
enum_values[label] = value
|
189
212
|
label = label.to_s
|
190
213
|
|
191
|
-
|
214
|
+
value_method_name = "#{prefix}#{label}#{suffix}"
|
215
|
+
value_method_names << value_method_name
|
216
|
+
define_enum_methods(name, value_method_name, value, scopes)
|
217
|
+
|
218
|
+
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
219
|
+
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
220
|
+
|
221
|
+
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
222
|
+
value_method_names << value_method_alias
|
223
|
+
define_enum_methods(name, value_method_alias, value, scopes)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
228
|
+
enum_values.freeze
|
229
|
+
end
|
230
|
+
|
231
|
+
class EnumMethods < Module # :nodoc:
|
232
|
+
def initialize(klass)
|
233
|
+
@klass = klass
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
attr_reader :klass
|
238
|
+
|
239
|
+
def define_enum_methods(name, value_method_name, value, scopes)
|
240
|
+
# def active?() status_for_database == 0 end
|
192
241
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
193
|
-
define_method("#{value_method_name}?") {
|
242
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
194
243
|
|
195
244
|
# def active!() update!(status: 0) end
|
196
245
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
197
|
-
define_method("#{value_method_name}!") { update!(
|
246
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
198
247
|
|
199
248
|
# scope :active, -> { where(status: 0) }
|
200
|
-
|
201
|
-
|
249
|
+
# scope :not_active, -> { where.not(status: 0) }
|
250
|
+
if scopes
|
251
|
+
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
252
|
+
klass.scope value_method_name, -> { where(name => value) }
|
253
|
+
|
254
|
+
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
255
|
+
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
256
|
+
end
|
202
257
|
end
|
203
|
-
end
|
204
|
-
enum_values.freeze
|
205
258
|
end
|
206
|
-
|
259
|
+
private_constant :EnumMethods
|
207
260
|
|
208
|
-
private
|
209
261
|
def _enum_methods_module
|
210
262
|
@_enum_methods_module ||= begin
|
211
|
-
mod =
|
263
|
+
mod = EnumMethods.new(self)
|
212
264
|
include mod
|
213
265
|
mod
|
214
266
|
end
|
215
267
|
end
|
216
268
|
|
269
|
+
def assert_valid_enum_definition_values(values)
|
270
|
+
unless values.is_a?(Hash) || values.all?(Symbol) || values.all?(String)
|
271
|
+
error_message = <<~MSG
|
272
|
+
Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
|
273
|
+
MSG
|
274
|
+
raise ArgumentError, error_message
|
275
|
+
end
|
276
|
+
|
277
|
+
if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
|
278
|
+
raise ArgumentError, "Enum label name must not be blank."
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
217
282
|
ENUM_CONFLICT_MESSAGE = \
|
218
283
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
219
284
|
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
220
285
|
"by %{source}."
|
286
|
+
private_constant :ENUM_CONFLICT_MESSAGE
|
221
287
|
|
222
288
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
223
289
|
if klass_method && dangerous_class_method?(method_name)
|
@@ -240,5 +306,18 @@ module ActiveRecord
|
|
240
306
|
source: source
|
241
307
|
}
|
242
308
|
end
|
309
|
+
|
310
|
+
def detect_negative_enum_conditions!(method_names)
|
311
|
+
return unless logger
|
312
|
+
|
313
|
+
method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
|
314
|
+
inverted_form = potential_not.sub("not_", "")
|
315
|
+
if method_names.include?(inverted_form)
|
316
|
+
logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
|
317
|
+
" This has caused a conflict with auto generated negative scopes." \
|
318
|
+
" Avoid using enum elements starting with 'not' where the positive form is also an element."
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
243
322
|
end
|
244
323
|
end
|
data/lib/active_record/errors.rb
CHANGED
@@ -7,6 +7,10 @@ module ActiveRecord
|
|
7
7
|
class ActiveRecordError < StandardError
|
8
8
|
end
|
9
9
|
|
10
|
+
# Raised when trying to use a feature in Active Record which requires Active Job but the gem is not present.
|
11
|
+
class ActiveJobRequiredError < ActiveRecordError
|
12
|
+
end
|
13
|
+
|
10
14
|
# Raised when the single-table inheritance mechanism fails to locate the subclass
|
11
15
|
# (for example due to improper usage of column that
|
12
16
|
# {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
|
@@ -38,6 +42,10 @@ module ActiveRecord
|
|
38
42
|
class AdapterNotSpecified < ActiveRecordError
|
39
43
|
end
|
40
44
|
|
45
|
+
# Raised when a model makes a query but it has not specified an associated table.
|
46
|
+
class TableNotSpecified < ActiveRecordError
|
47
|
+
end
|
48
|
+
|
41
49
|
# Raised when Active Record cannot find database adapter specified in
|
42
50
|
# +config/database.yml+ or programmatically.
|
43
51
|
class AdapterNotFound < ActiveRecordError
|
@@ -49,6 +57,47 @@ module ActiveRecord
|
|
49
57
|
class ConnectionNotEstablished < ActiveRecordError
|
50
58
|
end
|
51
59
|
|
60
|
+
# Raised when a connection could not be obtained within the connection
|
61
|
+
# acquisition timeout period: because max connections in pool
|
62
|
+
# are in use.
|
63
|
+
class ConnectionTimeoutError < ConnectionNotEstablished
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when connection to the database could not been established because it was not
|
67
|
+
# able to connect to the host or when the authorization failed.
|
68
|
+
class DatabaseConnectionError < ConnectionNotEstablished
|
69
|
+
def initialize(message = nil)
|
70
|
+
super(message || "Database connection error")
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
def hostname_error(hostname)
|
75
|
+
DatabaseConnectionError.new(<<~MSG)
|
76
|
+
There is an issue connecting with your hostname: #{hostname}.\n
|
77
|
+
Please check your database configuration and ensure there is a valid connection to your database.
|
78
|
+
MSG
|
79
|
+
end
|
80
|
+
|
81
|
+
def username_error(username)
|
82
|
+
DatabaseConnectionError.new(<<~MSG)
|
83
|
+
There is an issue connecting to your database with your username/password, username: #{username}.\n
|
84
|
+
Please check your database configuration to ensure the username/password are valid.
|
85
|
+
MSG
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Raised when a pool was unable to get ahold of all its connections
|
91
|
+
# to perform a "group" action such as
|
92
|
+
# {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
|
93
|
+
# or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
|
94
|
+
class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised when a write to the database is attempted on a read only connection.
|
98
|
+
class ReadOnlyError < ActiveRecordError
|
99
|
+
end
|
100
|
+
|
52
101
|
# Raised when Active Record cannot find a record by given id or set of ids.
|
53
102
|
class RecordNotFound < ActiveRecordError
|
54
103
|
attr_reader :model, :primary_key, :id
|
@@ -64,7 +113,7 @@ module ActiveRecord
|
|
64
113
|
|
65
114
|
# Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
66
115
|
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
|
67
|
-
# methods when a record is invalid and
|
116
|
+
# methods when a record is invalid and cannot be saved.
|
68
117
|
class RecordNotSaved < ActiveRecordError
|
69
118
|
attr_reader :record
|
70
119
|
|
@@ -75,7 +124,7 @@ module ActiveRecord
|
|
75
124
|
end
|
76
125
|
|
77
126
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
78
|
-
# when a call to {#destroy}[rdoc-ref:Persistence#destroy
|
127
|
+
# when a call to {#destroy}[rdoc-ref:Persistence#destroy]
|
79
128
|
# would return false.
|
80
129
|
#
|
81
130
|
# begin
|
@@ -93,13 +142,27 @@ module ActiveRecord
|
|
93
142
|
end
|
94
143
|
end
|
95
144
|
|
145
|
+
# Raised when Active Record finds multiple records but only expected one.
|
146
|
+
class SoleRecordExceeded < ActiveRecordError
|
147
|
+
attr_reader :record
|
148
|
+
|
149
|
+
def initialize(record = nil)
|
150
|
+
@record = record
|
151
|
+
super "Wanted only one #{record&.name || "record"}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
96
155
|
# Superclass for all database execution errors.
|
97
156
|
#
|
98
157
|
# Wraps the underlying database error as +cause+.
|
99
158
|
class StatementInvalid < ActiveRecordError
|
100
|
-
def initialize(message = nil)
|
101
|
-
super(message ||
|
159
|
+
def initialize(message = nil, sql: nil, binds: nil)
|
160
|
+
super(message || $!&.message)
|
161
|
+
@sql = sql
|
162
|
+
@binds = binds
|
102
163
|
end
|
164
|
+
|
165
|
+
attr_reader :sql, :binds
|
103
166
|
end
|
104
167
|
|
105
168
|
# Defunct wrapper class kept for compatibility.
|
@@ -111,14 +174,14 @@ module ActiveRecord
|
|
111
174
|
class RecordNotUnique < WrappedDatabaseException
|
112
175
|
end
|
113
176
|
|
114
|
-
# Raised when a record cannot be inserted or updated because it references a non-existent record
|
177
|
+
# Raised when a record cannot be inserted or updated because it references a non-existent record,
|
178
|
+
# or when a record cannot be deleted because a parent record references it.
|
115
179
|
class InvalidForeignKey < WrappedDatabaseException
|
116
180
|
end
|
117
181
|
|
118
182
|
# Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
|
119
183
|
class MismatchedForeignKey < StatementInvalid
|
120
184
|
def initialize(
|
121
|
-
adapter = nil,
|
122
185
|
message: nil,
|
123
186
|
sql: nil,
|
124
187
|
binds: nil,
|
@@ -130,14 +193,14 @@ module ActiveRecord
|
|
130
193
|
)
|
131
194
|
if table
|
132
195
|
type = primary_key_column.bigint? ? :bigint : primary_key_column.type
|
133
|
-
msg =
|
196
|
+
msg = <<~EOM.squish
|
134
197
|
Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
|
135
198
|
which has type `#{primary_key_column.sql_type}`.
|
136
199
|
To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
|
137
200
|
(For example `t.#{type} :#{foreign_key}`).
|
138
201
|
EOM
|
139
202
|
else
|
140
|
-
msg =
|
203
|
+
msg = <<~EOM.squish
|
141
204
|
There is a mismatch between the foreign key and primary key column types.
|
142
205
|
Verify that the foreign key column type and the primary key of the associated table match types.
|
143
206
|
EOM
|
@@ -145,7 +208,7 @@ module ActiveRecord
|
|
145
208
|
if message
|
146
209
|
msg << "\nOriginal message: #{message}"
|
147
210
|
end
|
148
|
-
super(msg)
|
211
|
+
super(msg, sql: sql, binds: binds)
|
149
212
|
end
|
150
213
|
end
|
151
214
|
|
@@ -161,9 +224,9 @@ module ActiveRecord
|
|
161
224
|
class RangeError < StatementInvalid
|
162
225
|
end
|
163
226
|
|
164
|
-
# Raised when number of
|
165
|
-
#
|
166
|
-
# does not match number of
|
227
|
+
# Raised when the number of placeholders in an SQL fragment passed to
|
228
|
+
# {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]
|
229
|
+
# does not match the number of values supplied.
|
167
230
|
#
|
168
231
|
# For example, when there are two placeholders with only one value supplied:
|
169
232
|
#
|
@@ -173,6 +236,34 @@ module ActiveRecord
|
|
173
236
|
|
174
237
|
# Raised when a given database does not exist.
|
175
238
|
class NoDatabaseError < StatementInvalid
|
239
|
+
include ActiveSupport::ActionableError
|
240
|
+
|
241
|
+
action "Create database" do
|
242
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
243
|
+
end
|
244
|
+
|
245
|
+
def initialize(message = nil)
|
246
|
+
super(message || "Database not found")
|
247
|
+
end
|
248
|
+
|
249
|
+
class << self
|
250
|
+
def db_error(db_name)
|
251
|
+
NoDatabaseError.new(<<~MSG)
|
252
|
+
We could not find your database: #{db_name}. Which can be found in the database configuration file located at config/database.yml.
|
253
|
+
|
254
|
+
To resolve this issue:
|
255
|
+
|
256
|
+
- Did you create the database for this app, or delete it? You may need to create your database.
|
257
|
+
- Has the database name changed? Check your database.yml config has the correct database name.
|
258
|
+
|
259
|
+
To create your database, run:\n\n bin/rails db:create
|
260
|
+
MSG
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Raised when creating a database if it exists.
|
266
|
+
class DatabaseAlreadyExists < StatementInvalid
|
176
267
|
end
|
177
268
|
|
178
269
|
# Raised when PostgreSQL returns 'cached plan must not change result type' and
|
@@ -212,6 +303,10 @@ module ActiveRecord
|
|
212
303
|
class ReadOnlyRecord < ActiveRecordError
|
213
304
|
end
|
214
305
|
|
306
|
+
# Raised on attempt to lazily load records that are marked as strict loading.
|
307
|
+
class StrictLoadingViolationError < ActiveRecordError
|
308
|
+
end
|
309
|
+
|
215
310
|
# {ActiveRecord::Base.transaction}[rdoc-ref:Transactions::ClassMethods#transaction]
|
216
311
|
# uses this exception to distinguish a deliberate rollback from other exceptional situations.
|
217
312
|
# Normally, raising an exception will cause the
|
@@ -231,7 +326,7 @@ module ActiveRecord
|
|
231
326
|
# # The system must fail on Friday so that our support department
|
232
327
|
# # won't be out of job. We silently rollback this transaction
|
233
328
|
# # without telling the user.
|
234
|
-
# raise ActiveRecord::Rollback
|
329
|
+
# raise ActiveRecord::Rollback
|
235
330
|
# end
|
236
331
|
# end
|
237
332
|
# # ActiveRecord::Rollback is the only exception that won't be passed on
|
@@ -322,10 +417,15 @@ module ActiveRecord
|
|
322
417
|
# See the following:
|
323
418
|
#
|
324
419
|
# * https://www.postgresql.org/docs/current/static/transaction-iso.html
|
325
|
-
# * https://dev.mysql.com/doc/
|
420
|
+
# * https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html#error_er_lock_deadlock
|
326
421
|
class TransactionRollbackError < StatementInvalid
|
327
422
|
end
|
328
423
|
|
424
|
+
# AsynchronousQueryInsideTransactionError will be raised when attempting
|
425
|
+
# to perform an asynchronous query from inside a transaction
|
426
|
+
class AsynchronousQueryInsideTransactionError < ActiveRecordError
|
427
|
+
end
|
428
|
+
|
329
429
|
# SerializationFailure will be raised when a transaction is rolled
|
330
430
|
# back by the database due to a serialization failure.
|
331
431
|
class SerializationFailure < TransactionRollbackError
|
@@ -341,37 +441,43 @@ module ActiveRecord
|
|
341
441
|
class IrreversibleOrderError < ActiveRecordError
|
342
442
|
end
|
343
443
|
|
444
|
+
# Superclass for errors that have been aborted (either by client or server).
|
445
|
+
class QueryAborted < StatementInvalid
|
446
|
+
end
|
447
|
+
|
344
448
|
# LockWaitTimeout will be raised when lock wait timeout exceeded.
|
345
449
|
class LockWaitTimeout < StatementInvalid
|
346
450
|
end
|
347
451
|
|
348
452
|
# StatementTimeout will be raised when statement timeout exceeded.
|
349
|
-
class StatementTimeout <
|
453
|
+
class StatementTimeout < QueryAborted
|
350
454
|
end
|
351
455
|
|
352
456
|
# QueryCanceled will be raised when canceling statement due to user request.
|
353
|
-
class QueryCanceled <
|
457
|
+
class QueryCanceled < QueryAborted
|
458
|
+
end
|
459
|
+
|
460
|
+
# AdapterTimeout will be raised when database clients times out while waiting from the server.
|
461
|
+
class AdapterTimeout < QueryAborted
|
354
462
|
end
|
355
463
|
|
356
464
|
# UnknownAttributeReference is raised when an unknown and potentially unsafe
|
357
|
-
# value is passed to a query method
|
358
|
-
#
|
359
|
-
# #order method might cause this exception.
|
465
|
+
# value is passed to a query method. For example, passing a non column name
|
466
|
+
# value to a relation's #order method might cause this exception.
|
360
467
|
#
|
361
468
|
# When working around this exception, caution should be taken to avoid SQL
|
362
469
|
# injection vulnerabilities when passing user-provided values to query
|
363
470
|
# methods. Known-safe values can be passed to query methods by wrapping them
|
364
471
|
# in Arel.sql.
|
365
472
|
#
|
366
|
-
# For example,
|
367
|
-
# code would raise this exception:
|
473
|
+
# For example, the following code would raise this exception:
|
368
474
|
#
|
369
|
-
# Post.order("
|
475
|
+
# Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
|
370
476
|
#
|
371
477
|
# The desired result can be accomplished by wrapping the known-safe string
|
372
478
|
# in Arel.sql:
|
373
479
|
#
|
374
|
-
# Post.order(Arel.sql("
|
480
|
+
# Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
|
375
481
|
#
|
376
482
|
# Again, such a workaround should *not* be used when passing user-provided
|
377
483
|
# values, such as request parameters or model attributes to query methods.
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
# Returns a formatted string ready to be logged.
|
19
19
|
def exec_explain(queries) # :nodoc:
|
20
20
|
str = queries.map do |sql, binds|
|
21
|
-
msg = "EXPLAIN for: #{sql}"
|
21
|
+
msg = +"EXPLAIN for: #{sql}"
|
22
22
|
unless binds.empty?
|
23
23
|
msg << " "
|
24
24
|
msg << binds.map { |attr| render_bind(attr) }.inspect
|
@@ -36,15 +36,19 @@ module ActiveRecord
|
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
|
-
|
40
39
|
def render_bind(attr)
|
41
|
-
|
42
|
-
|
40
|
+
if ActiveModel::Attribute === attr
|
41
|
+
value = if attr.type.binary? && attr.value
|
42
|
+
"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
43
|
+
else
|
44
|
+
connection.type_cast(attr.value_for_database)
|
45
|
+
end
|
43
46
|
else
|
44
|
-
connection.type_cast(attr
|
47
|
+
value = connection.type_cast(attr)
|
48
|
+
attr = nil
|
45
49
|
end
|
46
50
|
|
47
|
-
[attr
|
51
|
+
[attr&.name, value]
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
# This is a thread locals registry for EXPLAIN. For example
|
@@ -8,13 +8,18 @@ module ActiveRecord
|
|
8
8
|
# ActiveRecord::ExplainRegistry.queries
|
9
9
|
#
|
10
10
|
# returns the collected queries local to the current thread.
|
11
|
-
#
|
12
|
-
# See the documentation of ActiveSupport::PerThreadRegistry
|
13
|
-
# for further details.
|
14
11
|
class ExplainRegistry # :nodoc:
|
15
|
-
|
12
|
+
class << self
|
13
|
+
delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
14
|
+
|
15
|
+
private
|
16
|
+
def instance
|
17
|
+
ActiveSupport::IsolatedExecutionState[:active_record_explain_registry] ||= new
|
18
|
+
end
|
19
|
+
end
|
16
20
|
|
17
|
-
attr_accessor :
|
21
|
+
attr_accessor :collect
|
22
|
+
attr_reader :queries
|
18
23
|
|
19
24
|
def initialize
|
20
25
|
reset
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
payload[:exception] ||
|
27
27
|
payload[:cached] ||
|
28
28
|
IGNORED_PAYLOADS.include?(payload[:name]) ||
|
29
|
-
payload[:sql]
|
29
|
+
!payload[:sql].match?(EXPLAINED_SQLS)
|
30
30
|
end
|
31
31
|
|
32
32
|
ActiveSupport::Notifications.subscribe("sql.active_record", new)
|