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
|
@@ -3,20 +3,70 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
# = Active Record \Relation
|
|
5
5
|
class Relation
|
|
6
|
+
class ExplainProxy # :nodoc:
|
|
7
|
+
def initialize(relation, options)
|
|
8
|
+
@relation = relation
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def inspect
|
|
13
|
+
exec_explain { @relation.send(:exec_queries) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def average(column_name)
|
|
17
|
+
exec_explain { @relation.average(column_name) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def count(column_name = nil)
|
|
21
|
+
exec_explain { @relation.count(column_name) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def first(limit = nil)
|
|
25
|
+
exec_explain { @relation.first(limit) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def last(limit = nil)
|
|
29
|
+
exec_explain { @relation.last(limit) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def maximum(column_name)
|
|
33
|
+
exec_explain { @relation.maximum(column_name) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def minimum(column_name)
|
|
37
|
+
exec_explain { @relation.minimum(column_name) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def pluck(*column_names)
|
|
41
|
+
exec_explain { @relation.pluck(*column_names) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def sum(identity_or_column = nil)
|
|
45
|
+
exec_explain { @relation.sum(identity_or_column) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def exec_explain(&block)
|
|
50
|
+
@relation.exec_explain(@relation.collecting_queries_for_explain { block.call }, @options)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
6
54
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
|
7
55
|
:order, :joins, :left_outer_joins, :references,
|
|
8
|
-
:extending, :unscope, :optimizer_hints, :annotate
|
|
56
|
+
:extending, :unscope, :optimizer_hints, :annotate,
|
|
57
|
+
:with]
|
|
9
58
|
|
|
10
|
-
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
|
59
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
|
|
11
60
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
|
12
61
|
|
|
13
62
|
CLAUSE_METHODS = [:where, :having, :from]
|
|
14
|
-
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :
|
|
63
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
|
|
15
64
|
|
|
16
65
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
|
17
66
|
|
|
18
67
|
include Enumerable
|
|
19
68
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
|
69
|
+
include SignedId::RelationMethods, TokenFor::RelationMethods
|
|
20
70
|
|
|
21
71
|
attr_reader :table, :klass, :loaded, :predicate_builder
|
|
22
72
|
attr_accessor :skip_preloading_value
|
|
@@ -28,10 +78,13 @@ module ActiveRecord
|
|
|
28
78
|
@klass = klass
|
|
29
79
|
@table = table
|
|
30
80
|
@values = values
|
|
31
|
-
@offsets = {}
|
|
32
81
|
@loaded = false
|
|
33
82
|
@predicate_builder = predicate_builder
|
|
34
83
|
@delegate_to_klass = false
|
|
84
|
+
@future_result = nil
|
|
85
|
+
@records = nil
|
|
86
|
+
@async = false
|
|
87
|
+
@none = false
|
|
35
88
|
end
|
|
36
89
|
|
|
37
90
|
def initialize_copy(other)
|
|
@@ -39,17 +92,13 @@ module ActiveRecord
|
|
|
39
92
|
reset
|
|
40
93
|
end
|
|
41
94
|
|
|
42
|
-
def arel_attribute(name) # :nodoc:
|
|
43
|
-
klass.arel_attribute(name, table)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
95
|
def bind_attribute(name, value) # :nodoc:
|
|
47
96
|
if reflection = klass._reflect_on_association(name)
|
|
48
97
|
name = reflection.foreign_key
|
|
49
|
-
value = value.read_attribute(reflection.
|
|
98
|
+
value = value.read_attribute(reflection.association_primary_key) unless value.nil?
|
|
50
99
|
end
|
|
51
100
|
|
|
52
|
-
attr =
|
|
101
|
+
attr = table[name]
|
|
53
102
|
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
|
54
103
|
yield attr, bind
|
|
55
104
|
end
|
|
@@ -67,10 +116,13 @@ module ActiveRecord
|
|
|
67
116
|
# user = users.new { |user| user.name = 'Oscar' }
|
|
68
117
|
# user.name # => Oscar
|
|
69
118
|
def new(attributes = nil, &block)
|
|
70
|
-
|
|
71
|
-
|
|
119
|
+
if attributes.is_a?(Array)
|
|
120
|
+
attributes.collect { |attr| new(attr, &block) }
|
|
121
|
+
else
|
|
122
|
+
block = current_scope_restoring_block(&block)
|
|
123
|
+
scoping { _new(attributes, &block) }
|
|
124
|
+
end
|
|
72
125
|
end
|
|
73
|
-
|
|
74
126
|
alias build new
|
|
75
127
|
|
|
76
128
|
# Tries to create a new record with the same scoped attributes
|
|
@@ -96,8 +148,8 @@ module ActiveRecord
|
|
|
96
148
|
if attributes.is_a?(Array)
|
|
97
149
|
attributes.collect { |attr| create(attr, &block) }
|
|
98
150
|
else
|
|
99
|
-
block =
|
|
100
|
-
scoping {
|
|
151
|
+
block = current_scope_restoring_block(&block)
|
|
152
|
+
scoping { _create(attributes, &block) }
|
|
101
153
|
end
|
|
102
154
|
end
|
|
103
155
|
|
|
@@ -111,8 +163,8 @@ module ActiveRecord
|
|
|
111
163
|
if attributes.is_a?(Array)
|
|
112
164
|
attributes.collect { |attr| create!(attr, &block) }
|
|
113
165
|
else
|
|
114
|
-
block =
|
|
115
|
-
scoping {
|
|
166
|
+
block = current_scope_restoring_block(&block)
|
|
167
|
+
scoping { _create!(attributes, &block) }
|
|
116
168
|
end
|
|
117
169
|
end
|
|
118
170
|
|
|
@@ -149,7 +201,7 @@ module ActiveRecord
|
|
|
149
201
|
# above can be alternatively written this way:
|
|
150
202
|
#
|
|
151
203
|
# # Find the first user named "Scarlett" or create a new one with a
|
|
152
|
-
# #
|
|
204
|
+
# # particular last name.
|
|
153
205
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
|
154
206
|
# user.last_name = 'Johansson'
|
|
155
207
|
# end
|
|
@@ -159,38 +211,41 @@ module ActiveRecord
|
|
|
159
211
|
# failed due to validation errors it won't be persisted, you get what
|
|
160
212
|
# #create returns in such situation.
|
|
161
213
|
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
214
|
+
# If creation failed because of a unique constraint, this method will
|
|
215
|
+
# assume it encountered a race condition and will try finding the record
|
|
216
|
+
# once more. If somehow the second find still does not find a record
|
|
217
|
+
# because a concurrent DELETE happened, it will then raise an
|
|
218
|
+
# ActiveRecord::RecordNotFound exception.
|
|
166
219
|
#
|
|
167
|
-
#
|
|
220
|
+
# Please note <b>this method is not atomic</b>, it runs first a SELECT,
|
|
221
|
+
# and if there are no results an INSERT is attempted. So if the table
|
|
222
|
+
# doesn't have a relevant unique constraint it could be the case that
|
|
223
|
+
# you end up with two or more similar records.
|
|
168
224
|
def find_or_create_by(attributes, &block)
|
|
169
|
-
find_by(attributes) ||
|
|
225
|
+
find_by(attributes) || create_or_find_by(attributes, &block)
|
|
170
226
|
end
|
|
171
227
|
|
|
172
228
|
# Like #find_or_create_by, but calls
|
|
173
229
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
|
174
230
|
# is raised if the created record is invalid.
|
|
175
231
|
def find_or_create_by!(attributes, &block)
|
|
176
|
-
find_by(attributes) ||
|
|
232
|
+
find_by(attributes) || create_or_find_by!(attributes, &block)
|
|
177
233
|
end
|
|
178
234
|
|
|
179
|
-
# Attempts to create a record with the given attributes in a table that has a unique constraint
|
|
235
|
+
# Attempts to create a record with the given attributes in a table that has a unique database constraint
|
|
180
236
|
# on one or several of its columns. If a row already exists with one or several of these
|
|
181
237
|
# unique constraints, the exception such an insertion would normally raise is caught,
|
|
182
238
|
# and the existing record with those attributes is found using #find_by!.
|
|
183
239
|
#
|
|
184
|
-
# This is similar to #find_or_create_by, but
|
|
185
|
-
#
|
|
186
|
-
# if none is found.
|
|
240
|
+
# This is similar to #find_or_create_by, but tries to create the record first. As such it is
|
|
241
|
+
# better suited for cases where the record is most likely not to exist yet.
|
|
187
242
|
#
|
|
188
243
|
# There are several drawbacks to #create_or_find_by, though:
|
|
189
244
|
#
|
|
190
|
-
# * The underlying table must have the relevant columns defined with unique constraints.
|
|
245
|
+
# * The underlying table must have the relevant columns defined with unique database constraints.
|
|
191
246
|
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
|
192
247
|
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
|
193
|
-
# matching record, which will then raise an
|
|
248
|
+
# matching record, which will then raise an ActiveRecord::RecordNotFound exception,
|
|
194
249
|
# rather than a record with the given attributes.
|
|
195
250
|
# * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
|
|
196
251
|
# we actually have another race condition between INSERT -> SELECT, which can be triggered
|
|
@@ -199,29 +254,53 @@ module ActiveRecord
|
|
|
199
254
|
# * It relies on exception handling to handle control flow, which may be marginally slower.
|
|
200
255
|
# * The primary key may auto-increment on each create, even if it fails. This can accelerate
|
|
201
256
|
# the problem of running out of integers, if the underlying table is still stuck on a primary
|
|
202
|
-
# key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
|
|
257
|
+
# key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
|
|
203
258
|
# to this problem).
|
|
259
|
+
# * Columns with unique database constraints should not have uniqueness validations defined,
|
|
260
|
+
# otherwise #create will fail due to validation errors and #find_by will never be called.
|
|
204
261
|
#
|
|
205
262
|
# This method will return a record if all given attributes are covered by unique constraints
|
|
206
263
|
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
|
207
264
|
# and failed due to validation errors it won't be persisted, you get what #create returns in
|
|
208
265
|
# such situation.
|
|
209
266
|
def create_or_find_by(attributes, &block)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
267
|
+
with_connection do |connection|
|
|
268
|
+
record = nil
|
|
269
|
+
transaction(requires_new: true) do
|
|
270
|
+
record = create(attributes, &block)
|
|
271
|
+
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
272
|
+
end
|
|
273
|
+
record
|
|
274
|
+
rescue ActiveRecord::RecordNotUnique
|
|
275
|
+
if connection.transaction_open?
|
|
276
|
+
where(attributes).lock.find_by!(attributes)
|
|
277
|
+
else
|
|
278
|
+
find_by!(attributes)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
213
281
|
end
|
|
214
282
|
|
|
215
283
|
# Like #create_or_find_by, but calls
|
|
216
284
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
|
217
285
|
# is raised if the created record is invalid.
|
|
218
286
|
def create_or_find_by!(attributes, &block)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
287
|
+
with_connection do |connection|
|
|
288
|
+
record = nil
|
|
289
|
+
transaction(requires_new: true) do
|
|
290
|
+
record = create!(attributes, &block)
|
|
291
|
+
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
292
|
+
end
|
|
293
|
+
record
|
|
294
|
+
rescue ActiveRecord::RecordNotUnique
|
|
295
|
+
if connection.transaction_open?
|
|
296
|
+
where(attributes).lock.find_by!(attributes)
|
|
297
|
+
else
|
|
298
|
+
find_by!(attributes)
|
|
299
|
+
end
|
|
300
|
+
end
|
|
222
301
|
end
|
|
223
302
|
|
|
224
|
-
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core
|
|
303
|
+
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
|
|
225
304
|
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
|
226
305
|
def find_or_initialize_by(attributes, &block)
|
|
227
306
|
find_by(attributes) || new(attributes, &block)
|
|
@@ -231,13 +310,30 @@ module ActiveRecord
|
|
|
231
310
|
# returns the result as a string. The string is formatted imitating the
|
|
232
311
|
# ones printed by the database shell.
|
|
233
312
|
#
|
|
313
|
+
# User.all.explain
|
|
314
|
+
# # EXPLAIN SELECT `users`.* FROM `users`
|
|
315
|
+
# # ...
|
|
316
|
+
#
|
|
234
317
|
# Note that this method actually runs the queries, since the results of some
|
|
235
318
|
# are needed by the next ones when eager loading is going on.
|
|
236
319
|
#
|
|
320
|
+
# To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
|
|
321
|
+
# these methods on +explain+:
|
|
322
|
+
#
|
|
323
|
+
# User.all.explain.count
|
|
324
|
+
# # EXPLAIN SELECT COUNT(*) FROM `users`
|
|
325
|
+
# # ...
|
|
326
|
+
#
|
|
327
|
+
# The column name can be passed if required:
|
|
328
|
+
#
|
|
329
|
+
# User.all.explain.maximum(:id)
|
|
330
|
+
# # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
|
|
331
|
+
# # ...
|
|
332
|
+
#
|
|
237
333
|
# Please see further details in the
|
|
238
334
|
# {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
|
239
|
-
def explain
|
|
240
|
-
|
|
335
|
+
def explain(*options)
|
|
336
|
+
ExplainProxy.new(self, options)
|
|
241
337
|
end
|
|
242
338
|
|
|
243
339
|
# Converts relation objects to Array.
|
|
@@ -258,37 +354,71 @@ module ActiveRecord
|
|
|
258
354
|
|
|
259
355
|
# Returns size of the records.
|
|
260
356
|
def size
|
|
261
|
-
loaded?
|
|
357
|
+
if loaded?
|
|
358
|
+
records.length
|
|
359
|
+
else
|
|
360
|
+
count(:all)
|
|
361
|
+
end
|
|
262
362
|
end
|
|
263
363
|
|
|
264
364
|
# Returns true if there are no records.
|
|
265
365
|
def empty?
|
|
266
|
-
return
|
|
267
|
-
|
|
366
|
+
return true if @none
|
|
367
|
+
|
|
368
|
+
if loaded?
|
|
369
|
+
records.empty?
|
|
370
|
+
else
|
|
371
|
+
!exists?
|
|
372
|
+
end
|
|
268
373
|
end
|
|
269
374
|
|
|
270
375
|
# Returns true if there are no records.
|
|
271
|
-
|
|
272
|
-
|
|
376
|
+
#
|
|
377
|
+
# When a pattern argument is given, this method checks whether elements in
|
|
378
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
|
379
|
+
#
|
|
380
|
+
# posts.none?(Comment) # => true or false
|
|
381
|
+
def none?(*args)
|
|
382
|
+
return true if @none
|
|
383
|
+
|
|
384
|
+
return super if args.present? || block_given?
|
|
273
385
|
empty?
|
|
274
386
|
end
|
|
275
387
|
|
|
276
388
|
# Returns true if there are any records.
|
|
277
|
-
|
|
278
|
-
|
|
389
|
+
#
|
|
390
|
+
# When a pattern argument is given, this method checks whether elements in
|
|
391
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
|
392
|
+
#
|
|
393
|
+
# posts.any?(Post) # => true or false
|
|
394
|
+
def any?(*args)
|
|
395
|
+
return false if @none
|
|
396
|
+
|
|
397
|
+
return super if args.present? || block_given?
|
|
279
398
|
!empty?
|
|
280
399
|
end
|
|
281
400
|
|
|
282
401
|
# Returns true if there is exactly one record.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
402
|
+
#
|
|
403
|
+
# When a pattern argument is given, this method checks whether elements in
|
|
404
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
|
405
|
+
#
|
|
406
|
+
# posts.one?(Post) # => true or false
|
|
407
|
+
def one?(*args)
|
|
408
|
+
return false if @none
|
|
409
|
+
|
|
410
|
+
return super if args.present? || block_given?
|
|
411
|
+
return records.one? if loaded?
|
|
412
|
+
limited_count == 1
|
|
286
413
|
end
|
|
287
414
|
|
|
288
415
|
# Returns true if there is more than one record.
|
|
289
416
|
def many?
|
|
417
|
+
return false if @none
|
|
418
|
+
|
|
290
419
|
return super if block_given?
|
|
291
|
-
|
|
420
|
+
return records.many? if loaded?
|
|
421
|
+
limited_count > 1
|
|
292
422
|
end
|
|
293
423
|
|
|
294
424
|
# Returns a stable cache key that can be used to identify this query.
|
|
@@ -298,7 +428,7 @@ module ActiveRecord
|
|
|
298
428
|
# # => "products/query-1850ab3d302391b85b8693e941286659"
|
|
299
429
|
#
|
|
300
430
|
# If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
|
|
301
|
-
# in Rails 6.0 and earlier, the cache key will also include a version.
|
|
431
|
+
# in \Rails 6.0 and earlier, the cache key will also include a version.
|
|
302
432
|
#
|
|
303
433
|
# ActiveRecord::Base.collection_cache_versioning = false
|
|
304
434
|
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
|
@@ -308,7 +438,7 @@ module ActiveRecord
|
|
|
308
438
|
# last updated record.
|
|
309
439
|
#
|
|
310
440
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
|
311
|
-
def cache_key(timestamp_column =
|
|
441
|
+
def cache_key(timestamp_column = "updated_at")
|
|
312
442
|
@cache_keys ||= {}
|
|
313
443
|
@cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
|
|
314
444
|
end
|
|
@@ -317,7 +447,7 @@ module ActiveRecord
|
|
|
317
447
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
|
318
448
|
key = "#{klass.model_name.cache_key}/query-#{query_signature}"
|
|
319
449
|
|
|
320
|
-
if
|
|
450
|
+
if collection_cache_versioning
|
|
321
451
|
key
|
|
322
452
|
else
|
|
323
453
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
|
@@ -343,64 +473,89 @@ module ActiveRecord
|
|
|
343
473
|
end
|
|
344
474
|
|
|
345
475
|
def compute_cache_version(timestamp_column) # :nodoc:
|
|
346
|
-
|
|
476
|
+
timestamp_column = timestamp_column.to_s
|
|
477
|
+
|
|
478
|
+
if loaded?
|
|
347
479
|
size = records.size
|
|
348
480
|
if size > 0
|
|
349
|
-
timestamp =
|
|
481
|
+
timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
|
|
350
482
|
end
|
|
351
483
|
else
|
|
352
484
|
collection = eager_loading? ? apply_join_dependency : self
|
|
353
485
|
|
|
354
|
-
|
|
355
|
-
|
|
486
|
+
with_connection do |c|
|
|
487
|
+
column = c.visitor.compile(table[timestamp_column])
|
|
488
|
+
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
|
356
489
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
490
|
+
if collection.has_limit_or_offset?
|
|
491
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
|
492
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
|
493
|
+
subquery_alias = "subquery_for_cache_key"
|
|
494
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
|
495
|
+
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
|
496
|
+
else
|
|
497
|
+
query = collection.unscope(:order)
|
|
498
|
+
query.select_values = [select_values % column]
|
|
499
|
+
arel = query.arel
|
|
500
|
+
end
|
|
367
501
|
|
|
368
|
-
|
|
502
|
+
size, timestamp = c.select_rows(arel, nil).first
|
|
369
503
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
size = 0
|
|
504
|
+
if size
|
|
505
|
+
column_type = klass.type_for_attribute(timestamp_column)
|
|
506
|
+
timestamp = column_type.deserialize(timestamp)
|
|
507
|
+
else
|
|
508
|
+
size = 0
|
|
509
|
+
end
|
|
377
510
|
end
|
|
378
511
|
end
|
|
379
512
|
|
|
380
513
|
if timestamp
|
|
381
|
-
"#{size}-#{timestamp.utc.
|
|
514
|
+
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
|
382
515
|
else
|
|
383
516
|
"#{size}"
|
|
384
517
|
end
|
|
385
518
|
end
|
|
386
519
|
private :compute_cache_version
|
|
387
520
|
|
|
521
|
+
# Returns a cache key along with the version.
|
|
522
|
+
def cache_key_with_version
|
|
523
|
+
if version = cache_version
|
|
524
|
+
"#{cache_key}-#{version}"
|
|
525
|
+
else
|
|
526
|
+
cache_key
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
|
|
388
530
|
# Scope all queries to the current scope.
|
|
389
531
|
#
|
|
390
532
|
# Comment.where(post_id: 1).scoping do
|
|
391
533
|
# Comment.first
|
|
392
534
|
# end
|
|
393
|
-
# #
|
|
535
|
+
# # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
|
|
536
|
+
#
|
|
537
|
+
# If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
|
|
538
|
+
# for the relation including +update+ and +delete+ on instances.
|
|
539
|
+
# Once +all_queries+ is set to true it cannot be set to false in a
|
|
540
|
+
# nested block.
|
|
394
541
|
#
|
|
395
542
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
396
543
|
# the default_scope) during the execution of a block.
|
|
397
|
-
def scoping
|
|
398
|
-
|
|
544
|
+
def scoping(all_queries: nil, &block)
|
|
545
|
+
registry = klass.scope_registry
|
|
546
|
+
if global_scope?(registry) && all_queries == false
|
|
547
|
+
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
|
548
|
+
elsif already_in_scope?(registry)
|
|
549
|
+
yield
|
|
550
|
+
else
|
|
551
|
+
_scoping(self, registry, all_queries, &block)
|
|
552
|
+
end
|
|
399
553
|
end
|
|
400
554
|
|
|
401
|
-
def _exec_scope(
|
|
555
|
+
def _exec_scope(...) # :nodoc:
|
|
402
556
|
@delegate_to_klass = true
|
|
403
|
-
|
|
557
|
+
registry = klass.scope_registry
|
|
558
|
+
_scoping(nil, registry) { instance_exec(...) || self }
|
|
404
559
|
ensure
|
|
405
560
|
@delegate_to_klass = false
|
|
406
561
|
end
|
|
@@ -408,13 +563,14 @@ module ActiveRecord
|
|
|
408
563
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
|
409
564
|
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
|
410
565
|
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
|
411
|
-
# Active Record's normal type casting and serialization.
|
|
566
|
+
# Active Record's normal type casting and serialization. Returns the number of rows affected.
|
|
412
567
|
#
|
|
413
568
|
# Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
|
|
414
569
|
#
|
|
415
570
|
# ==== Parameters
|
|
416
571
|
#
|
|
417
|
-
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
|
|
572
|
+
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
|
|
573
|
+
# be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
|
|
418
574
|
#
|
|
419
575
|
# ==== Examples
|
|
420
576
|
#
|
|
@@ -429,35 +585,40 @@ module ActiveRecord
|
|
|
429
585
|
#
|
|
430
586
|
# # Update all invoices and set the number column to its id value.
|
|
431
587
|
# Invoice.update_all('number = id')
|
|
588
|
+
#
|
|
589
|
+
# # Update all books with 'Rails' in their title
|
|
590
|
+
# Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
|
|
432
591
|
def update_all(updates)
|
|
433
592
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
|
434
593
|
|
|
435
|
-
if
|
|
436
|
-
relation = apply_join_dependency
|
|
437
|
-
return relation.update_all(updates)
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
stmt = Arel::UpdateManager.new
|
|
441
|
-
stmt.table(arel.join_sources.empty? ? table : arel.source)
|
|
442
|
-
stmt.key = arel_attribute(primary_key)
|
|
443
|
-
stmt.take(arel.limit)
|
|
444
|
-
stmt.offset(arel.offset)
|
|
445
|
-
stmt.order(*arel.orders)
|
|
446
|
-
stmt.wheres = arel.constraints
|
|
594
|
+
return 0 if @none
|
|
447
595
|
|
|
448
596
|
if updates.is_a?(Hash)
|
|
449
597
|
if klass.locking_enabled? &&
|
|
450
598
|
!updates.key?(klass.locking_column) &&
|
|
451
599
|
!updates.key?(klass.locking_column.to_sym)
|
|
452
|
-
attr =
|
|
600
|
+
attr = table[klass.locking_column]
|
|
453
601
|
updates[attr.name] = _increment_attribute(attr)
|
|
454
602
|
end
|
|
455
|
-
|
|
603
|
+
values = _substitute_values(updates)
|
|
456
604
|
else
|
|
457
|
-
|
|
605
|
+
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
|
458
606
|
end
|
|
459
607
|
|
|
460
|
-
|
|
608
|
+
klass.with_connection do |c|
|
|
609
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
610
|
+
arel.source.left = table
|
|
611
|
+
|
|
612
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
613
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
614
|
+
key = if klass.composite_primary_key?
|
|
615
|
+
primary_key.map { |pk| table[pk] }
|
|
616
|
+
else
|
|
617
|
+
table[primary_key]
|
|
618
|
+
end
|
|
619
|
+
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
|
620
|
+
c.update(stmt, "#{klass} Update All").tap { reset }
|
|
621
|
+
end
|
|
461
622
|
end
|
|
462
623
|
|
|
463
624
|
def update(id = :all, attributes) # :nodoc:
|
|
@@ -468,31 +629,330 @@ module ActiveRecord
|
|
|
468
629
|
end
|
|
469
630
|
end
|
|
470
631
|
|
|
471
|
-
def
|
|
632
|
+
def update!(id = :all, attributes) # :nodoc:
|
|
633
|
+
if id == :all
|
|
634
|
+
each { |record| record.update!(attributes) }
|
|
635
|
+
else
|
|
636
|
+
klass.update!(id, attributes)
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
# Inserts a single record into the database in a single SQL INSERT
|
|
642
|
+
# statement. It does not instantiate any models nor does it trigger
|
|
643
|
+
# Active Record callbacks or validations. Though passed values
|
|
644
|
+
# go through Active Record's type casting and serialization.
|
|
645
|
+
#
|
|
646
|
+
# See #insert_all for documentation.
|
|
647
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
|
648
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
|
652
|
+
# statement. It does not instantiate any models nor does it trigger
|
|
653
|
+
# Active Record callbacks or validations. Though passed values
|
|
654
|
+
# go through Active Record's type casting and serialization.
|
|
655
|
+
#
|
|
656
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
|
657
|
+
# the attributes for a single row and must have the same keys.
|
|
658
|
+
#
|
|
659
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
|
660
|
+
# duplicate rows are skipped.
|
|
661
|
+
# Override with <tt>:unique_by</tt> (see below).
|
|
662
|
+
#
|
|
663
|
+
# Returns an ActiveRecord::Result with its contents based on
|
|
664
|
+
# <tt>:returning</tt> (see below).
|
|
665
|
+
#
|
|
666
|
+
# ==== Options
|
|
667
|
+
#
|
|
668
|
+
# [:returning]
|
|
669
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
|
670
|
+
# inserted records, which by default is the primary key.
|
|
671
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
672
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
673
|
+
# clause entirely.
|
|
674
|
+
#
|
|
675
|
+
# You can also pass an SQL string if you need more control on the return values
|
|
676
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
|
677
|
+
#
|
|
678
|
+
# [:unique_by]
|
|
679
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
|
680
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
|
681
|
+
#
|
|
682
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
|
683
|
+
#
|
|
684
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
|
685
|
+
# row has an existing id, or is not unique by another unique index,
|
|
686
|
+
# ActiveRecord::RecordNotUnique is raised.
|
|
687
|
+
#
|
|
688
|
+
# Unique indexes can be identified by columns or name:
|
|
689
|
+
#
|
|
690
|
+
# unique_by: :isbn
|
|
691
|
+
# unique_by: %i[ author_id name ]
|
|
692
|
+
# unique_by: :index_books_on_isbn
|
|
693
|
+
#
|
|
694
|
+
# [:record_timestamps]
|
|
695
|
+
# By default, automatic setting of timestamp columns is controlled by
|
|
696
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
|
697
|
+
# behavior.
|
|
698
|
+
#
|
|
699
|
+
# To override this and force automatic setting of timestamp columns one
|
|
700
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
|
701
|
+
#
|
|
702
|
+
# record_timestamps: true # Always set timestamps automatically
|
|
703
|
+
# record_timestamps: false # Never set timestamps automatically
|
|
704
|
+
#
|
|
705
|
+
# Because it relies on the index information from the database
|
|
706
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
|
707
|
+
# Active Record's schema_cache.
|
|
708
|
+
#
|
|
709
|
+
# ==== Example
|
|
710
|
+
#
|
|
711
|
+
# # Insert records and skip inserting any duplicates.
|
|
712
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
|
713
|
+
#
|
|
714
|
+
# Book.insert_all([
|
|
715
|
+
# { id: 1, title: "Rework", author: "David" },
|
|
716
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
|
717
|
+
# ])
|
|
718
|
+
#
|
|
719
|
+
# # insert_all works on chained scopes, and you can use create_with
|
|
720
|
+
# # to set default attributes for all inserted records.
|
|
721
|
+
#
|
|
722
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
|
723
|
+
# { id: 1, title: "Rework" },
|
|
724
|
+
# { id: 2, title: "Eloquent Ruby" }
|
|
725
|
+
# ])
|
|
726
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
|
727
|
+
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# Inserts a single record into the database in a single SQL INSERT
|
|
731
|
+
# statement. It does not instantiate any models nor does it trigger
|
|
732
|
+
# Active Record callbacks or validations. Though passed values
|
|
733
|
+
# go through Active Record's type casting and serialization.
|
|
734
|
+
#
|
|
735
|
+
# See #insert_all! for more.
|
|
736
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
|
737
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
|
741
|
+
# statement. It does not instantiate any models nor does it trigger
|
|
742
|
+
# Active Record callbacks or validations. Though passed values
|
|
743
|
+
# go through Active Record's type casting and serialization.
|
|
744
|
+
#
|
|
745
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
|
746
|
+
# the attributes for a single row and must have the same keys.
|
|
747
|
+
#
|
|
748
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
|
749
|
+
# unique index on the table. In that case, no rows are inserted.
|
|
750
|
+
#
|
|
751
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
|
752
|
+
#
|
|
753
|
+
# Returns an ActiveRecord::Result with its contents based on
|
|
754
|
+
# <tt>:returning</tt> (see below).
|
|
755
|
+
#
|
|
756
|
+
# ==== Options
|
|
757
|
+
#
|
|
758
|
+
# [:returning]
|
|
759
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
|
760
|
+
# inserted records, which by default is the primary key.
|
|
761
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
762
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
763
|
+
# clause entirely.
|
|
764
|
+
#
|
|
765
|
+
# You can also pass an SQL string if you need more control on the return values
|
|
766
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
|
767
|
+
#
|
|
768
|
+
# [:record_timestamps]
|
|
769
|
+
# By default, automatic setting of timestamp columns is controlled by
|
|
770
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
|
771
|
+
# behavior.
|
|
772
|
+
#
|
|
773
|
+
# To override this and force automatic setting of timestamp columns one
|
|
774
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
|
775
|
+
#
|
|
776
|
+
# record_timestamps: true # Always set timestamps automatically
|
|
777
|
+
# record_timestamps: false # Never set timestamps automatically
|
|
778
|
+
#
|
|
779
|
+
# ==== Examples
|
|
780
|
+
#
|
|
781
|
+
# # Insert multiple records
|
|
782
|
+
# Book.insert_all!([
|
|
783
|
+
# { title: "Rework", author: "David" },
|
|
784
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
|
785
|
+
# ])
|
|
786
|
+
#
|
|
787
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
|
788
|
+
# # does not have a unique id.
|
|
789
|
+
# Book.insert_all!([
|
|
790
|
+
# { id: 1, title: "Rework", author: "David" },
|
|
791
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
|
792
|
+
# ])
|
|
793
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
|
794
|
+
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
# Updates or inserts (upserts) a single record into the database in a
|
|
798
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
|
799
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
|
800
|
+
# go through Active Record's type casting and serialization.
|
|
801
|
+
#
|
|
802
|
+
# See #upsert_all for documentation.
|
|
803
|
+
def upsert(attributes, **kwargs)
|
|
804
|
+
upsert_all([ attributes ], **kwargs)
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
|
808
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
|
809
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
|
810
|
+
# go through Active Record's type casting and serialization.
|
|
811
|
+
#
|
|
812
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
|
813
|
+
# the attributes for a single row and must have the same keys.
|
|
814
|
+
#
|
|
815
|
+
# Returns an ActiveRecord::Result with its contents based on
|
|
816
|
+
# <tt>:returning</tt> (see below).
|
|
817
|
+
#
|
|
818
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
|
819
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
|
820
|
+
# columns, and columns covered by the optional +unique_by+.
|
|
821
|
+
#
|
|
822
|
+
# ==== Options
|
|
823
|
+
#
|
|
824
|
+
# [:returning]
|
|
825
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
|
826
|
+
# upserted records, which by default is the primary key.
|
|
827
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
828
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
829
|
+
# clause entirely.
|
|
830
|
+
#
|
|
831
|
+
# You can also pass an SQL string if you need more control on the return values
|
|
832
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
|
833
|
+
#
|
|
834
|
+
# [:unique_by]
|
|
835
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
|
836
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
|
837
|
+
#
|
|
838
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
|
839
|
+
#
|
|
840
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
|
841
|
+
# row has an existing id, or is not unique by another unique index,
|
|
842
|
+
# ActiveRecord::RecordNotUnique is raised.
|
|
843
|
+
#
|
|
844
|
+
# Unique indexes can be identified by columns or name:
|
|
845
|
+
#
|
|
846
|
+
# unique_by: :isbn
|
|
847
|
+
# unique_by: %i[ author_id name ]
|
|
848
|
+
# unique_by: :index_books_on_isbn
|
|
849
|
+
#
|
|
850
|
+
# Because it relies on the index information from the database
|
|
851
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
|
852
|
+
# Active Record's schema_cache.
|
|
853
|
+
#
|
|
854
|
+
# [:on_duplicate]
|
|
855
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
|
856
|
+
#
|
|
857
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
|
858
|
+
# by yourself.
|
|
859
|
+
#
|
|
860
|
+
# Example:
|
|
861
|
+
#
|
|
862
|
+
# Commodity.upsert_all(
|
|
863
|
+
# [
|
|
864
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
|
865
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
|
866
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
|
867
|
+
# ],
|
|
868
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
|
869
|
+
# )
|
|
870
|
+
#
|
|
871
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
|
872
|
+
#
|
|
873
|
+
# [:update_only]
|
|
874
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
|
875
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
|
876
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
|
877
|
+
#
|
|
878
|
+
# Example:
|
|
879
|
+
#
|
|
880
|
+
# Commodity.upsert_all(
|
|
881
|
+
# [
|
|
882
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
|
883
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
|
884
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
|
885
|
+
# ],
|
|
886
|
+
# update_only: [:price] # Only prices will be updated
|
|
887
|
+
# )
|
|
888
|
+
#
|
|
889
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
|
890
|
+
#
|
|
891
|
+
# [:record_timestamps]
|
|
892
|
+
# By default, automatic setting of timestamp columns is controlled by
|
|
893
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
|
894
|
+
# behavior.
|
|
895
|
+
#
|
|
896
|
+
# To override this and force automatic setting of timestamp columns one
|
|
897
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
|
898
|
+
#
|
|
899
|
+
# record_timestamps: true # Always set timestamps automatically
|
|
900
|
+
# record_timestamps: false # Never set timestamps automatically
|
|
901
|
+
#
|
|
902
|
+
# ==== Examples
|
|
903
|
+
#
|
|
904
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
|
905
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
|
906
|
+
#
|
|
907
|
+
# Book.upsert_all([
|
|
908
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
|
909
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
|
910
|
+
# ], unique_by: :isbn)
|
|
911
|
+
#
|
|
912
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
|
913
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
|
914
|
+
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
# Updates the counters of the records in the current relation.
|
|
918
|
+
#
|
|
919
|
+
# ==== Parameters
|
|
920
|
+
#
|
|
921
|
+
# * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
|
|
922
|
+
# * <tt>:touch</tt> option - Touch the timestamp columns when updating.
|
|
923
|
+
# * If attributes names are passed, they are updated along with update_at/on attributes.
|
|
924
|
+
#
|
|
925
|
+
# ==== Examples
|
|
926
|
+
#
|
|
927
|
+
# # For Posts by a given author increment the comment_count by 1.
|
|
928
|
+
# Post.where(author_id: author.id).update_counters(comment_count: 1)
|
|
929
|
+
def update_counters(counters)
|
|
472
930
|
touch = counters.delete(:touch)
|
|
473
931
|
|
|
474
932
|
updates = {}
|
|
475
933
|
counters.each do |counter_name, value|
|
|
476
|
-
attr =
|
|
934
|
+
attr = table[counter_name]
|
|
477
935
|
updates[attr.name] = _increment_attribute(attr, value)
|
|
478
936
|
end
|
|
479
937
|
|
|
480
938
|
if touch
|
|
481
939
|
names = touch if touch != true
|
|
482
|
-
|
|
940
|
+
names = Array.wrap(names)
|
|
941
|
+
options = names.extract_options!
|
|
942
|
+
touch_updates = klass.touch_attributes_with_time(*names, **options)
|
|
483
943
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
|
484
944
|
end
|
|
485
945
|
|
|
486
946
|
update_all updates
|
|
487
947
|
end
|
|
488
948
|
|
|
489
|
-
# Touches all records in the current relation
|
|
490
|
-
#
|
|
949
|
+
# Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
|
|
950
|
+
# It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
|
|
491
951
|
# This method can be passed attribute names and an optional time argument.
|
|
492
952
|
# If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
|
493
953
|
# If no time argument is passed, the current time is used as default.
|
|
494
954
|
#
|
|
495
|
-
#
|
|
955
|
+
# ==== Examples
|
|
496
956
|
#
|
|
497
957
|
# # Touch all records
|
|
498
958
|
# Person.all.touch_all
|
|
@@ -552,6 +1012,8 @@ module ActiveRecord
|
|
|
552
1012
|
# Post.distinct.delete_all
|
|
553
1013
|
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
|
|
554
1014
|
def delete_all
|
|
1015
|
+
return 0 if @none
|
|
1016
|
+
|
|
555
1017
|
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
|
|
556
1018
|
value = @values[method]
|
|
557
1019
|
method == :distinct ? value : value&.any?
|
|
@@ -560,23 +1022,79 @@ module ActiveRecord
|
|
|
560
1022
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
|
561
1023
|
end
|
|
562
1024
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
1025
|
+
klass.with_connection do |c|
|
|
1026
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
1027
|
+
arel.source.left = table
|
|
1028
|
+
|
|
1029
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
1030
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
1031
|
+
key = if klass.composite_primary_key?
|
|
1032
|
+
primary_key.map { |pk| table[pk] }
|
|
1033
|
+
else
|
|
1034
|
+
table[primary_key]
|
|
1035
|
+
end
|
|
1036
|
+
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
|
1037
|
+
|
|
1038
|
+
c.delete(stmt, "#{klass} Delete All").tap { reset }
|
|
566
1039
|
end
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
|
1043
|
+
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
|
1044
|
+
# Record objects are not instantiated, so the object's callbacks are not
|
|
1045
|
+
# executed, including any <tt>:dependent</tt> association options.
|
|
1046
|
+
#
|
|
1047
|
+
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
|
1048
|
+
#
|
|
1049
|
+
# Note: Although it is often much faster than the alternative, #destroy,
|
|
1050
|
+
# skipping callbacks might bypass business logic in your application
|
|
1051
|
+
# that ensures referential integrity or performs other essential jobs.
|
|
1052
|
+
#
|
|
1053
|
+
# ==== Examples
|
|
1054
|
+
#
|
|
1055
|
+
# # Delete a single row
|
|
1056
|
+
# Todo.delete(1)
|
|
1057
|
+
#
|
|
1058
|
+
# # Delete multiple rows
|
|
1059
|
+
# Todo.delete([2,3,4])
|
|
1060
|
+
def delete(id_or_array)
|
|
1061
|
+
return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
|
|
567
1062
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
stmt.key = arel_attribute(primary_key)
|
|
571
|
-
stmt.take(arel.limit)
|
|
572
|
-
stmt.offset(arel.offset)
|
|
573
|
-
stmt.order(*arel.orders)
|
|
574
|
-
stmt.wheres = arel.constraints
|
|
1063
|
+
where(model.primary_key => id_or_array).delete_all
|
|
1064
|
+
end
|
|
575
1065
|
|
|
576
|
-
affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
|
|
577
1066
|
|
|
578
|
-
|
|
579
|
-
|
|
1067
|
+
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
|
1068
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
|
1069
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
|
1070
|
+
#
|
|
1071
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
|
1072
|
+
# from the attributes, and then calls destroy on it.
|
|
1073
|
+
#
|
|
1074
|
+
# ==== Parameters
|
|
1075
|
+
#
|
|
1076
|
+
# * +id+ - This should be the id or an array of ids to be destroyed.
|
|
1077
|
+
#
|
|
1078
|
+
# ==== Examples
|
|
1079
|
+
#
|
|
1080
|
+
# # Destroy a single object
|
|
1081
|
+
# Todo.destroy(1)
|
|
1082
|
+
#
|
|
1083
|
+
# # Destroy multiple objects
|
|
1084
|
+
# todos = [1,2,3]
|
|
1085
|
+
# Todo.destroy(todos)
|
|
1086
|
+
def destroy(id)
|
|
1087
|
+
multiple_ids = if model.composite_primary_key?
|
|
1088
|
+
id.first.is_a?(Array)
|
|
1089
|
+
else
|
|
1090
|
+
id.is_a?(Array)
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
if multiple_ids
|
|
1094
|
+
find(id).each(&:destroy)
|
|
1095
|
+
else
|
|
1096
|
+
find(id).destroy
|
|
1097
|
+
end
|
|
580
1098
|
end
|
|
581
1099
|
|
|
582
1100
|
# Finds and destroys all records matching the specified conditions.
|
|
@@ -605,6 +1123,49 @@ module ActiveRecord
|
|
|
605
1123
|
where(*args).delete_all
|
|
606
1124
|
end
|
|
607
1125
|
|
|
1126
|
+
# Schedule the query to be performed from a background thread pool.
|
|
1127
|
+
#
|
|
1128
|
+
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
|
1129
|
+
#
|
|
1130
|
+
# When the +Relation+ is iterated, if the background query wasn't executed yet,
|
|
1131
|
+
# it will be performed by the foreground thread.
|
|
1132
|
+
#
|
|
1133
|
+
# Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
|
|
1134
|
+
# for queries to actually be executed concurrently. Otherwise it defaults to
|
|
1135
|
+
# executing them in the foreground.
|
|
1136
|
+
#
|
|
1137
|
+
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
|
1138
|
+
# fixtures are enabled.
|
|
1139
|
+
#
|
|
1140
|
+
# If the query was actually executed in the background, the Active Record logs will show
|
|
1141
|
+
# it by prefixing the log line with <tt>ASYNC</tt>:
|
|
1142
|
+
#
|
|
1143
|
+
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
|
1144
|
+
def load_async
|
|
1145
|
+
with_connection do |c|
|
|
1146
|
+
return load if !c.async_enabled?
|
|
1147
|
+
|
|
1148
|
+
unless loaded?
|
|
1149
|
+
result = exec_main_query(async: c.current_transaction.closed?)
|
|
1150
|
+
|
|
1151
|
+
if result.is_a?(Array)
|
|
1152
|
+
@records = result
|
|
1153
|
+
else
|
|
1154
|
+
@future_result = result
|
|
1155
|
+
end
|
|
1156
|
+
@loaded = true
|
|
1157
|
+
end
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
self
|
|
1161
|
+
end
|
|
1162
|
+
|
|
1163
|
+
# Returns <tt>true</tt> if the relation was scheduled on the background
|
|
1164
|
+
# thread pool.
|
|
1165
|
+
def scheduled?
|
|
1166
|
+
!!@future_result
|
|
1167
|
+
end
|
|
1168
|
+
|
|
608
1169
|
# Causes the records to be loaded from the database if they have not
|
|
609
1170
|
# been loaded already. You can use this if for some reason you need
|
|
610
1171
|
# to explicitly load some records before actually using them. The
|
|
@@ -612,7 +1173,10 @@ module ActiveRecord
|
|
|
612
1173
|
#
|
|
613
1174
|
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
|
614
1175
|
def load(&block)
|
|
615
|
-
|
|
1176
|
+
if !loaded? || scheduled?
|
|
1177
|
+
@records = exec_queries(&block)
|
|
1178
|
+
@loaded = true
|
|
1179
|
+
end
|
|
616
1180
|
|
|
617
1181
|
self
|
|
618
1182
|
end
|
|
@@ -624,27 +1188,29 @@ module ActiveRecord
|
|
|
624
1188
|
end
|
|
625
1189
|
|
|
626
1190
|
def reset
|
|
1191
|
+
@future_result&.cancel
|
|
1192
|
+
@future_result = nil
|
|
627
1193
|
@delegate_to_klass = false
|
|
628
|
-
@_deprecated_scope_source = nil
|
|
629
1194
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
|
630
|
-
@
|
|
631
|
-
@
|
|
1195
|
+
@offsets = @take = nil
|
|
1196
|
+
@cache_keys = nil
|
|
1197
|
+
@cache_versions = nil
|
|
1198
|
+
@records = nil
|
|
632
1199
|
self
|
|
633
1200
|
end
|
|
634
1201
|
|
|
635
1202
|
# Returns sql statement for the relation.
|
|
636
1203
|
#
|
|
637
1204
|
# User.where(name: 'Oscar').to_sql
|
|
638
|
-
# #
|
|
1205
|
+
# # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
|
639
1206
|
def to_sql
|
|
640
|
-
@to_sql ||=
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
conn = klass.connection
|
|
1207
|
+
@to_sql ||= if eager_loading?
|
|
1208
|
+
apply_join_dependency do |relation, join_dependency|
|
|
1209
|
+
relation = join_dependency.apply_column_aliases(relation)
|
|
1210
|
+
relation.to_sql
|
|
1211
|
+
end
|
|
1212
|
+
else
|
|
1213
|
+
klass.with_connection do |conn|
|
|
648
1214
|
conn.unprepared_statement { conn.to_sql(arel) }
|
|
649
1215
|
end
|
|
650
1216
|
end
|
|
@@ -654,12 +1220,14 @@ module ActiveRecord
|
|
|
654
1220
|
#
|
|
655
1221
|
# User.where(name: 'Oscar').where_values_hash
|
|
656
1222
|
# # => {name: "Oscar"}
|
|
657
|
-
def where_values_hash(relation_table_name = klass.table_name)
|
|
1223
|
+
def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
|
|
658
1224
|
where_clause.to_h(relation_table_name)
|
|
659
1225
|
end
|
|
660
1226
|
|
|
661
1227
|
def scope_for_create
|
|
662
|
-
|
|
1228
|
+
hash = where_clause.to_h(klass.table_name, equality_only: true)
|
|
1229
|
+
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
|
1230
|
+
hash
|
|
663
1231
|
end
|
|
664
1232
|
|
|
665
1233
|
# Returns true if relation needs eager loading.
|
|
@@ -672,7 +1240,7 @@ module ActiveRecord
|
|
|
672
1240
|
# Joins that are also marked for preloading. In which case we should just eager load them.
|
|
673
1241
|
# Note that this is a naive implementation because we could have strings and symbols which
|
|
674
1242
|
# represent the same association, but that aren't matched by this. Also, we could have
|
|
675
|
-
# nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
|
|
1243
|
+
# nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
|
|
676
1244
|
def joined_includes_values
|
|
677
1245
|
includes_values & joins_values
|
|
678
1246
|
end
|
|
@@ -689,8 +1257,13 @@ module ActiveRecord
|
|
|
689
1257
|
end
|
|
690
1258
|
end
|
|
691
1259
|
|
|
692
|
-
def pretty_print(
|
|
693
|
-
|
|
1260
|
+
def pretty_print(pp)
|
|
1261
|
+
subject = loaded? ? records : annotate("loading for pp")
|
|
1262
|
+
entries = subject.take([limit_value, 11].compact.min)
|
|
1263
|
+
|
|
1264
|
+
entries[10] = "..." if entries.size == 11
|
|
1265
|
+
|
|
1266
|
+
pp.pp(entries)
|
|
694
1267
|
end
|
|
695
1268
|
|
|
696
1269
|
# Returns true if relation is blank.
|
|
@@ -702,8 +1275,12 @@ module ActiveRecord
|
|
|
702
1275
|
@values.dup
|
|
703
1276
|
end
|
|
704
1277
|
|
|
1278
|
+
def values_for_queries # :nodoc:
|
|
1279
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
|
1280
|
+
end
|
|
1281
|
+
|
|
705
1282
|
def inspect
|
|
706
|
-
subject = loaded? ? records :
|
|
1283
|
+
subject = loaded? ? records : annotate("loading for inspect")
|
|
707
1284
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
|
708
1285
|
|
|
709
1286
|
entries[10] = "..." if entries.size == 11
|
|
@@ -720,64 +1297,87 @@ module ActiveRecord
|
|
|
720
1297
|
end
|
|
721
1298
|
|
|
722
1299
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
|
723
|
-
|
|
724
|
-
|
|
1300
|
+
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
|
1301
|
+
end
|
|
1302
|
+
|
|
1303
|
+
class StrictLoadingScope # :nodoc:
|
|
1304
|
+
def self.empty_scope?
|
|
1305
|
+
true
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
def self.strict_loading_value
|
|
1309
|
+
true
|
|
1310
|
+
end
|
|
725
1311
|
end
|
|
726
1312
|
|
|
727
1313
|
def preload_associations(records) # :nodoc:
|
|
728
1314
|
preload = preload_values
|
|
729
1315
|
preload += includes_values unless eager_loading?
|
|
730
|
-
|
|
1316
|
+
scope = strict_loading_value ? StrictLoadingScope : nil
|
|
731
1317
|
preload.each do |associations|
|
|
732
|
-
|
|
733
|
-
preloader.preload records, associations
|
|
1318
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
|
734
1319
|
end
|
|
735
1320
|
end
|
|
736
1321
|
|
|
737
|
-
attr_reader :_deprecated_scope_source # :nodoc:
|
|
738
|
-
|
|
739
1322
|
protected
|
|
740
|
-
attr_writer :_deprecated_scope_source # :nodoc:
|
|
741
|
-
|
|
742
1323
|
def load_records(records)
|
|
743
1324
|
@records = records.freeze
|
|
744
1325
|
@loaded = true
|
|
745
1326
|
end
|
|
746
1327
|
|
|
747
|
-
def null_relation? # :nodoc:
|
|
748
|
-
is_a?(NullRelation)
|
|
749
|
-
end
|
|
750
|
-
|
|
751
1328
|
private
|
|
752
|
-
def already_in_scope?
|
|
753
|
-
@delegate_to_klass &&
|
|
754
|
-
scope = klass.current_scope(true)
|
|
755
|
-
scope && !scope._deprecated_scope_source
|
|
756
|
-
end
|
|
1329
|
+
def already_in_scope?(registry)
|
|
1330
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
|
757
1331
|
end
|
|
758
1332
|
|
|
759
|
-
def
|
|
760
|
-
|
|
1333
|
+
def global_scope?(registry)
|
|
1334
|
+
registry.global_current_scope(klass, true)
|
|
761
1335
|
end
|
|
762
1336
|
|
|
763
|
-
def
|
|
1337
|
+
def current_scope_restoring_block(&block)
|
|
1338
|
+
current_scope = klass.current_scope(true)
|
|
764
1339
|
-> record do
|
|
765
|
-
klass.current_scope =
|
|
1340
|
+
klass.current_scope = current_scope
|
|
766
1341
|
yield record if block_given?
|
|
767
1342
|
end
|
|
768
1343
|
end
|
|
769
1344
|
|
|
770
|
-
def
|
|
771
|
-
|
|
1345
|
+
def _new(attributes, &block)
|
|
1346
|
+
klass.new(attributes, &block)
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
def _create(attributes, &block)
|
|
1350
|
+
klass.create(attributes, &block)
|
|
1351
|
+
end
|
|
1352
|
+
|
|
1353
|
+
def _create!(attributes, &block)
|
|
1354
|
+
klass.create!(attributes, &block)
|
|
1355
|
+
end
|
|
1356
|
+
|
|
1357
|
+
def _scoping(scope, registry, all_queries = false)
|
|
1358
|
+
previous = registry.current_scope(klass, true)
|
|
1359
|
+
registry.set_current_scope(klass, scope)
|
|
1360
|
+
|
|
1361
|
+
if all_queries
|
|
1362
|
+
previous_global = registry.global_current_scope(klass, true)
|
|
1363
|
+
registry.set_global_current_scope(klass, scope)
|
|
1364
|
+
end
|
|
772
1365
|
yield
|
|
773
1366
|
ensure
|
|
774
|
-
klass
|
|
1367
|
+
registry.set_current_scope(klass, previous)
|
|
1368
|
+
if all_queries
|
|
1369
|
+
registry.set_global_current_scope(klass, previous_global)
|
|
1370
|
+
end
|
|
775
1371
|
end
|
|
776
1372
|
|
|
777
1373
|
def _substitute_values(values)
|
|
778
1374
|
values.map do |name, value|
|
|
779
|
-
attr =
|
|
780
|
-
|
|
1375
|
+
attr = table[name]
|
|
1376
|
+
if Arel.arel_node?(value)
|
|
1377
|
+
if value.is_a?(Arel::Nodes::SqlLiteral)
|
|
1378
|
+
value = Arel::Nodes::Grouping.new(value)
|
|
1379
|
+
end
|
|
1380
|
+
else
|
|
781
1381
|
type = klass.type_for_attribute(attr.name)
|
|
782
1382
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
|
783
1383
|
end
|
|
@@ -794,66 +1394,101 @@ module ActiveRecord
|
|
|
794
1394
|
|
|
795
1395
|
def exec_queries(&block)
|
|
796
1396
|
skip_query_cache_if_necessary do
|
|
797
|
-
|
|
798
|
-
|
|
1397
|
+
rows = if scheduled?
|
|
1398
|
+
future = @future_result
|
|
1399
|
+
@future_result = nil
|
|
1400
|
+
future.result
|
|
1401
|
+
else
|
|
1402
|
+
exec_main_query
|
|
1403
|
+
end
|
|
1404
|
+
|
|
1405
|
+
records = instantiate_records(rows, &block)
|
|
1406
|
+
preload_associations(records) unless skip_preloading_value
|
|
1407
|
+
|
|
1408
|
+
records.each(&:readonly!) if readonly_value
|
|
1409
|
+
records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
|
|
1410
|
+
|
|
1411
|
+
records
|
|
1412
|
+
end
|
|
1413
|
+
end
|
|
1414
|
+
|
|
1415
|
+
def exec_main_query(async: false)
|
|
1416
|
+
if @none
|
|
1417
|
+
if async
|
|
1418
|
+
return FutureResult.wrap([])
|
|
1419
|
+
else
|
|
1420
|
+
return []
|
|
1421
|
+
end
|
|
1422
|
+
end
|
|
1423
|
+
|
|
1424
|
+
skip_query_cache_if_necessary do
|
|
1425
|
+
if where_clause.contradiction?
|
|
1426
|
+
[].freeze
|
|
1427
|
+
elsif eager_loading?
|
|
1428
|
+
klass.with_connection do |c|
|
|
799
1429
|
apply_join_dependency do |relation, join_dependency|
|
|
800
1430
|
if relation.null_relation?
|
|
801
|
-
[]
|
|
1431
|
+
[].freeze
|
|
802
1432
|
else
|
|
803
1433
|
relation = join_dependency.apply_column_aliases(relation)
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
end
|
|
1434
|
+
@_join_dependency = join_dependency
|
|
1435
|
+
c.select_all(relation.arel, "SQL", async: async)
|
|
1436
|
+
end
|
|
807
1437
|
end
|
|
808
|
-
else
|
|
809
|
-
klass.find_by_sql(arel, &block).freeze
|
|
810
1438
|
end
|
|
1439
|
+
else
|
|
1440
|
+
klass.with_connection do |c|
|
|
1441
|
+
klass._query_by_sql(c, arel, async: async)
|
|
1442
|
+
end
|
|
1443
|
+
end
|
|
1444
|
+
end
|
|
1445
|
+
end
|
|
811
1446
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
@
|
|
817
|
-
|
|
1447
|
+
def instantiate_records(rows, &block)
|
|
1448
|
+
return [].freeze if rows.empty?
|
|
1449
|
+
if eager_loading?
|
|
1450
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
|
1451
|
+
@_join_dependency = nil
|
|
1452
|
+
records
|
|
1453
|
+
else
|
|
1454
|
+
klass._load_from_sql(rows, &block).freeze
|
|
818
1455
|
end
|
|
819
1456
|
end
|
|
820
1457
|
|
|
821
|
-
def skip_query_cache_if_necessary
|
|
1458
|
+
def skip_query_cache_if_necessary(&block)
|
|
822
1459
|
if skip_query_cache_value
|
|
823
|
-
uncached
|
|
824
|
-
yield
|
|
825
|
-
end
|
|
1460
|
+
uncached(&block)
|
|
826
1461
|
else
|
|
827
1462
|
yield
|
|
828
1463
|
end
|
|
829
1464
|
end
|
|
830
1465
|
|
|
831
|
-
def build_preloader
|
|
832
|
-
ActiveRecord::Associations::Preloader.new
|
|
833
|
-
end
|
|
834
|
-
|
|
835
1466
|
def references_eager_loaded_tables?
|
|
836
|
-
joined_tables =
|
|
1467
|
+
joined_tables = build_joins([]).flat_map do |join|
|
|
837
1468
|
if join.is_a?(Arel::Nodes::StringJoin)
|
|
838
1469
|
tables_in_string(join.left)
|
|
839
1470
|
else
|
|
840
|
-
|
|
1471
|
+
join.left.name
|
|
841
1472
|
end
|
|
842
1473
|
end
|
|
843
1474
|
|
|
844
|
-
joined_tables
|
|
1475
|
+
joined_tables << table.name
|
|
845
1476
|
|
|
846
1477
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
|
847
|
-
joined_tables
|
|
1478
|
+
joined_tables.map!(&:downcase)
|
|
848
1479
|
|
|
849
|
-
(references_values - joined_tables).
|
|
1480
|
+
!(references_values.map(&:to_s) - joined_tables).empty?
|
|
850
1481
|
end
|
|
851
1482
|
|
|
852
1483
|
def tables_in_string(string)
|
|
853
1484
|
return [] if string.blank?
|
|
854
1485
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
|
855
1486
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
|
856
|
-
string.scan(/
|
|
1487
|
+
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
|
1488
|
+
end
|
|
1489
|
+
|
|
1490
|
+
def limited_count
|
|
1491
|
+
limit_value ? count : limit(2).count
|
|
857
1492
|
end
|
|
858
1493
|
end
|
|
859
1494
|
end
|