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
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/insert_all"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
# = Active Record \Persistence
|
5
7
|
module Persistence
|
@@ -55,6 +57,283 @@ module ActiveRecord
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
# Inserts a single record into the database in a single SQL INSERT
|
61
|
+
# statement. It does not instantiate any models nor does it trigger
|
62
|
+
# Active Record callbacks or validations. Though passed values
|
63
|
+
# go through Active Record's type casting and serialization.
|
64
|
+
#
|
65
|
+
# See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
|
66
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
67
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
71
|
+
# statement. It does not instantiate any models nor does it trigger
|
72
|
+
# Active Record callbacks or validations. Though passed values
|
73
|
+
# go through Active Record's type casting and serialization.
|
74
|
+
#
|
75
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
76
|
+
# the attributes for a single row and must have the same keys.
|
77
|
+
#
|
78
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
79
|
+
# duplicate rows are skipped.
|
80
|
+
# Override with <tt>:unique_by</tt> (see below).
|
81
|
+
#
|
82
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
83
|
+
# <tt>:returning</tt> (see below).
|
84
|
+
#
|
85
|
+
# ==== Options
|
86
|
+
#
|
87
|
+
# [:returning]
|
88
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
89
|
+
# inserted records, which by default is the primary key.
|
90
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
91
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
|
+
# clause entirely.
|
93
|
+
#
|
94
|
+
# You can also pass an SQL string if you need more control on the return values
|
95
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
96
|
+
#
|
97
|
+
# [:unique_by]
|
98
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
99
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
100
|
+
#
|
101
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
102
|
+
#
|
103
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
104
|
+
# row has an existing id, or is not unique by another unique index,
|
105
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
106
|
+
#
|
107
|
+
# Unique indexes can be identified by columns or name:
|
108
|
+
#
|
109
|
+
# unique_by: :isbn
|
110
|
+
# unique_by: %i[ author_id name ]
|
111
|
+
# unique_by: :index_books_on_isbn
|
112
|
+
#
|
113
|
+
# [:record_timestamps]
|
114
|
+
# By default, automatic setting of timestamp columns is controlled by
|
115
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
116
|
+
# behavior.
|
117
|
+
#
|
118
|
+
# To override this and force automatic setting of timestamp columns one
|
119
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
120
|
+
#
|
121
|
+
# record_timestamps: true # Always set timestamps automatically
|
122
|
+
# record_timestamps: false # Never set timestamps automatically
|
123
|
+
#
|
124
|
+
# Because it relies on the index information from the database
|
125
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
126
|
+
# Active Record's schema_cache.
|
127
|
+
#
|
128
|
+
# ==== Example
|
129
|
+
#
|
130
|
+
# # Insert records and skip inserting any duplicates.
|
131
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
132
|
+
#
|
133
|
+
# Book.insert_all([
|
134
|
+
# { id: 1, title: "Rework", author: "David" },
|
135
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
136
|
+
# ])
|
137
|
+
#
|
138
|
+
# # insert_all works on chained scopes, and you can use create_with
|
139
|
+
# # to set default attributes for all inserted records.
|
140
|
+
#
|
141
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
142
|
+
# { id: 1, title: "Rework" },
|
143
|
+
# { id: 2, title: "Eloquent Ruby" }
|
144
|
+
# ])
|
145
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
146
|
+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
147
|
+
end
|
148
|
+
|
149
|
+
# Inserts a single record into the database in a single SQL INSERT
|
150
|
+
# statement. It does not instantiate any models nor does it trigger
|
151
|
+
# Active Record callbacks or validations. Though passed values
|
152
|
+
# go through Active Record's type casting and serialization.
|
153
|
+
#
|
154
|
+
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
155
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
156
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
160
|
+
# statement. It does not instantiate any models nor does it trigger
|
161
|
+
# Active Record callbacks or validations. Though passed values
|
162
|
+
# go through Active Record's type casting and serialization.
|
163
|
+
#
|
164
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
165
|
+
# the attributes for a single row and must have the same keys.
|
166
|
+
#
|
167
|
+
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
168
|
+
# unique index on the table. In that case, no rows are inserted.
|
169
|
+
#
|
170
|
+
# To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
|
171
|
+
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
172
|
+
#
|
173
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
174
|
+
# <tt>:returning</tt> (see below).
|
175
|
+
#
|
176
|
+
# ==== Options
|
177
|
+
#
|
178
|
+
# [:returning]
|
179
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
180
|
+
# inserted records, which by default is the primary key.
|
181
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
182
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
183
|
+
# clause entirely.
|
184
|
+
#
|
185
|
+
# You can also pass an SQL string if you need more control on the return values
|
186
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
187
|
+
#
|
188
|
+
# [:record_timestamps]
|
189
|
+
# By default, automatic setting of timestamp columns is controlled by
|
190
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
191
|
+
# behavior.
|
192
|
+
#
|
193
|
+
# To override this and force automatic setting of timestamp columns one
|
194
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
195
|
+
#
|
196
|
+
# record_timestamps: true # Always set timestamps automatically
|
197
|
+
# record_timestamps: false # Never set timestamps automatically
|
198
|
+
#
|
199
|
+
# ==== Examples
|
200
|
+
#
|
201
|
+
# # Insert multiple records
|
202
|
+
# Book.insert_all!([
|
203
|
+
# { title: "Rework", author: "David" },
|
204
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
205
|
+
# ])
|
206
|
+
#
|
207
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
208
|
+
# # does not have a unique id.
|
209
|
+
# Book.insert_all!([
|
210
|
+
# { id: 1, title: "Rework", author: "David" },
|
211
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
212
|
+
# ])
|
213
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
214
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
|
215
|
+
end
|
216
|
+
|
217
|
+
# Updates or inserts (upserts) a single record into the database in a
|
218
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
219
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
220
|
+
# go through Active Record's type casting and serialization.
|
221
|
+
#
|
222
|
+
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
223
|
+
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
|
224
|
+
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
228
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
229
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
230
|
+
# go through Active Record's type casting and serialization.
|
231
|
+
#
|
232
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
233
|
+
# the attributes for a single row and must have the same keys.
|
234
|
+
#
|
235
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
236
|
+
# <tt>:returning</tt> (see below).
|
237
|
+
#
|
238
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
239
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
240
|
+
# columns, and columns covered by the optional +unique_by+.
|
241
|
+
#
|
242
|
+
# ==== Options
|
243
|
+
#
|
244
|
+
# [:returning]
|
245
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
246
|
+
# inserted records, which by default is the primary key.
|
247
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
248
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
249
|
+
# clause entirely.
|
250
|
+
#
|
251
|
+
# You can also pass an SQL string if you need more control on the return values
|
252
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
253
|
+
#
|
254
|
+
# [:unique_by]
|
255
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
256
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
257
|
+
#
|
258
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
259
|
+
#
|
260
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
261
|
+
# row has an existing id, or is not unique by another unique index,
|
262
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
263
|
+
#
|
264
|
+
# Unique indexes can be identified by columns or name:
|
265
|
+
#
|
266
|
+
# unique_by: :isbn
|
267
|
+
# unique_by: %i[ author_id name ]
|
268
|
+
# unique_by: :index_books_on_isbn
|
269
|
+
#
|
270
|
+
# Because it relies on the index information from the database
|
271
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
272
|
+
# Active Record's schema_cache.
|
273
|
+
#
|
274
|
+
# [:on_duplicate]
|
275
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
276
|
+
#
|
277
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
278
|
+
# by yourself.
|
279
|
+
#
|
280
|
+
# Example:
|
281
|
+
#
|
282
|
+
# Commodity.upsert_all(
|
283
|
+
# [
|
284
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
285
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
286
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
287
|
+
# ],
|
288
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
289
|
+
# )
|
290
|
+
#
|
291
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
292
|
+
#
|
293
|
+
# [:update_only]
|
294
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
295
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
296
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
297
|
+
#
|
298
|
+
# Example:
|
299
|
+
#
|
300
|
+
# Commodity.upsert_all(
|
301
|
+
# [
|
302
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
303
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
304
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
305
|
+
# ],
|
306
|
+
# update_only: [:price] # Only prices will be updated
|
307
|
+
# )
|
308
|
+
#
|
309
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
310
|
+
#
|
311
|
+
# [:record_timestamps]
|
312
|
+
# By default, automatic setting of timestamp columns is controlled by
|
313
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
314
|
+
# behavior.
|
315
|
+
#
|
316
|
+
# To override this and force automatic setting of timestamp columns one
|
317
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
318
|
+
#
|
319
|
+
# record_timestamps: true # Always set timestamps automatically
|
320
|
+
# record_timestamps: false # Never set timestamps automatically
|
321
|
+
#
|
322
|
+
# ==== Examples
|
323
|
+
#
|
324
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
325
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
326
|
+
#
|
327
|
+
# Book.upsert_all([
|
328
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
329
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
330
|
+
# ], unique_by: :isbn)
|
331
|
+
#
|
332
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
333
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
334
|
+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
335
|
+
end
|
336
|
+
|
58
337
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
59
338
|
# the appropriate class. Accepts only keys as strings.
|
60
339
|
#
|
@@ -67,8 +346,7 @@ module ActiveRecord
|
|
67
346
|
# how this "single-table" inheritance mapping is implemented.
|
68
347
|
def instantiate(attributes, column_types = {}, &block)
|
69
348
|
klass = discriminate_class_for_record(attributes)
|
70
|
-
|
71
|
-
klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block)
|
349
|
+
instantiate_instance_of(klass, attributes, column_types, &block)
|
72
350
|
end
|
73
351
|
|
74
352
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -77,6 +355,7 @@ module ActiveRecord
|
|
77
355
|
# ==== Parameters
|
78
356
|
#
|
79
357
|
# * +id+ - This should be the id or an array of ids to be updated.
|
358
|
+
# Optional argument, defaults to all records in the relation.
|
80
359
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
81
360
|
#
|
82
361
|
# ==== Examples
|
@@ -99,6 +378,11 @@ module ActiveRecord
|
|
99
378
|
# for updating all records in a single query.
|
100
379
|
def update(id = :all, attributes)
|
101
380
|
if id.is_a?(Array)
|
381
|
+
if id.any?(ActiveRecord::Base)
|
382
|
+
raise ArgumentError,
|
383
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
384
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
385
|
+
end
|
102
386
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
103
387
|
object.update(attributes[idx])
|
104
388
|
}
|
@@ -116,6 +400,32 @@ module ActiveRecord
|
|
116
400
|
end
|
117
401
|
end
|
118
402
|
|
403
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
404
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
405
|
+
def update!(id = :all, attributes)
|
406
|
+
if id.is_a?(Array)
|
407
|
+
if id.any?(ActiveRecord::Base)
|
408
|
+
raise ArgumentError,
|
409
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
410
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
411
|
+
end
|
412
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
413
|
+
object.update!(attributes[idx])
|
414
|
+
}
|
415
|
+
elsif id == :all
|
416
|
+
all.each { |record| record.update!(attributes) }
|
417
|
+
else
|
418
|
+
if ActiveRecord::Base === id
|
419
|
+
raise ArgumentError,
|
420
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
421
|
+
"Please pass the id of the object by calling `.id`."
|
422
|
+
end
|
423
|
+
object = find(id)
|
424
|
+
object.update!(attributes)
|
425
|
+
object
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
119
429
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
120
430
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
121
431
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -143,7 +453,7 @@ module ActiveRecord
|
|
143
453
|
end
|
144
454
|
end
|
145
455
|
|
146
|
-
# Deletes the row with a primary key matching the +id+ argument, using
|
456
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
147
457
|
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
148
458
|
# Record objects are not instantiated, so the object's callbacks are not
|
149
459
|
# executed, including any <tt>:dependent</tt> association options.
|
@@ -162,52 +472,72 @@ module ActiveRecord
|
|
162
472
|
# # Delete multiple rows
|
163
473
|
# Todo.delete([2,3,4])
|
164
474
|
def delete(id_or_array)
|
165
|
-
|
475
|
+
delete_by(primary_key => id_or_array)
|
166
476
|
end
|
167
477
|
|
168
478
|
def _insert_record(values) # :nodoc:
|
479
|
+
primary_key = self.primary_key
|
169
480
|
primary_key_value = nil
|
170
481
|
|
171
|
-
if
|
172
|
-
|
173
|
-
|
174
|
-
if !primary_key_value && prefetch_primary_key?
|
482
|
+
if prefetch_primary_key? && primary_key
|
483
|
+
values[primary_key] ||= begin
|
175
484
|
primary_key_value = next_sequence_value
|
176
|
-
|
485
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
177
486
|
end
|
178
487
|
end
|
179
488
|
|
489
|
+
im = Arel::InsertManager.new(arel_table)
|
490
|
+
|
180
491
|
if values.empty?
|
181
|
-
im
|
182
|
-
im.into arel_table
|
492
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
183
493
|
else
|
184
|
-
im
|
494
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
185
495
|
end
|
186
496
|
|
187
497
|
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
188
498
|
end
|
189
499
|
|
190
500
|
def _update_record(values, constraints) # :nodoc:
|
191
|
-
constraints =
|
501
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
502
|
+
|
503
|
+
default_constraint = build_default_constraint
|
504
|
+
constraints << default_constraint if default_constraint
|
192
505
|
|
193
|
-
|
194
|
-
constraints.
|
195
|
-
|
506
|
+
if current_scope = self.global_current_scope
|
507
|
+
constraints << current_scope.where_clause.ast
|
508
|
+
end
|
509
|
+
|
510
|
+
um = Arel::UpdateManager.new(arel_table)
|
511
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
512
|
+
um.wheres = constraints
|
196
513
|
|
197
514
|
connection.update(um, "#{self} Update")
|
198
515
|
end
|
199
516
|
|
200
517
|
def _delete_record(constraints) # :nodoc:
|
201
|
-
constraints =
|
518
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
519
|
+
|
520
|
+
default_constraint = build_default_constraint
|
521
|
+
constraints << default_constraint if default_constraint
|
522
|
+
|
523
|
+
if current_scope = self.global_current_scope
|
524
|
+
constraints << current_scope.where_clause.ast
|
525
|
+
end
|
202
526
|
|
203
|
-
dm = Arel::DeleteManager.new
|
204
|
-
dm.from(arel_table)
|
527
|
+
dm = Arel::DeleteManager.new(arel_table)
|
205
528
|
dm.wheres = constraints
|
206
529
|
|
207
530
|
connection.delete(dm, "#{self} Destroy")
|
208
531
|
end
|
209
532
|
|
210
533
|
private
|
534
|
+
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
535
|
+
# new instance of the class. Accepts only keys as strings.
|
536
|
+
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
537
|
+
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
|
538
|
+
klass.allocate.init_with_attributes(attributes, &block)
|
539
|
+
end
|
540
|
+
|
211
541
|
# Called by +instantiate+ to decide which class to use for a new
|
212
542
|
# record instance.
|
213
543
|
#
|
@@ -217,38 +547,49 @@ module ActiveRecord
|
|
217
547
|
self
|
218
548
|
end
|
219
549
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
550
|
+
# Called by +_update_record+ and +_delete_record+
|
551
|
+
# to build `where` clause from default scopes.
|
552
|
+
# Skips empty scopes.
|
553
|
+
def build_default_constraint
|
554
|
+
return unless default_scopes?(all_queries: true)
|
555
|
+
|
556
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
557
|
+
default_where_clause.ast unless default_where_clause.empty?
|
226
558
|
end
|
227
559
|
end
|
228
560
|
|
229
561
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
230
562
|
# for the object doesn't exist in the database yet; otherwise, returns false.
|
231
563
|
def new_record?
|
232
|
-
sync_with_transaction_state
|
233
564
|
@new_record
|
234
565
|
end
|
235
566
|
|
567
|
+
# Returns true if this object was just created -- that is, prior to the last
|
568
|
+
# save, the object didn't exist in the database and new_record? would have
|
569
|
+
# returned true.
|
570
|
+
def previously_new_record?
|
571
|
+
@previously_new_record
|
572
|
+
end
|
573
|
+
|
574
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
575
|
+
def previously_persisted?
|
576
|
+
!new_record? && destroyed?
|
577
|
+
end
|
578
|
+
|
236
579
|
# Returns true if this object has been destroyed, otherwise returns false.
|
237
580
|
def destroyed?
|
238
|
-
sync_with_transaction_state
|
239
581
|
@destroyed
|
240
582
|
end
|
241
583
|
|
242
584
|
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
243
585
|
# not destroyed, otherwise returns false.
|
244
586
|
def persisted?
|
245
|
-
sync_with_transaction_state
|
246
587
|
!(@new_record || @destroyed)
|
247
588
|
end
|
248
589
|
|
249
590
|
##
|
250
591
|
# :call-seq:
|
251
|
-
# save(
|
592
|
+
# save(**options)
|
252
593
|
#
|
253
594
|
# Saves the model.
|
254
595
|
#
|
@@ -271,15 +612,15 @@ module ActiveRecord
|
|
271
612
|
#
|
272
613
|
# Attributes marked as readonly are silently ignored if the record is
|
273
614
|
# being updated.
|
274
|
-
def save(
|
275
|
-
create_or_update(
|
615
|
+
def save(**options, &block)
|
616
|
+
create_or_update(**options, &block)
|
276
617
|
rescue ActiveRecord::RecordInvalid
|
277
618
|
false
|
278
619
|
end
|
279
620
|
|
280
621
|
##
|
281
622
|
# :call-seq:
|
282
|
-
# save!(
|
623
|
+
# save!(**options)
|
283
624
|
#
|
284
625
|
# Saves the model.
|
285
626
|
#
|
@@ -304,8 +645,8 @@ module ActiveRecord
|
|
304
645
|
# being updated.
|
305
646
|
#
|
306
647
|
# Unless an error is raised, returns true.
|
307
|
-
def save!(
|
308
|
-
create_or_update(
|
648
|
+
def save!(**options, &block)
|
649
|
+
create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
|
309
650
|
end
|
310
651
|
|
311
652
|
# Deletes the record in the database and freezes this instance to
|
@@ -319,7 +660,7 @@ module ActiveRecord
|
|
319
660
|
#
|
320
661
|
# To enforce the object's +before_destroy+ and +after_destroy+
|
321
662
|
# callbacks or any <tt>:dependent</tt> association
|
322
|
-
# options, use
|
663
|
+
# options, use #destroy.
|
323
664
|
def delete
|
324
665
|
_delete_row if persisted?
|
325
666
|
@destroyed = true
|
@@ -336,7 +677,6 @@ module ActiveRecord
|
|
336
677
|
def destroy
|
337
678
|
_raise_readonly_record_error if readonly?
|
338
679
|
destroy_associations
|
339
|
-
self.class.connection.add_transaction_record(self)
|
340
680
|
@_trigger_destroy_callback = if persisted?
|
341
681
|
destroy_row > 0
|
342
682
|
else
|
@@ -358,34 +698,36 @@ module ActiveRecord
|
|
358
698
|
end
|
359
699
|
|
360
700
|
# Returns an instance of the specified +klass+ with the attributes of the
|
361
|
-
# current record. This is mostly useful in relation to single
|
362
|
-
# inheritance structures where you want a subclass to appear as the
|
701
|
+
# current record. This is mostly useful in relation to single table
|
702
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
363
703
|
# superclass. This can be used along with record identification in
|
364
704
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
365
705
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
366
706
|
# instance using the companies/company partial instead of clients/client.
|
367
707
|
#
|
368
708
|
# Note: The new instance will share a link to the same attributes as the original class.
|
369
|
-
# Therefore the
|
709
|
+
# Therefore the STI column value will still be the same.
|
370
710
|
# Any change to the attributes on either instance will affect both instances.
|
371
|
-
# If you want to change the
|
711
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
372
712
|
def becomes(klass)
|
373
713
|
became = klass.allocate
|
374
|
-
|
375
|
-
became.
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
714
|
+
|
715
|
+
became.send(:initialize) do |becoming|
|
716
|
+
becoming.instance_variable_set(:@attributes, @attributes)
|
717
|
+
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
718
|
+
becoming.instance_variable_set(:@new_record, new_record?)
|
719
|
+
becoming.instance_variable_set(:@destroyed, destroyed?)
|
720
|
+
becoming.errors.copy!(errors)
|
721
|
+
end
|
722
|
+
|
381
723
|
became
|
382
724
|
end
|
383
725
|
|
384
|
-
# Wrapper around #becomes that also changes the instance's
|
726
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
385
727
|
# This is especially useful if you want to persist the changed class in your
|
386
728
|
# database.
|
387
729
|
#
|
388
|
-
# Note: The old instance's
|
730
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
389
731
|
# share the same set of attributes.
|
390
732
|
def becomes!(klass)
|
391
733
|
became = becomes(klass)
|
@@ -429,8 +771,6 @@ module ActiveRecord
|
|
429
771
|
end
|
430
772
|
end
|
431
773
|
|
432
|
-
alias update_attributes update
|
433
|
-
|
434
774
|
# Updates its receiver just like #update but calls #save! instead
|
435
775
|
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
436
776
|
def update!(attributes)
|
@@ -442,8 +782,6 @@ module ActiveRecord
|
|
442
782
|
end
|
443
783
|
end
|
444
784
|
|
445
|
-
alias update_attributes! update!
|
446
|
-
|
447
785
|
# Equivalent to <code>update_columns(name => value)</code>.
|
448
786
|
def update_column(name, value)
|
449
787
|
update_columns(name => value)
|
@@ -469,18 +807,21 @@ module ActiveRecord
|
|
469
807
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
470
808
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
471
809
|
|
472
|
-
attributes.
|
473
|
-
|
810
|
+
attributes = attributes.transform_keys do |key|
|
811
|
+
name = key.to_s
|
812
|
+
name = self.class.attribute_aliases[name] || name
|
813
|
+
verify_readonly_attribute(name) || name
|
474
814
|
end
|
475
815
|
|
476
|
-
|
477
|
-
attributes.
|
478
|
-
|
816
|
+
update_constraints = _primary_key_constraints_hash
|
817
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
818
|
+
h[k] = @attributes.write_cast_value(k, v)
|
819
|
+
clear_attribute_change(k)
|
479
820
|
end
|
480
821
|
|
481
822
|
affected_rows = self.class._update_record(
|
482
823
|
attributes,
|
483
|
-
|
824
|
+
update_constraints
|
484
825
|
)
|
485
826
|
|
486
827
|
affected_rows == 1
|
@@ -503,9 +844,9 @@ module ActiveRecord
|
|
503
844
|
# Returns +self+.
|
504
845
|
def increment!(attribute, by = 1, touch: nil)
|
505
846
|
increment(attribute, by)
|
506
|
-
change = public_send(attribute) - (
|
847
|
+
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
|
507
848
|
self.class.update_counters(id, attribute => change, touch: touch)
|
508
|
-
|
849
|
+
public_send(:"clear_#{attribute}_change")
|
509
850
|
self
|
510
851
|
end
|
511
852
|
|
@@ -602,15 +943,16 @@ module ActiveRecord
|
|
602
943
|
def reload(options = nil)
|
603
944
|
self.class.connection.clear_query_cache
|
604
945
|
|
605
|
-
fresh_object =
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
end
|
946
|
+
fresh_object = if apply_scoping?(options)
|
947
|
+
_find_record(options)
|
948
|
+
else
|
949
|
+
self.class.unscoped { _find_record(options) }
|
950
|
+
end
|
611
951
|
|
612
|
-
@
|
952
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
953
|
+
@attributes = fresh_object.instance_variable_get(:@attributes)
|
613
954
|
@new_record = false
|
955
|
+
@previously_new_record = false
|
614
956
|
self
|
615
957
|
end
|
616
958
|
|
@@ -649,15 +991,13 @@ module ActiveRecord
|
|
649
991
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
650
992
|
#
|
651
993
|
def touch(*names, time: nil)
|
652
|
-
unless persisted?
|
653
|
-
raise ActiveRecordError, <<-MSG.squish
|
654
|
-
cannot touch on a new or destroyed record object. Consider using
|
655
|
-
persisted?, new_record?, or destroyed? before touching
|
656
|
-
MSG
|
657
|
-
end
|
994
|
+
_raise_record_not_touched_error unless persisted?
|
658
995
|
|
659
996
|
attribute_names = timestamp_attributes_for_update_in_model
|
660
|
-
attribute_names |= names.map
|
997
|
+
attribute_names |= names.map! do |name|
|
998
|
+
name = name.to_s
|
999
|
+
self.class.attribute_aliases[name] || name
|
1000
|
+
end unless names.empty?
|
661
1001
|
|
662
1002
|
unless attribute_names.empty?
|
663
1003
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -668,6 +1008,28 @@ module ActiveRecord
|
|
668
1008
|
end
|
669
1009
|
|
670
1010
|
private
|
1011
|
+
def strict_loaded_associations
|
1012
|
+
@association_cache.find_all do |_, assoc|
|
1013
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
1014
|
+
end.map(&:first)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def _find_record(options)
|
1018
|
+
if options && options[:lock]
|
1019
|
+
self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
|
1020
|
+
else
|
1021
|
+
self.class.preload(strict_loaded_associations).find(id)
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def apply_scoping?(options)
|
1026
|
+
!(options && options[:unscoped]) &&
|
1027
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def _primary_key_constraints_hash
|
1031
|
+
{ @primary_key => id_in_database }
|
1032
|
+
end
|
671
1033
|
|
672
1034
|
# A hook to be overridden by association modules.
|
673
1035
|
def destroy_associations
|
@@ -678,15 +1040,14 @@ module ActiveRecord
|
|
678
1040
|
end
|
679
1041
|
|
680
1042
|
def _delete_row
|
681
|
-
self.class._delete_record(
|
1043
|
+
self.class._delete_record(_primary_key_constraints_hash)
|
682
1044
|
end
|
683
1045
|
|
684
1046
|
def _touch_row(attribute_names, time)
|
685
1047
|
time ||= current_time_from_proper_timezone
|
686
1048
|
|
687
1049
|
attribute_names.each do |attr_name|
|
688
|
-
|
689
|
-
clear_attribute_change(attr_name)
|
1050
|
+
_write_attribute(attr_name, time)
|
690
1051
|
end
|
691
1052
|
|
692
1053
|
_update_row(attribute_names, "touch")
|
@@ -695,21 +1056,20 @@ module ActiveRecord
|
|
695
1056
|
def _update_row(attribute_names, attempted_action = "update")
|
696
1057
|
self.class._update_record(
|
697
1058
|
attributes_with_values(attribute_names),
|
698
|
-
|
1059
|
+
_primary_key_constraints_hash
|
699
1060
|
)
|
700
1061
|
end
|
701
1062
|
|
702
|
-
def create_or_update(
|
1063
|
+
def create_or_update(**, &block)
|
703
1064
|
_raise_readonly_record_error if readonly?
|
704
1065
|
return false if destroyed?
|
705
|
-
result = new_record? ? _create_record(&block) : _update_record(
|
1066
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
706
1067
|
result != false
|
707
1068
|
end
|
708
1069
|
|
709
1070
|
# Updates the associated record with values matching those of the instance attributes.
|
710
1071
|
# Returns the number of affected rows.
|
711
1072
|
def _update_record(attribute_names = self.attribute_names)
|
712
|
-
attribute_names &= self.class.column_names
|
713
1073
|
attribute_names = attributes_for_update(attribute_names)
|
714
1074
|
|
715
1075
|
if attribute_names.empty?
|
@@ -720,6 +1080,8 @@ module ActiveRecord
|
|
720
1080
|
@_trigger_update_callback = affected_rows == 1
|
721
1081
|
end
|
722
1082
|
|
1083
|
+
@previously_new_record = false
|
1084
|
+
|
723
1085
|
yield(self) if block_given?
|
724
1086
|
|
725
1087
|
affected_rows
|
@@ -728,13 +1090,16 @@ module ActiveRecord
|
|
728
1090
|
# Creates a record with values matching those of the instance attributes
|
729
1091
|
# and returns its id.
|
730
1092
|
def _create_record(attribute_names = self.attribute_names)
|
731
|
-
attribute_names
|
732
|
-
|
1093
|
+
attribute_names = attributes_for_create(attribute_names)
|
1094
|
+
|
1095
|
+
new_id = self.class._insert_record(
|
1096
|
+
attributes_with_values(attribute_names)
|
1097
|
+
)
|
733
1098
|
|
734
|
-
new_id
|
735
|
-
self.id ||= new_id if self.class.primary_key
|
1099
|
+
self.id ||= new_id if @primary_key
|
736
1100
|
|
737
1101
|
@new_record = false
|
1102
|
+
@previously_new_record = true
|
738
1103
|
|
739
1104
|
yield(self) if block_given?
|
740
1105
|
|
@@ -742,22 +1107,32 @@ module ActiveRecord
|
|
742
1107
|
end
|
743
1108
|
|
744
1109
|
def verify_readonly_attribute(name)
|
745
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.
|
1110
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
746
1111
|
end
|
747
1112
|
|
748
1113
|
def _raise_record_not_destroyed
|
749
1114
|
@_association_destroy_exception ||= nil
|
750
|
-
|
1115
|
+
key = self.class.primary_key
|
1116
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
|
751
1117
|
ensure
|
752
1118
|
@_association_destroy_exception = nil
|
753
1119
|
end
|
754
1120
|
|
755
|
-
def belongs_to_touch_method
|
756
|
-
:touch
|
757
|
-
end
|
758
|
-
|
759
1121
|
def _raise_readonly_record_error
|
760
1122
|
raise ReadOnlyRecord, "#{self.class} is marked as readonly"
|
761
1123
|
end
|
1124
|
+
|
1125
|
+
def _raise_record_not_touched_error
|
1126
|
+
raise ActiveRecordError, <<~MSG.squish
|
1127
|
+
Cannot touch on a new or destroyed record object. Consider using
|
1128
|
+
persisted?, new_record?, or destroyed? before touching.
|
1129
|
+
MSG
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# The name of the method used to touch a +belongs_to+ association when the
|
1133
|
+
# +:touch+ option is used.
|
1134
|
+
def belongs_to_touch_method
|
1135
|
+
:touch
|
1136
|
+
end
|
762
1137
|
end
|
763
1138
|
end
|