activerecord 6.0.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -25
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +171 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -57,190 +57,34 @@ module ActiveRecord
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# Active Record callbacks or validations. Though passed values
|
|
63
|
-
# go through Active Record's type casting and serialization.
|
|
60
|
+
# Builds an object (or multiple objects) and returns either the built object or a list of built
|
|
61
|
+
# objects.
|
|
64
62
|
#
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
|
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
|
-
# [:unique_by]
|
|
95
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
|
96
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
|
97
|
-
#
|
|
98
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
|
99
|
-
#
|
|
100
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
|
101
|
-
# row has an existing id, or is not unique by another unique index,
|
|
102
|
-
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
|
103
|
-
#
|
|
104
|
-
# Unique indexes can be identified by columns or name:
|
|
105
|
-
#
|
|
106
|
-
# unique_by: :isbn
|
|
107
|
-
# unique_by: %i[ author_id name ]
|
|
108
|
-
# unique_by: :index_books_on_isbn
|
|
109
|
-
#
|
|
110
|
-
# Because it relies on the index information from the database
|
|
111
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
|
112
|
-
# Active Record's schema_cache.
|
|
113
|
-
#
|
|
114
|
-
# ==== Example
|
|
115
|
-
#
|
|
116
|
-
# # Insert records and skip inserting any duplicates.
|
|
117
|
-
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
|
118
|
-
#
|
|
119
|
-
# Book.insert_all([
|
|
120
|
-
# { id: 1, title: "Rework", author: "David" },
|
|
121
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
|
122
|
-
# ])
|
|
123
|
-
def insert_all(attributes, returning: nil, unique_by: nil)
|
|
124
|
-
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Inserts a single record into the database in a single SQL INSERT
|
|
128
|
-
# statement. It does not instantiate any models nor does it trigger
|
|
129
|
-
# Active Record callbacks or validations. Though passed values
|
|
130
|
-
# go through Active Record's type casting and serialization.
|
|
131
|
-
#
|
|
132
|
-
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
|
133
|
-
def insert!(attributes, returning: nil)
|
|
134
|
-
insert_all!([ attributes ], returning: returning)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Inserts multiple records into the database in a single SQL INSERT
|
|
138
|
-
# statement. It does not instantiate any models nor does it trigger
|
|
139
|
-
# Active Record callbacks or validations. Though passed values
|
|
140
|
-
# go through Active Record's type casting and serialization.
|
|
141
|
-
#
|
|
142
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
|
143
|
-
# the attributes for a single row and must have the same keys.
|
|
144
|
-
#
|
|
145
|
-
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
|
146
|
-
# unique index on the table. In that case, no rows are inserted.
|
|
147
|
-
#
|
|
148
|
-
# To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
|
|
149
|
-
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
|
150
|
-
#
|
|
151
|
-
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
|
152
|
-
# <tt>:returning</tt> (see below).
|
|
153
|
-
#
|
|
154
|
-
# ==== Options
|
|
155
|
-
#
|
|
156
|
-
# [:returning]
|
|
157
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
|
158
|
-
# inserted records, which by default is the primary key.
|
|
159
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
160
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
161
|
-
# clause entirely.
|
|
162
|
-
#
|
|
163
|
-
# ==== Examples
|
|
164
|
-
#
|
|
165
|
-
# # Insert multiple records
|
|
166
|
-
# Book.insert_all!([
|
|
167
|
-
# { title: "Rework", author: "David" },
|
|
168
|
-
# { title: "Eloquent Ruby", author: "Russ" }
|
|
169
|
-
# ])
|
|
170
|
-
#
|
|
171
|
-
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
|
172
|
-
# # does not have a unique id.
|
|
173
|
-
# Book.insert_all!([
|
|
174
|
-
# { id: 1, title: "Rework", author: "David" },
|
|
175
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
|
176
|
-
# ])
|
|
177
|
-
def insert_all!(attributes, returning: nil)
|
|
178
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Updates or inserts (upserts) a single record into the database in a
|
|
182
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
|
183
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
|
184
|
-
# go through Active Record's type casting and serialization.
|
|
185
|
-
#
|
|
186
|
-
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
|
187
|
-
def upsert(attributes, returning: nil, unique_by: nil)
|
|
188
|
-
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Updates or inserts (upserts) multiple records into the database in a
|
|
192
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
|
193
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
|
194
|
-
# go through Active Record's type casting and serialization.
|
|
195
|
-
#
|
|
196
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
|
197
|
-
# the attributes for a single row and must have the same keys.
|
|
198
|
-
#
|
|
199
|
-
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
|
200
|
-
# <tt>:returning</tt> (see below).
|
|
201
|
-
#
|
|
202
|
-
# ==== Options
|
|
203
|
-
#
|
|
204
|
-
# [:returning]
|
|
205
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
|
206
|
-
# inserted records, which by default is the primary key.
|
|
207
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
208
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
209
|
-
# clause entirely.
|
|
210
|
-
#
|
|
211
|
-
# [:unique_by]
|
|
212
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
|
213
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
|
214
|
-
#
|
|
215
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
|
216
|
-
#
|
|
217
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
|
218
|
-
# row has an existing id, or is not unique by another unique index,
|
|
219
|
-
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
|
220
|
-
#
|
|
221
|
-
# Unique indexes can be identified by columns or name:
|
|
222
|
-
#
|
|
223
|
-
# unique_by: :isbn
|
|
224
|
-
# unique_by: %i[ author_id name ]
|
|
225
|
-
# unique_by: :index_books_on_isbn
|
|
226
|
-
#
|
|
227
|
-
# Because it relies on the index information from the database
|
|
228
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
|
229
|
-
# Active Record's schema_cache.
|
|
63
|
+
# The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
|
|
64
|
+
# attributes on the objects that are to be built.
|
|
230
65
|
#
|
|
231
66
|
# ==== Examples
|
|
67
|
+
# # Build a single new object
|
|
68
|
+
# User.build(first_name: 'Jamie')
|
|
232
69
|
#
|
|
233
|
-
# #
|
|
234
|
-
#
|
|
70
|
+
# # Build an Array of new objects
|
|
71
|
+
# User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
|
|
235
72
|
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
73
|
+
# # Build a single object and pass it into a block to set other attributes.
|
|
74
|
+
# User.build(first_name: 'Jamie') do |u|
|
|
75
|
+
# u.is_admin = false
|
|
76
|
+
# end
|
|
240
77
|
#
|
|
241
|
-
#
|
|
242
|
-
|
|
243
|
-
|
|
78
|
+
# # Building an Array of new objects using a block, where the block is executed for each object:
|
|
79
|
+
# User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
|
|
80
|
+
# u.is_admin = false
|
|
81
|
+
# end
|
|
82
|
+
def build(attributes = nil, &block)
|
|
83
|
+
if attributes.is_a?(Array)
|
|
84
|
+
attributes.collect { |attr| build(attr, &block) }
|
|
85
|
+
else
|
|
86
|
+
new(attributes, &block)
|
|
87
|
+
end
|
|
244
88
|
end
|
|
245
89
|
|
|
246
90
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
|
@@ -264,6 +108,7 @@ module ActiveRecord
|
|
|
264
108
|
# ==== Parameters
|
|
265
109
|
#
|
|
266
110
|
# * +id+ - This should be the id or an array of ids to be updated.
|
|
111
|
+
# Optional argument, defaults to all records in the relation.
|
|
267
112
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
|
268
113
|
#
|
|
269
114
|
# ==== Examples
|
|
@@ -286,6 +131,11 @@ module ActiveRecord
|
|
|
286
131
|
# for updating all records in a single query.
|
|
287
132
|
def update(id = :all, attributes)
|
|
288
133
|
if id.is_a?(Array)
|
|
134
|
+
if id.any?(ActiveRecord::Base)
|
|
135
|
+
raise ArgumentError,
|
|
136
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
|
137
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
|
138
|
+
end
|
|
289
139
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
|
290
140
|
object.update(attributes[idx])
|
|
291
141
|
}
|
|
@@ -303,99 +153,161 @@ module ActiveRecord
|
|
|
303
153
|
end
|
|
304
154
|
end
|
|
305
155
|
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
|
|
309
|
-
#
|
|
310
|
-
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
|
311
|
-
# from the attributes, and then calls destroy on it.
|
|
312
|
-
#
|
|
313
|
-
# ==== Parameters
|
|
314
|
-
#
|
|
315
|
-
# * +id+ - This should be the id or an array of ids to be destroyed.
|
|
316
|
-
#
|
|
317
|
-
# ==== Examples
|
|
318
|
-
#
|
|
319
|
-
# # Destroy a single object
|
|
320
|
-
# Todo.destroy(1)
|
|
321
|
-
#
|
|
322
|
-
# # Destroy multiple objects
|
|
323
|
-
# todos = [1,2,3]
|
|
324
|
-
# Todo.destroy(todos)
|
|
325
|
-
def destroy(id)
|
|
156
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
|
157
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
|
158
|
+
def update!(id = :all, attributes)
|
|
326
159
|
if id.is_a?(Array)
|
|
327
|
-
|
|
160
|
+
if id.any?(ActiveRecord::Base)
|
|
161
|
+
raise ArgumentError,
|
|
162
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
|
163
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
|
164
|
+
end
|
|
165
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
|
166
|
+
object.update!(attributes[idx])
|
|
167
|
+
}
|
|
168
|
+
elsif id == :all
|
|
169
|
+
all.each { |record| record.update!(attributes) }
|
|
328
170
|
else
|
|
329
|
-
|
|
171
|
+
if ActiveRecord::Base === id
|
|
172
|
+
raise ArgumentError,
|
|
173
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
|
174
|
+
"Please pass the id of the object by calling `.id`."
|
|
175
|
+
end
|
|
176
|
+
object = find(id)
|
|
177
|
+
object.update!(attributes)
|
|
178
|
+
object
|
|
330
179
|
end
|
|
331
180
|
end
|
|
332
181
|
|
|
333
|
-
#
|
|
334
|
-
#
|
|
335
|
-
# Record objects are not instantiated, so the object's callbacks are not
|
|
336
|
-
# executed, including any <tt>:dependent</tt> association options.
|
|
182
|
+
# Accepts a list of attribute names to be used in the WHERE clause
|
|
183
|
+
# of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
|
|
337
184
|
#
|
|
338
|
-
#
|
|
185
|
+
# class Developer < ActiveRecord::Base
|
|
186
|
+
# query_constraints :company_id, :id
|
|
187
|
+
# end
|
|
339
188
|
#
|
|
340
|
-
#
|
|
341
|
-
#
|
|
342
|
-
#
|
|
189
|
+
# developer = Developer.first
|
|
190
|
+
# # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
|
|
191
|
+
# developer.inspect # => #<Developer id: 1, company_id: 1, ...>
|
|
343
192
|
#
|
|
344
|
-
#
|
|
193
|
+
# developer.update!(name: "Nikita")
|
|
194
|
+
# # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
|
345
195
|
#
|
|
346
|
-
# #
|
|
347
|
-
#
|
|
196
|
+
# # It is possible to update an attribute used in the query_constraints clause:
|
|
197
|
+
# developer.update!(company_id: 2)
|
|
198
|
+
# # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
|
348
199
|
#
|
|
349
|
-
#
|
|
350
|
-
#
|
|
351
|
-
|
|
352
|
-
|
|
200
|
+
# developer.name = "Bob"
|
|
201
|
+
# developer.save!
|
|
202
|
+
# # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
|
203
|
+
#
|
|
204
|
+
# developer.destroy!
|
|
205
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
|
206
|
+
#
|
|
207
|
+
# developer.delete
|
|
208
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
|
209
|
+
#
|
|
210
|
+
# developer.reload
|
|
211
|
+
# # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
|
|
212
|
+
def query_constraints(*columns_list)
|
|
213
|
+
raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
|
|
214
|
+
|
|
215
|
+
@query_constraints_list = columns_list.map(&:to_s)
|
|
216
|
+
@has_query_constraints = @query_constraints_list
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def has_query_constraints? # :nodoc:
|
|
220
|
+
@has_query_constraints
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def query_constraints_list # :nodoc:
|
|
224
|
+
@query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
|
|
225
|
+
primary_key if primary_key.is_a?(Array)
|
|
226
|
+
else
|
|
227
|
+
base_class.query_constraints_list
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Returns an array of column names to be used in queries. The source of column
|
|
232
|
+
# names is derived from +query_constraints_list+ or +primary_key+. This method
|
|
233
|
+
# is for internal use when the primary key is to be treated as an array.
|
|
234
|
+
def composite_query_constraints_list # :nodoc:
|
|
235
|
+
@composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
|
|
353
236
|
end
|
|
354
237
|
|
|
355
|
-
def _insert_record(values) # :nodoc:
|
|
238
|
+
def _insert_record(connection, values, returning) # :nodoc:
|
|
356
239
|
primary_key = self.primary_key
|
|
357
240
|
primary_key_value = nil
|
|
358
241
|
|
|
359
|
-
if
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
|
242
|
+
if prefetch_primary_key? && primary_key
|
|
243
|
+
values[primary_key] ||= begin
|
|
363
244
|
primary_key_value = next_sequence_value
|
|
364
|
-
|
|
245
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
|
365
246
|
end
|
|
366
247
|
end
|
|
367
248
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
249
|
+
im = Arel::InsertManager.new(arel_table)
|
|
250
|
+
|
|
251
|
+
with_connection do |c|
|
|
252
|
+
if values.empty?
|
|
253
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
|
254
|
+
else
|
|
255
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
|
256
|
+
end
|
|
374
257
|
|
|
375
|
-
|
|
258
|
+
connection.insert(
|
|
259
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
|
260
|
+
returning: returning
|
|
261
|
+
)
|
|
262
|
+
end
|
|
376
263
|
end
|
|
377
264
|
|
|
378
265
|
def _update_record(values, constraints) # :nodoc:
|
|
379
|
-
constraints =
|
|
266
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
|
267
|
+
|
|
268
|
+
default_constraint = build_default_constraint
|
|
269
|
+
constraints << default_constraint if default_constraint
|
|
380
270
|
|
|
381
|
-
|
|
382
|
-
constraints.
|
|
383
|
-
|
|
271
|
+
if current_scope = self.global_current_scope
|
|
272
|
+
constraints << current_scope.where_clause.ast
|
|
273
|
+
end
|
|
384
274
|
|
|
385
|
-
|
|
275
|
+
um = Arel::UpdateManager.new(arel_table)
|
|
276
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
|
277
|
+
um.wheres = constraints
|
|
278
|
+
|
|
279
|
+
with_connection do |c|
|
|
280
|
+
c.update(um, "#{self} Update")
|
|
281
|
+
end
|
|
386
282
|
end
|
|
387
283
|
|
|
388
284
|
def _delete_record(constraints) # :nodoc:
|
|
389
|
-
constraints =
|
|
285
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
|
390
286
|
|
|
391
|
-
|
|
392
|
-
|
|
287
|
+
default_constraint = build_default_constraint
|
|
288
|
+
constraints << default_constraint if default_constraint
|
|
289
|
+
|
|
290
|
+
if current_scope = self.global_current_scope
|
|
291
|
+
constraints << current_scope.where_clause.ast
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
dm = Arel::DeleteManager.new(arel_table)
|
|
393
295
|
dm.wheres = constraints
|
|
394
296
|
|
|
395
|
-
|
|
297
|
+
with_connection do |c|
|
|
298
|
+
c.delete(dm, "#{self} Destroy")
|
|
299
|
+
end
|
|
396
300
|
end
|
|
397
301
|
|
|
398
302
|
private
|
|
303
|
+
def inherited(subclass)
|
|
304
|
+
super
|
|
305
|
+
subclass.class_eval do
|
|
306
|
+
@_query_constraints_list = nil
|
|
307
|
+
@has_query_constraints = false
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
399
311
|
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
|
400
312
|
# new instance of the class. Accepts only keys as strings.
|
|
401
313
|
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
|
@@ -412,38 +324,49 @@ module ActiveRecord
|
|
|
412
324
|
self
|
|
413
325
|
end
|
|
414
326
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
327
|
+
# Called by +_update_record+ and +_delete_record+
|
|
328
|
+
# to build `where` clause from default scopes.
|
|
329
|
+
# Skips empty scopes.
|
|
330
|
+
def build_default_constraint
|
|
331
|
+
return unless default_scopes?(all_queries: true)
|
|
332
|
+
|
|
333
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
|
334
|
+
default_where_clause.ast unless default_where_clause.empty?
|
|
421
335
|
end
|
|
422
336
|
end
|
|
423
337
|
|
|
424
338
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
|
425
339
|
# for the object doesn't exist in the database yet; otherwise, returns false.
|
|
426
340
|
def new_record?
|
|
427
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
|
428
341
|
@new_record
|
|
429
342
|
end
|
|
430
343
|
|
|
344
|
+
# Returns true if this object was just created -- that is, prior to the last
|
|
345
|
+
# update or delete, the object didn't exist in the database and new_record? would have
|
|
346
|
+
# returned true.
|
|
347
|
+
def previously_new_record?
|
|
348
|
+
@previously_new_record
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
|
352
|
+
def previously_persisted?
|
|
353
|
+
!new_record? && destroyed?
|
|
354
|
+
end
|
|
355
|
+
|
|
431
356
|
# Returns true if this object has been destroyed, otherwise returns false.
|
|
432
357
|
def destroyed?
|
|
433
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
|
434
358
|
@destroyed
|
|
435
359
|
end
|
|
436
360
|
|
|
437
361
|
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
|
438
362
|
# not destroyed, otherwise returns false.
|
|
439
363
|
def persisted?
|
|
440
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
|
441
364
|
!(@new_record || @destroyed)
|
|
442
365
|
end
|
|
443
366
|
|
|
444
367
|
##
|
|
445
368
|
# :call-seq:
|
|
446
|
-
# save(
|
|
369
|
+
# save(**options)
|
|
447
370
|
#
|
|
448
371
|
# Saves the model.
|
|
449
372
|
#
|
|
@@ -466,15 +389,15 @@ module ActiveRecord
|
|
|
466
389
|
#
|
|
467
390
|
# Attributes marked as readonly are silently ignored if the record is
|
|
468
391
|
# being updated.
|
|
469
|
-
def save(
|
|
470
|
-
create_or_update(
|
|
392
|
+
def save(**options, &block)
|
|
393
|
+
create_or_update(**options, &block)
|
|
471
394
|
rescue ActiveRecord::RecordInvalid
|
|
472
395
|
false
|
|
473
396
|
end
|
|
474
397
|
|
|
475
398
|
##
|
|
476
399
|
# :call-seq:
|
|
477
|
-
# save!(
|
|
400
|
+
# save!(**options)
|
|
478
401
|
#
|
|
479
402
|
# Saves the model.
|
|
480
403
|
#
|
|
@@ -499,8 +422,8 @@ module ActiveRecord
|
|
|
499
422
|
# being updated.
|
|
500
423
|
#
|
|
501
424
|
# Unless an error is raised, returns true.
|
|
502
|
-
def save!(
|
|
503
|
-
create_or_update(
|
|
425
|
+
def save!(**options, &block)
|
|
426
|
+
create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
|
|
504
427
|
end
|
|
505
428
|
|
|
506
429
|
# Deletes the record in the database and freezes this instance to
|
|
@@ -514,10 +437,11 @@ module ActiveRecord
|
|
|
514
437
|
#
|
|
515
438
|
# To enforce the object's +before_destroy+ and +after_destroy+
|
|
516
439
|
# callbacks or any <tt>:dependent</tt> association
|
|
517
|
-
# options, use
|
|
440
|
+
# options, use #destroy.
|
|
518
441
|
def delete
|
|
519
442
|
_delete_row if persisted?
|
|
520
443
|
@destroyed = true
|
|
444
|
+
@previously_new_record = false
|
|
521
445
|
freeze
|
|
522
446
|
end
|
|
523
447
|
|
|
@@ -531,12 +455,9 @@ module ActiveRecord
|
|
|
531
455
|
def destroy
|
|
532
456
|
_raise_readonly_record_error if readonly?
|
|
533
457
|
destroy_associations
|
|
534
|
-
@_trigger_destroy_callback
|
|
535
|
-
destroy_row > 0
|
|
536
|
-
else
|
|
537
|
-
true
|
|
538
|
-
end
|
|
458
|
+
@_trigger_destroy_callback ||= persisted? && destroy_row > 0
|
|
539
459
|
@destroyed = true
|
|
460
|
+
@previously_new_record = false
|
|
540
461
|
freeze
|
|
541
462
|
end
|
|
542
463
|
|
|
@@ -552,33 +473,39 @@ module ActiveRecord
|
|
|
552
473
|
end
|
|
553
474
|
|
|
554
475
|
# Returns an instance of the specified +klass+ with the attributes of the
|
|
555
|
-
# current record. This is mostly useful in relation to single
|
|
556
|
-
# inheritance structures where you want a subclass to appear as the
|
|
476
|
+
# current record. This is mostly useful in relation to single table
|
|
477
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
|
557
478
|
# superclass. This can be used along with record identification in
|
|
558
479
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
|
559
480
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
|
560
481
|
# instance using the companies/company partial instead of clients/client.
|
|
561
482
|
#
|
|
562
483
|
# Note: The new instance will share a link to the same attributes as the original class.
|
|
563
|
-
# Therefore the
|
|
484
|
+
# Therefore the STI column value will still be the same.
|
|
564
485
|
# Any change to the attributes on either instance will affect both instances.
|
|
565
|
-
#
|
|
486
|
+
# This includes any attribute initialization done by the new instance.
|
|
487
|
+
#
|
|
488
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
|
566
489
|
def becomes(klass)
|
|
567
490
|
became = klass.allocate
|
|
568
|
-
|
|
569
|
-
became.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
491
|
+
|
|
492
|
+
became.send(:initialize) do |becoming|
|
|
493
|
+
@attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
|
|
494
|
+
becoming.instance_variable_set(:@attributes, @attributes)
|
|
495
|
+
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
|
496
|
+
becoming.instance_variable_set(:@new_record, new_record?)
|
|
497
|
+
becoming.instance_variable_set(:@destroyed, destroyed?)
|
|
498
|
+
becoming.errors.copy!(errors)
|
|
499
|
+
end
|
|
500
|
+
|
|
574
501
|
became
|
|
575
502
|
end
|
|
576
503
|
|
|
577
|
-
# Wrapper around #becomes that also changes the instance's
|
|
504
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
|
578
505
|
# This is especially useful if you want to persist the changed class in your
|
|
579
506
|
# database.
|
|
580
507
|
#
|
|
581
|
-
# Note: The old instance's
|
|
508
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
|
582
509
|
# share the same set of attributes.
|
|
583
510
|
def becomes!(klass)
|
|
584
511
|
became = becomes(klass)
|
|
@@ -598,7 +525,7 @@ module ActiveRecord
|
|
|
598
525
|
# * updated_at/updated_on column is updated if that column is available.
|
|
599
526
|
# * Updates all the attributes that are dirty in this object.
|
|
600
527
|
#
|
|
601
|
-
# This method raises an ActiveRecord::ActiveRecordError
|
|
528
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
|
602
529
|
# attribute is marked as readonly.
|
|
603
530
|
#
|
|
604
531
|
# Also see #update_column.
|
|
@@ -610,6 +537,28 @@ module ActiveRecord
|
|
|
610
537
|
save(validate: false)
|
|
611
538
|
end
|
|
612
539
|
|
|
540
|
+
# Updates a single attribute and saves the record.
|
|
541
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
|
542
|
+
#
|
|
543
|
+
# * Validation is skipped.
|
|
544
|
+
# * \Callbacks are invoked.
|
|
545
|
+
# * updated_at/updated_on column is updated if that column is available.
|
|
546
|
+
# * Updates all the attributes that are dirty in this object.
|
|
547
|
+
#
|
|
548
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
|
549
|
+
# attribute is marked as readonly.
|
|
550
|
+
#
|
|
551
|
+
# If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
|
552
|
+
# and #update_attribute! raises ActiveRecord::RecordNotSaved. See
|
|
553
|
+
# ActiveRecord::Callbacks for further details.
|
|
554
|
+
def update_attribute!(name, value)
|
|
555
|
+
name = name.to_s
|
|
556
|
+
verify_readonly_attribute(name)
|
|
557
|
+
public_send("#{name}=", value)
|
|
558
|
+
|
|
559
|
+
save!(validate: false)
|
|
560
|
+
end
|
|
561
|
+
|
|
613
562
|
# Updates the attributes of the model from the passed-in hash and saves the
|
|
614
563
|
# record, all wrapped in a transaction. If the object is invalid, the saving
|
|
615
564
|
# will fail and false will be returned.
|
|
@@ -622,9 +571,6 @@ module ActiveRecord
|
|
|
622
571
|
end
|
|
623
572
|
end
|
|
624
573
|
|
|
625
|
-
alias update_attributes update
|
|
626
|
-
deprecate update_attributes: "please, use update instead"
|
|
627
|
-
|
|
628
574
|
# Updates its receiver just like #update but calls #save! instead
|
|
629
575
|
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
|
630
576
|
def update!(attributes)
|
|
@@ -636,9 +582,6 @@ module ActiveRecord
|
|
|
636
582
|
end
|
|
637
583
|
end
|
|
638
584
|
|
|
639
|
-
alias update_attributes! update!
|
|
640
|
-
deprecate update_attributes!: "please, use update! instead"
|
|
641
|
-
|
|
642
585
|
# Equivalent to <code>update_columns(name => value)</code>.
|
|
643
586
|
def update_column(name, value)
|
|
644
587
|
update_columns(name => value)
|
|
@@ -663,24 +606,23 @@ module ActiveRecord
|
|
|
663
606
|
def update_columns(attributes)
|
|
664
607
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
|
665
608
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
|
609
|
+
_raise_readonly_record_error if readonly?
|
|
666
610
|
|
|
667
611
|
attributes = attributes.transform_keys do |key|
|
|
668
612
|
name = key.to_s
|
|
669
|
-
self.class.attribute_aliases[name] || name
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
attributes.each_key do |key|
|
|
673
|
-
verify_readonly_attribute(key)
|
|
613
|
+
name = self.class.attribute_aliases[name] || name
|
|
614
|
+
verify_readonly_attribute(name) || name
|
|
674
615
|
end
|
|
675
616
|
|
|
676
|
-
|
|
677
|
-
attributes.
|
|
678
|
-
|
|
617
|
+
update_constraints = _query_constraints_hash
|
|
618
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
|
619
|
+
h[k] = @attributes.write_cast_value(k, v)
|
|
620
|
+
clear_attribute_change(k)
|
|
679
621
|
end
|
|
680
622
|
|
|
681
623
|
affected_rows = self.class._update_record(
|
|
682
624
|
attributes,
|
|
683
|
-
|
|
625
|
+
update_constraints
|
|
684
626
|
)
|
|
685
627
|
|
|
686
628
|
affected_rows == 1
|
|
@@ -703,9 +645,9 @@ module ActiveRecord
|
|
|
703
645
|
# Returns +self+.
|
|
704
646
|
def increment!(attribute, by = 1, touch: nil)
|
|
705
647
|
increment(attribute, by)
|
|
706
|
-
change = public_send(attribute) - (
|
|
648
|
+
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
|
|
707
649
|
self.class.update_counters(id, attribute => change, touch: touch)
|
|
708
|
-
|
|
650
|
+
public_send(:"clear_#{attribute}_change")
|
|
709
651
|
self
|
|
710
652
|
end
|
|
711
653
|
|
|
@@ -800,17 +742,19 @@ module ActiveRecord
|
|
|
800
742
|
# end
|
|
801
743
|
#
|
|
802
744
|
def reload(options = nil)
|
|
803
|
-
self.class.
|
|
745
|
+
self.class.connection_pool.clear_query_cache
|
|
804
746
|
|
|
805
|
-
fresh_object =
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
end
|
|
747
|
+
fresh_object = if apply_scoping?(options)
|
|
748
|
+
_find_record((options || {}).merge(all_queries: true))
|
|
749
|
+
else
|
|
750
|
+
self.class.unscoped { _find_record(options) }
|
|
751
|
+
end
|
|
811
752
|
|
|
812
|
-
@
|
|
753
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
|
754
|
+
@association_cache.each_value { |association| association.owner = self }
|
|
755
|
+
@attributes = fresh_object.instance_variable_get(:@attributes)
|
|
813
756
|
@new_record = false
|
|
757
|
+
@previously_new_record = false
|
|
814
758
|
self
|
|
815
759
|
end
|
|
816
760
|
|
|
@@ -849,17 +793,16 @@ module ActiveRecord
|
|
|
849
793
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
|
850
794
|
#
|
|
851
795
|
def touch(*names, time: nil)
|
|
852
|
-
unless persisted?
|
|
853
|
-
|
|
854
|
-
cannot touch on a new or destroyed record object. Consider using
|
|
855
|
-
persisted?, new_record?, or destroyed? before touching
|
|
856
|
-
MSG
|
|
857
|
-
end
|
|
796
|
+
_raise_record_not_touched_error unless persisted?
|
|
797
|
+
_raise_readonly_record_error if readonly?
|
|
858
798
|
|
|
859
799
|
attribute_names = timestamp_attributes_for_update_in_model
|
|
860
|
-
attribute_names
|
|
861
|
-
|
|
862
|
-
|
|
800
|
+
attribute_names = (attribute_names | names).map! do |name|
|
|
801
|
+
name = name.to_s
|
|
802
|
+
name = self.class.attribute_aliases[name] || name
|
|
803
|
+
verify_readonly_attribute(name)
|
|
804
|
+
name
|
|
805
|
+
end
|
|
863
806
|
|
|
864
807
|
unless attribute_names.empty?
|
|
865
808
|
affected_rows = _touch_row(attribute_names, time)
|
|
@@ -870,6 +813,53 @@ module ActiveRecord
|
|
|
870
813
|
end
|
|
871
814
|
|
|
872
815
|
private
|
|
816
|
+
def init_internals
|
|
817
|
+
super
|
|
818
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
|
819
|
+
@previously_new_record = false
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def strict_loaded_associations
|
|
823
|
+
@association_cache.find_all do |_, assoc|
|
|
824
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
|
825
|
+
end.map(&:first)
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
def _find_record(options)
|
|
829
|
+
all_queries = options ? options[:all_queries] : nil
|
|
830
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
|
831
|
+
|
|
832
|
+
if options && options[:lock]
|
|
833
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
|
834
|
+
else
|
|
835
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
def _in_memory_query_constraints_hash
|
|
840
|
+
if self.class.query_constraints_list.nil?
|
|
841
|
+
{ @primary_key => id }
|
|
842
|
+
else
|
|
843
|
+
self.class.query_constraints_list.index_with do |column_name|
|
|
844
|
+
attribute(column_name)
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
def apply_scoping?(options)
|
|
850
|
+
!(options && options[:unscoped]) &&
|
|
851
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def _query_constraints_hash
|
|
855
|
+
if self.class.query_constraints_list.nil?
|
|
856
|
+
{ @primary_key => id_in_database }
|
|
857
|
+
else
|
|
858
|
+
self.class.query_constraints_list.index_with do |column_name|
|
|
859
|
+
attribute_in_database(column_name)
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
end
|
|
873
863
|
|
|
874
864
|
# A hook to be overridden by association modules.
|
|
875
865
|
def destroy_associations
|
|
@@ -880,7 +870,7 @@ module ActiveRecord
|
|
|
880
870
|
end
|
|
881
871
|
|
|
882
872
|
def _delete_row
|
|
883
|
-
self.class._delete_record(
|
|
873
|
+
self.class._delete_record(_query_constraints_hash)
|
|
884
874
|
end
|
|
885
875
|
|
|
886
876
|
def _touch_row(attribute_names, time)
|
|
@@ -896,7 +886,7 @@ module ActiveRecord
|
|
|
896
886
|
def _update_row(attribute_names, attempted_action = "update")
|
|
897
887
|
self.class._update_record(
|
|
898
888
|
attributes_with_values(attribute_names),
|
|
899
|
-
|
|
889
|
+
_query_constraints_hash
|
|
900
890
|
)
|
|
901
891
|
end
|
|
902
892
|
|
|
@@ -920,6 +910,8 @@ module ActiveRecord
|
|
|
920
910
|
@_trigger_update_callback = affected_rows == 1
|
|
921
911
|
end
|
|
922
912
|
|
|
913
|
+
@previously_new_record = false
|
|
914
|
+
|
|
923
915
|
yield(self) if block_given?
|
|
924
916
|
|
|
925
917
|
affected_rows
|
|
@@ -930,13 +922,22 @@ module ActiveRecord
|
|
|
930
922
|
def _create_record(attribute_names = self.attribute_names)
|
|
931
923
|
attribute_names = attributes_for_create(attribute_names)
|
|
932
924
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
)
|
|
925
|
+
self.class.with_connection do |connection|
|
|
926
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
|
936
927
|
|
|
937
|
-
|
|
928
|
+
returning_values = self.class._insert_record(
|
|
929
|
+
connection,
|
|
930
|
+
attributes_with_values(attribute_names),
|
|
931
|
+
returning_columns
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
returning_columns.zip(returning_values).each do |column, value|
|
|
935
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
|
936
|
+
end if returning_values
|
|
937
|
+
end
|
|
938
938
|
|
|
939
939
|
@new_record = false
|
|
940
|
+
@previously_new_record = true
|
|
940
941
|
|
|
941
942
|
yield(self) if block_given?
|
|
942
943
|
|
|
@@ -944,24 +945,26 @@ module ActiveRecord
|
|
|
944
945
|
end
|
|
945
946
|
|
|
946
947
|
def verify_readonly_attribute(name)
|
|
947
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.
|
|
948
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
|
948
949
|
end
|
|
949
950
|
|
|
950
951
|
def _raise_record_not_destroyed
|
|
951
952
|
@_association_destroy_exception ||= nil
|
|
952
|
-
|
|
953
|
+
key = self.class.primary_key
|
|
954
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
|
953
955
|
ensure
|
|
954
956
|
@_association_destroy_exception = nil
|
|
955
957
|
end
|
|
956
958
|
|
|
957
|
-
# The name of the method used to touch a +belongs_to+ association when the
|
|
958
|
-
# +:touch+ option is used.
|
|
959
|
-
def belongs_to_touch_method
|
|
960
|
-
:touch
|
|
961
|
-
end
|
|
962
|
-
|
|
963
959
|
def _raise_readonly_record_error
|
|
964
960
|
raise ReadOnlyRecord, "#{self.class} is marked as readonly"
|
|
965
961
|
end
|
|
962
|
+
|
|
963
|
+
def _raise_record_not_touched_error
|
|
964
|
+
raise ActiveRecordError, <<~MSG.squish
|
|
965
|
+
Cannot touch on a new or destroyed record object. Consider using
|
|
966
|
+
persisted?, new_record?, or destroyed? before touching.
|
|
967
|
+
MSG
|
|
968
|
+
end
|
|
966
969
|
end
|
|
967
970
|
end
|