activerecord 6.1.7 → 7.2.2
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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- 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 +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- 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 +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 -46
- 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 +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- 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 +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- 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 +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- 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 +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- 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 +170 -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 +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- 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 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -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 +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- 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/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- 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 +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- 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 +103 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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
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
|
@@ -31,6 +81,10 @@ module ActiveRecord
|
|
31
81
|
@loaded = false
|
32
82
|
@predicate_builder = predicate_builder
|
33
83
|
@delegate_to_klass = false
|
84
|
+
@future_result = nil
|
85
|
+
@records = nil
|
86
|
+
@async = false
|
87
|
+
@none = false
|
34
88
|
end
|
35
89
|
|
36
90
|
def initialize_copy(other)
|
@@ -38,15 +92,10 @@ module ActiveRecord
|
|
38
92
|
reset
|
39
93
|
end
|
40
94
|
|
41
|
-
def arel_attribute(name) # :nodoc:
|
42
|
-
table[name]
|
43
|
-
end
|
44
|
-
deprecate :arel_attribute
|
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
101
|
attr = table[name]
|
@@ -67,8 +116,12 @@ 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
126
|
alias build new
|
74
127
|
|
@@ -148,7 +201,7 @@ module ActiveRecord
|
|
148
201
|
# above can be alternatively written this way:
|
149
202
|
#
|
150
203
|
# # Find the first user named "Scarlett" or create a new one with a
|
151
|
-
# #
|
204
|
+
# # particular last name.
|
152
205
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
153
206
|
# user.last_name = 'Johansson'
|
154
207
|
# end
|
@@ -158,38 +211,41 @@ module ActiveRecord
|
|
158
211
|
# failed due to validation errors it won't be persisted, you get what
|
159
212
|
# #create returns in such situation.
|
160
213
|
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
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.
|
165
219
|
#
|
166
|
-
#
|
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.
|
167
224
|
def find_or_create_by(attributes, &block)
|
168
|
-
find_by(attributes) ||
|
225
|
+
find_by(attributes) || create_or_find_by(attributes, &block)
|
169
226
|
end
|
170
227
|
|
171
228
|
# Like #find_or_create_by, but calls
|
172
229
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
173
230
|
# is raised if the created record is invalid.
|
174
231
|
def find_or_create_by!(attributes, &block)
|
175
|
-
find_by(attributes) ||
|
232
|
+
find_by(attributes) || create_or_find_by!(attributes, &block)
|
176
233
|
end
|
177
234
|
|
178
|
-
# 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
|
179
236
|
# on one or several of its columns. If a row already exists with one or several of these
|
180
237
|
# unique constraints, the exception such an insertion would normally raise is caught,
|
181
238
|
# and the existing record with those attributes is found using #find_by!.
|
182
239
|
#
|
183
|
-
# This is similar to #find_or_create_by, but
|
184
|
-
#
|
185
|
-
# 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.
|
186
242
|
#
|
187
243
|
# There are several drawbacks to #create_or_find_by, though:
|
188
244
|
#
|
189
|
-
# * 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.
|
190
246
|
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
191
247
|
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
192
|
-
# matching record, which will then raise an
|
248
|
+
# matching record, which will then raise an ActiveRecord::RecordNotFound exception,
|
193
249
|
# rather than a record with the given attributes.
|
194
250
|
# * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
|
195
251
|
# we actually have another race condition between INSERT -> SELECT, which can be triggered
|
@@ -198,26 +254,40 @@ module ActiveRecord
|
|
198
254
|
# * It relies on exception handling to handle control flow, which may be marginally slower.
|
199
255
|
# * The primary key may auto-increment on each create, even if it fails. This can accelerate
|
200
256
|
# the problem of running out of integers, if the underlying table is still stuck on a primary
|
201
|
-
# 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
|
202
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.
|
203
261
|
#
|
204
262
|
# This method will return a record if all given attributes are covered by unique constraints
|
205
263
|
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
206
264
|
# and failed due to validation errors it won't be persisted, you get what #create returns in
|
207
265
|
# such situation.
|
208
266
|
def create_or_find_by(attributes, &block)
|
209
|
-
|
210
|
-
|
211
|
-
|
267
|
+
with_connection do |connection|
|
268
|
+
transaction(requires_new: true) { create(attributes, &block) }
|
269
|
+
rescue ActiveRecord::RecordNotUnique
|
270
|
+
if connection.transaction_open?
|
271
|
+
where(attributes).lock.find_by!(attributes)
|
272
|
+
else
|
273
|
+
find_by!(attributes)
|
274
|
+
end
|
275
|
+
end
|
212
276
|
end
|
213
277
|
|
214
278
|
# Like #create_or_find_by, but calls
|
215
279
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
216
280
|
# is raised if the created record is invalid.
|
217
281
|
def create_or_find_by!(attributes, &block)
|
218
|
-
|
219
|
-
|
220
|
-
|
282
|
+
with_connection do |connection|
|
283
|
+
transaction(requires_new: true) { create!(attributes, &block) }
|
284
|
+
rescue ActiveRecord::RecordNotUnique
|
285
|
+
if connection.transaction_open?
|
286
|
+
where(attributes).lock.find_by!(attributes)
|
287
|
+
else
|
288
|
+
find_by!(attributes)
|
289
|
+
end
|
290
|
+
end
|
221
291
|
end
|
222
292
|
|
223
293
|
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
|
@@ -230,13 +300,30 @@ module ActiveRecord
|
|
230
300
|
# returns the result as a string. The string is formatted imitating the
|
231
301
|
# ones printed by the database shell.
|
232
302
|
#
|
303
|
+
# User.all.explain
|
304
|
+
# # EXPLAIN SELECT `users`.* FROM `users`
|
305
|
+
# # ...
|
306
|
+
#
|
233
307
|
# Note that this method actually runs the queries, since the results of some
|
234
308
|
# are needed by the next ones when eager loading is going on.
|
235
309
|
#
|
310
|
+
# To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
|
311
|
+
# these methods on +explain+:
|
312
|
+
#
|
313
|
+
# User.all.explain.count
|
314
|
+
# # EXPLAIN SELECT COUNT(*) FROM `users`
|
315
|
+
# # ...
|
316
|
+
#
|
317
|
+
# The column name can be passed if required:
|
318
|
+
#
|
319
|
+
# User.all.explain.maximum(:id)
|
320
|
+
# # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
|
321
|
+
# # ...
|
322
|
+
#
|
236
323
|
# Please see further details in the
|
237
324
|
# {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
238
|
-
def explain
|
239
|
-
|
325
|
+
def explain(*options)
|
326
|
+
ExplainProxy.new(self, options)
|
240
327
|
end
|
241
328
|
|
242
329
|
# Converts relation objects to Array.
|
@@ -257,37 +344,71 @@ module ActiveRecord
|
|
257
344
|
|
258
345
|
# Returns size of the records.
|
259
346
|
def size
|
260
|
-
loaded?
|
347
|
+
if loaded?
|
348
|
+
records.length
|
349
|
+
else
|
350
|
+
count(:all)
|
351
|
+
end
|
261
352
|
end
|
262
353
|
|
263
354
|
# Returns true if there are no records.
|
264
355
|
def empty?
|
265
|
-
return
|
266
|
-
|
356
|
+
return true if @none
|
357
|
+
|
358
|
+
if loaded?
|
359
|
+
records.empty?
|
360
|
+
else
|
361
|
+
!exists?
|
362
|
+
end
|
267
363
|
end
|
268
364
|
|
269
365
|
# Returns true if there are no records.
|
270
|
-
|
271
|
-
|
366
|
+
#
|
367
|
+
# When a pattern argument is given, this method checks whether elements in
|
368
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
369
|
+
#
|
370
|
+
# posts.none?(Comment) # => true or false
|
371
|
+
def none?(*args)
|
372
|
+
return true if @none
|
373
|
+
|
374
|
+
return super if args.present? || block_given?
|
272
375
|
empty?
|
273
376
|
end
|
274
377
|
|
275
378
|
# Returns true if there are any records.
|
276
|
-
|
277
|
-
|
379
|
+
#
|
380
|
+
# When a pattern argument is given, this method checks whether elements in
|
381
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
382
|
+
#
|
383
|
+
# posts.any?(Post) # => true or false
|
384
|
+
def any?(*args)
|
385
|
+
return false if @none
|
386
|
+
|
387
|
+
return super if args.present? || block_given?
|
278
388
|
!empty?
|
279
389
|
end
|
280
390
|
|
281
391
|
# Returns true if there is exactly one record.
|
282
|
-
|
283
|
-
|
284
|
-
|
392
|
+
#
|
393
|
+
# When a pattern argument is given, this method checks whether elements in
|
394
|
+
# the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
|
395
|
+
#
|
396
|
+
# posts.one?(Post) # => true or false
|
397
|
+
def one?(*args)
|
398
|
+
return false if @none
|
399
|
+
|
400
|
+
return super if args.present? || block_given?
|
401
|
+
return records.one? if loaded?
|
402
|
+
limited_count == 1
|
285
403
|
end
|
286
404
|
|
287
405
|
# Returns true if there is more than one record.
|
288
406
|
def many?
|
407
|
+
return false if @none
|
408
|
+
|
289
409
|
return super if block_given?
|
290
|
-
|
410
|
+
return records.many? if loaded?
|
411
|
+
limited_count > 1
|
291
412
|
end
|
292
413
|
|
293
414
|
# Returns a stable cache key that can be used to identify this query.
|
@@ -297,7 +418,7 @@ module ActiveRecord
|
|
297
418
|
# # => "products/query-1850ab3d302391b85b8693e941286659"
|
298
419
|
#
|
299
420
|
# If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
|
300
|
-
# in Rails 6.0 and earlier, the cache key will also include a version.
|
421
|
+
# in \Rails 6.0 and earlier, the cache key will also include a version.
|
301
422
|
#
|
302
423
|
# ActiveRecord::Base.collection_cache_versioning = false
|
303
424
|
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
@@ -344,7 +465,7 @@ module ActiveRecord
|
|
344
465
|
def compute_cache_version(timestamp_column) # :nodoc:
|
345
466
|
timestamp_column = timestamp_column.to_s
|
346
467
|
|
347
|
-
if loaded?
|
468
|
+
if loaded?
|
348
469
|
size = records.size
|
349
470
|
if size > 0
|
350
471
|
timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
|
@@ -352,32 +473,35 @@ module ActiveRecord
|
|
352
473
|
else
|
353
474
|
collection = eager_loading? ? apply_join_dependency : self
|
354
475
|
|
355
|
-
|
356
|
-
|
476
|
+
with_connection do |c|
|
477
|
+
column = c.visitor.compile(table[timestamp_column])
|
478
|
+
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
357
479
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
480
|
+
if collection.has_limit_or_offset?
|
481
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
482
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
483
|
+
subquery_alias = "subquery_for_cache_key"
|
484
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
485
|
+
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
486
|
+
else
|
487
|
+
query = collection.unscope(:order)
|
488
|
+
query.select_values = [select_values % column]
|
489
|
+
arel = query.arel
|
490
|
+
end
|
368
491
|
|
369
|
-
|
492
|
+
size, timestamp = c.select_rows(arel, nil).first
|
370
493
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
494
|
+
if size
|
495
|
+
column_type = klass.type_for_attribute(timestamp_column)
|
496
|
+
timestamp = column_type.deserialize(timestamp)
|
497
|
+
else
|
498
|
+
size = 0
|
499
|
+
end
|
376
500
|
end
|
377
501
|
end
|
378
502
|
|
379
503
|
if timestamp
|
380
|
-
"#{size}-#{timestamp.utc.
|
504
|
+
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
381
505
|
else
|
382
506
|
"#{size}"
|
383
507
|
end
|
@@ -398,17 +522,30 @@ module ActiveRecord
|
|
398
522
|
# Comment.where(post_id: 1).scoping do
|
399
523
|
# Comment.first
|
400
524
|
# end
|
401
|
-
# #
|
525
|
+
# # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
|
526
|
+
#
|
527
|
+
# If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
|
528
|
+
# for the relation including +update+ and +delete+ on instances.
|
529
|
+
# Once +all_queries+ is set to true it cannot be set to false in a
|
530
|
+
# nested block.
|
402
531
|
#
|
403
532
|
# Please check unscoped if you want to remove all previous scopes (including
|
404
533
|
# the default_scope) during the execution of a block.
|
405
|
-
def scoping
|
406
|
-
|
534
|
+
def scoping(all_queries: nil, &block)
|
535
|
+
registry = klass.scope_registry
|
536
|
+
if global_scope?(registry) && all_queries == false
|
537
|
+
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
538
|
+
elsif already_in_scope?(registry)
|
539
|
+
yield
|
540
|
+
else
|
541
|
+
_scoping(self, registry, all_queries, &block)
|
542
|
+
end
|
407
543
|
end
|
408
544
|
|
409
|
-
def _exec_scope(
|
545
|
+
def _exec_scope(...) # :nodoc:
|
410
546
|
@delegate_to_klass = true
|
411
|
-
|
547
|
+
registry = klass.scope_registry
|
548
|
+
_scoping(nil, registry) { instance_exec(...) || self }
|
412
549
|
ensure
|
413
550
|
@delegate_to_klass = false
|
414
551
|
end
|
@@ -422,7 +559,8 @@ module ActiveRecord
|
|
422
559
|
#
|
423
560
|
# ==== Parameters
|
424
561
|
#
|
425
|
-
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
|
562
|
+
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
|
563
|
+
# be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
|
426
564
|
#
|
427
565
|
# ==== Examples
|
428
566
|
#
|
@@ -437,19 +575,13 @@ module ActiveRecord
|
|
437
575
|
#
|
438
576
|
# # Update all invoices and set the number column to its id value.
|
439
577
|
# Invoice.update_all('number = id')
|
578
|
+
#
|
579
|
+
# # Update all books with 'Rails' in their title
|
580
|
+
# Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
|
440
581
|
def update_all(updates)
|
441
582
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
442
583
|
|
443
|
-
|
444
|
-
arel.source.left = table
|
445
|
-
|
446
|
-
stmt = Arel::UpdateManager.new
|
447
|
-
stmt.table(arel.source)
|
448
|
-
stmt.key = table[primary_key]
|
449
|
-
stmt.take(arel.limit)
|
450
|
-
stmt.offset(arel.offset)
|
451
|
-
stmt.order(*arel.orders)
|
452
|
-
stmt.wheres = arel.constraints
|
584
|
+
return 0 if @none
|
453
585
|
|
454
586
|
if updates.is_a?(Hash)
|
455
587
|
if klass.locking_enabled? &&
|
@@ -458,12 +590,25 @@ module ActiveRecord
|
|
458
590
|
attr = table[klass.locking_column]
|
459
591
|
updates[attr.name] = _increment_attribute(attr)
|
460
592
|
end
|
461
|
-
|
593
|
+
values = _substitute_values(updates)
|
462
594
|
else
|
463
|
-
|
595
|
+
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
464
596
|
end
|
465
597
|
|
466
|
-
klass.
|
598
|
+
klass.with_connection do |c|
|
599
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
600
|
+
arel.source.left = table
|
601
|
+
|
602
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
603
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
604
|
+
key = if klass.composite_primary_key?
|
605
|
+
primary_key.map { |pk| table[pk] }
|
606
|
+
else
|
607
|
+
table[primary_key]
|
608
|
+
end
|
609
|
+
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
610
|
+
c.update(stmt, "#{klass} Update All").tap { reset }
|
611
|
+
end
|
467
612
|
end
|
468
613
|
|
469
614
|
def update(id = :all, attributes) # :nodoc:
|
@@ -474,6 +619,291 @@ module ActiveRecord
|
|
474
619
|
end
|
475
620
|
end
|
476
621
|
|
622
|
+
def update!(id = :all, attributes) # :nodoc:
|
623
|
+
if id == :all
|
624
|
+
each { |record| record.update!(attributes) }
|
625
|
+
else
|
626
|
+
klass.update!(id, attributes)
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
|
631
|
+
# Inserts a single record into the database in a single SQL INSERT
|
632
|
+
# statement. It does not instantiate any models nor does it trigger
|
633
|
+
# Active Record callbacks or validations. Though passed values
|
634
|
+
# go through Active Record's type casting and serialization.
|
635
|
+
#
|
636
|
+
# See #insert_all for documentation.
|
637
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
638
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
639
|
+
end
|
640
|
+
|
641
|
+
# Inserts multiple records 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
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
647
|
+
# the attributes for a single row and must have the same keys.
|
648
|
+
#
|
649
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
650
|
+
# duplicate rows are skipped.
|
651
|
+
# Override with <tt>:unique_by</tt> (see below).
|
652
|
+
#
|
653
|
+
# Returns an ActiveRecord::Result with its contents based on
|
654
|
+
# <tt>:returning</tt> (see below).
|
655
|
+
#
|
656
|
+
# ==== Options
|
657
|
+
#
|
658
|
+
# [:returning]
|
659
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
660
|
+
# inserted records, which by default is the primary key.
|
661
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
662
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
663
|
+
# clause entirely.
|
664
|
+
#
|
665
|
+
# You can also pass an SQL string if you need more control on the return values
|
666
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
667
|
+
#
|
668
|
+
# [:unique_by]
|
669
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
670
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
671
|
+
#
|
672
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
673
|
+
#
|
674
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
675
|
+
# row has an existing id, or is not unique by another unique index,
|
676
|
+
# ActiveRecord::RecordNotUnique is raised.
|
677
|
+
#
|
678
|
+
# Unique indexes can be identified by columns or name:
|
679
|
+
#
|
680
|
+
# unique_by: :isbn
|
681
|
+
# unique_by: %i[ author_id name ]
|
682
|
+
# unique_by: :index_books_on_isbn
|
683
|
+
#
|
684
|
+
# [:record_timestamps]
|
685
|
+
# By default, automatic setting of timestamp columns is controlled by
|
686
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
687
|
+
# behavior.
|
688
|
+
#
|
689
|
+
# To override this and force automatic setting of timestamp columns one
|
690
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
691
|
+
#
|
692
|
+
# record_timestamps: true # Always set timestamps automatically
|
693
|
+
# record_timestamps: false # Never set timestamps automatically
|
694
|
+
#
|
695
|
+
# Because it relies on the index information from the database
|
696
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
697
|
+
# Active Record's schema_cache.
|
698
|
+
#
|
699
|
+
# ==== Example
|
700
|
+
#
|
701
|
+
# # Insert records and skip inserting any duplicates.
|
702
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
703
|
+
#
|
704
|
+
# Book.insert_all([
|
705
|
+
# { id: 1, title: "Rework", author: "David" },
|
706
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
707
|
+
# ])
|
708
|
+
#
|
709
|
+
# # insert_all works on chained scopes, and you can use create_with
|
710
|
+
# # to set default attributes for all inserted records.
|
711
|
+
#
|
712
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
713
|
+
# { id: 1, title: "Rework" },
|
714
|
+
# { id: 2, title: "Eloquent Ruby" }
|
715
|
+
# ])
|
716
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
717
|
+
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
718
|
+
end
|
719
|
+
|
720
|
+
# Inserts a single record into the database in a single SQL INSERT
|
721
|
+
# statement. It does not instantiate any models nor does it trigger
|
722
|
+
# Active Record callbacks or validations. Though passed values
|
723
|
+
# go through Active Record's type casting and serialization.
|
724
|
+
#
|
725
|
+
# See #insert_all! for more.
|
726
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
727
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
728
|
+
end
|
729
|
+
|
730
|
+
# Inserts multiple records 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
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
736
|
+
# the attributes for a single row and must have the same keys.
|
737
|
+
#
|
738
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
739
|
+
# unique index on the table. In that case, no rows are inserted.
|
740
|
+
#
|
741
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
742
|
+
#
|
743
|
+
# Returns an ActiveRecord::Result with its contents based on
|
744
|
+
# <tt>:returning</tt> (see below).
|
745
|
+
#
|
746
|
+
# ==== Options
|
747
|
+
#
|
748
|
+
# [:returning]
|
749
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
750
|
+
# inserted records, which by default is the primary key.
|
751
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
752
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
753
|
+
# clause entirely.
|
754
|
+
#
|
755
|
+
# You can also pass an SQL string if you need more control on the return values
|
756
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
757
|
+
#
|
758
|
+
# [:record_timestamps]
|
759
|
+
# By default, automatic setting of timestamp columns is controlled by
|
760
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
761
|
+
# behavior.
|
762
|
+
#
|
763
|
+
# To override this and force automatic setting of timestamp columns one
|
764
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
765
|
+
#
|
766
|
+
# record_timestamps: true # Always set timestamps automatically
|
767
|
+
# record_timestamps: false # Never set timestamps automatically
|
768
|
+
#
|
769
|
+
# ==== Examples
|
770
|
+
#
|
771
|
+
# # Insert multiple records
|
772
|
+
# Book.insert_all!([
|
773
|
+
# { title: "Rework", author: "David" },
|
774
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
775
|
+
# ])
|
776
|
+
#
|
777
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
778
|
+
# # does not have a unique id.
|
779
|
+
# Book.insert_all!([
|
780
|
+
# { id: 1, title: "Rework", author: "David" },
|
781
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
782
|
+
# ])
|
783
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
784
|
+
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
785
|
+
end
|
786
|
+
|
787
|
+
# Updates or inserts (upserts) a single record into the database in a
|
788
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
789
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
790
|
+
# go through Active Record's type casting and serialization.
|
791
|
+
#
|
792
|
+
# See #upsert_all for documentation.
|
793
|
+
def upsert(attributes, **kwargs)
|
794
|
+
upsert_all([ attributes ], **kwargs)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Updates or inserts (upserts) multiple records 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
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
803
|
+
# the attributes for a single row and must have the same keys.
|
804
|
+
#
|
805
|
+
# Returns an ActiveRecord::Result with its contents based on
|
806
|
+
# <tt>:returning</tt> (see below).
|
807
|
+
#
|
808
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
809
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
810
|
+
# columns, and columns covered by the optional +unique_by+.
|
811
|
+
#
|
812
|
+
# ==== Options
|
813
|
+
#
|
814
|
+
# [:returning]
|
815
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
816
|
+
# inserted records, which by default is the primary key.
|
817
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
818
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
819
|
+
# clause entirely.
|
820
|
+
#
|
821
|
+
# You can also pass an SQL string if you need more control on the return values
|
822
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
823
|
+
#
|
824
|
+
# [:unique_by]
|
825
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
826
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
827
|
+
#
|
828
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
829
|
+
#
|
830
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
831
|
+
# row has an existing id, or is not unique by another unique index,
|
832
|
+
# ActiveRecord::RecordNotUnique is raised.
|
833
|
+
#
|
834
|
+
# Unique indexes can be identified by columns or name:
|
835
|
+
#
|
836
|
+
# unique_by: :isbn
|
837
|
+
# unique_by: %i[ author_id name ]
|
838
|
+
# unique_by: :index_books_on_isbn
|
839
|
+
#
|
840
|
+
# Because it relies on the index information from the database
|
841
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
842
|
+
# Active Record's schema_cache.
|
843
|
+
#
|
844
|
+
# [:on_duplicate]
|
845
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
846
|
+
#
|
847
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
848
|
+
# by yourself.
|
849
|
+
#
|
850
|
+
# Example:
|
851
|
+
#
|
852
|
+
# Commodity.upsert_all(
|
853
|
+
# [
|
854
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
855
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
856
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
857
|
+
# ],
|
858
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
859
|
+
# )
|
860
|
+
#
|
861
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
862
|
+
#
|
863
|
+
# [:update_only]
|
864
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
865
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
866
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
867
|
+
#
|
868
|
+
# Example:
|
869
|
+
#
|
870
|
+
# Commodity.upsert_all(
|
871
|
+
# [
|
872
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
873
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
874
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
875
|
+
# ],
|
876
|
+
# update_only: [:price] # Only prices will be updated
|
877
|
+
# )
|
878
|
+
#
|
879
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
880
|
+
#
|
881
|
+
# [:record_timestamps]
|
882
|
+
# By default, automatic setting of timestamp columns is controlled by
|
883
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
884
|
+
# behavior.
|
885
|
+
#
|
886
|
+
# To override this and force automatic setting of timestamp columns one
|
887
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
888
|
+
#
|
889
|
+
# record_timestamps: true # Always set timestamps automatically
|
890
|
+
# record_timestamps: false # Never set timestamps automatically
|
891
|
+
#
|
892
|
+
# ==== Examples
|
893
|
+
#
|
894
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
895
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
896
|
+
#
|
897
|
+
# Book.upsert_all([
|
898
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
899
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
900
|
+
# ], unique_by: :isbn)
|
901
|
+
#
|
902
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
903
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
904
|
+
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
905
|
+
end
|
906
|
+
|
477
907
|
# Updates the counters of the records in the current relation.
|
478
908
|
#
|
479
909
|
# ==== Parameters
|
@@ -572,6 +1002,8 @@ module ActiveRecord
|
|
572
1002
|
# Post.distinct.delete_all
|
573
1003
|
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
|
574
1004
|
def delete_all
|
1005
|
+
return 0 if @none
|
1006
|
+
|
575
1007
|
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
|
576
1008
|
value = @values[method]
|
577
1009
|
method == :distinct ? value : value&.any?
|
@@ -580,18 +1012,79 @@ module ActiveRecord
|
|
580
1012
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
581
1013
|
end
|
582
1014
|
|
583
|
-
|
584
|
-
|
1015
|
+
klass.with_connection do |c|
|
1016
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
1017
|
+
arel.source.left = table
|
585
1018
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
1019
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
1020
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
1021
|
+
key = if klass.composite_primary_key?
|
1022
|
+
primary_key.map { |pk| table[pk] }
|
1023
|
+
else
|
1024
|
+
table[primary_key]
|
1025
|
+
end
|
1026
|
+
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
1027
|
+
|
1028
|
+
c.delete(stmt, "#{klass} Delete All").tap { reset }
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
1033
|
+
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
1034
|
+
# Record objects are not instantiated, so the object's callbacks are not
|
1035
|
+
# executed, including any <tt>:dependent</tt> association options.
|
1036
|
+
#
|
1037
|
+
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
1038
|
+
#
|
1039
|
+
# Note: Although it is often much faster than the alternative, #destroy,
|
1040
|
+
# skipping callbacks might bypass business logic in your application
|
1041
|
+
# that ensures referential integrity or performs other essential jobs.
|
1042
|
+
#
|
1043
|
+
# ==== Examples
|
1044
|
+
#
|
1045
|
+
# # Delete a single row
|
1046
|
+
# Todo.delete(1)
|
1047
|
+
#
|
1048
|
+
# # Delete multiple rows
|
1049
|
+
# Todo.delete([2,3,4])
|
1050
|
+
def delete(id_or_array)
|
1051
|
+
return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
|
1052
|
+
|
1053
|
+
where(model.primary_key => id_or_array).delete_all
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
|
1057
|
+
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
1058
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
1059
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
1060
|
+
#
|
1061
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
1062
|
+
# from the attributes, and then calls destroy on it.
|
1063
|
+
#
|
1064
|
+
# ==== Parameters
|
1065
|
+
#
|
1066
|
+
# * +id+ - This should be the id or an array of ids to be destroyed.
|
1067
|
+
#
|
1068
|
+
# ==== Examples
|
1069
|
+
#
|
1070
|
+
# # Destroy a single object
|
1071
|
+
# Todo.destroy(1)
|
1072
|
+
#
|
1073
|
+
# # Destroy multiple objects
|
1074
|
+
# todos = [1,2,3]
|
1075
|
+
# Todo.destroy(todos)
|
1076
|
+
def destroy(id)
|
1077
|
+
multiple_ids = if model.composite_primary_key?
|
1078
|
+
id.first.is_a?(Array)
|
1079
|
+
else
|
1080
|
+
id.is_a?(Array)
|
1081
|
+
end
|
593
1082
|
|
594
|
-
|
1083
|
+
if multiple_ids
|
1084
|
+
find(id).each(&:destroy)
|
1085
|
+
else
|
1086
|
+
find(id).destroy
|
1087
|
+
end
|
595
1088
|
end
|
596
1089
|
|
597
1090
|
# Finds and destroys all records matching the specified conditions.
|
@@ -620,6 +1113,49 @@ module ActiveRecord
|
|
620
1113
|
where(*args).delete_all
|
621
1114
|
end
|
622
1115
|
|
1116
|
+
# Schedule the query to be performed from a background thread pool.
|
1117
|
+
#
|
1118
|
+
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
1119
|
+
#
|
1120
|
+
# When the +Relation+ is iterated, if the background query wasn't executed yet,
|
1121
|
+
# it will be performed by the foreground thread.
|
1122
|
+
#
|
1123
|
+
# Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
|
1124
|
+
# for queries to actually be executed concurrently. Otherwise it defaults to
|
1125
|
+
# executing them in the foreground.
|
1126
|
+
#
|
1127
|
+
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
1128
|
+
# fixtures are enabled.
|
1129
|
+
#
|
1130
|
+
# If the query was actually executed in the background, the Active Record logs will show
|
1131
|
+
# it by prefixing the log line with <tt>ASYNC</tt>:
|
1132
|
+
#
|
1133
|
+
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
1134
|
+
def load_async
|
1135
|
+
with_connection do |c|
|
1136
|
+
return load if !c.async_enabled?
|
1137
|
+
|
1138
|
+
unless loaded?
|
1139
|
+
result = exec_main_query(async: c.current_transaction.closed?)
|
1140
|
+
|
1141
|
+
if result.is_a?(Array)
|
1142
|
+
@records = result
|
1143
|
+
else
|
1144
|
+
@future_result = result
|
1145
|
+
end
|
1146
|
+
@loaded = true
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
self
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
# Returns <tt>true</tt> if the relation was scheduled on the background
|
1154
|
+
# thread pool.
|
1155
|
+
def scheduled?
|
1156
|
+
!!@future_result
|
1157
|
+
end
|
1158
|
+
|
623
1159
|
# Causes the records to be loaded from the database if they have not
|
624
1160
|
# been loaded already. You can use this if for some reason you need
|
625
1161
|
# to explicitly load some records before actually using them. The
|
@@ -627,7 +1163,7 @@ module ActiveRecord
|
|
627
1163
|
#
|
628
1164
|
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
629
1165
|
def load(&block)
|
630
|
-
|
1166
|
+
if !loaded? || scheduled?
|
631
1167
|
@records = exec_queries(&block)
|
632
1168
|
@loaded = true
|
633
1169
|
end
|
@@ -642,27 +1178,29 @@ module ActiveRecord
|
|
642
1178
|
end
|
643
1179
|
|
644
1180
|
def reset
|
1181
|
+
@future_result&.cancel
|
1182
|
+
@future_result = nil
|
645
1183
|
@delegate_to_klass = false
|
646
1184
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
647
1185
|
@offsets = @take = nil
|
648
1186
|
@cache_keys = nil
|
649
|
-
@
|
1187
|
+
@cache_versions = nil
|
1188
|
+
@records = nil
|
650
1189
|
self
|
651
1190
|
end
|
652
1191
|
|
653
1192
|
# Returns sql statement for the relation.
|
654
1193
|
#
|
655
1194
|
# User.where(name: 'Oscar').to_sql
|
656
|
-
# #
|
1195
|
+
# # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
657
1196
|
def to_sql
|
658
|
-
@to_sql ||=
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
conn = klass.connection
|
1197
|
+
@to_sql ||= if eager_loading?
|
1198
|
+
apply_join_dependency do |relation, join_dependency|
|
1199
|
+
relation = join_dependency.apply_column_aliases(relation)
|
1200
|
+
relation.to_sql
|
1201
|
+
end
|
1202
|
+
else
|
1203
|
+
klass.with_connection do |conn|
|
666
1204
|
conn.unprepared_statement { conn.to_sql(arel) }
|
667
1205
|
end
|
668
1206
|
end
|
@@ -672,7 +1210,7 @@ module ActiveRecord
|
|
672
1210
|
#
|
673
1211
|
# User.where(name: 'Oscar').where_values_hash
|
674
1212
|
# # => {name: "Oscar"}
|
675
|
-
def where_values_hash(relation_table_name = klass.table_name)
|
1213
|
+
def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
|
676
1214
|
where_clause.to_h(relation_table_name)
|
677
1215
|
end
|
678
1216
|
|
@@ -692,7 +1230,7 @@ module ActiveRecord
|
|
692
1230
|
# Joins that are also marked for preloading. In which case we should just eager load them.
|
693
1231
|
# Note that this is a naive implementation because we could have strings and symbols which
|
694
1232
|
# represent the same association, but that aren't matched by this. Also, we could have
|
695
|
-
# nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
|
1233
|
+
# nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
|
696
1234
|
def joined_includes_values
|
697
1235
|
includes_values & joins_values
|
698
1236
|
end
|
@@ -709,8 +1247,13 @@ module ActiveRecord
|
|
709
1247
|
end
|
710
1248
|
end
|
711
1249
|
|
712
|
-
def pretty_print(
|
713
|
-
|
1250
|
+
def pretty_print(pp)
|
1251
|
+
subject = loaded? ? records : annotate("loading for pp")
|
1252
|
+
entries = subject.take([limit_value, 11].compact.min)
|
1253
|
+
|
1254
|
+
entries[10] = "..." if entries.size == 11
|
1255
|
+
|
1256
|
+
pp.pp(entries)
|
714
1257
|
end
|
715
1258
|
|
716
1259
|
# Returns true if relation is blank.
|
@@ -722,6 +1265,10 @@ module ActiveRecord
|
|
722
1265
|
@values.dup
|
723
1266
|
end
|
724
1267
|
|
1268
|
+
def values_for_queries # :nodoc:
|
1269
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
1270
|
+
end
|
1271
|
+
|
725
1272
|
def inspect
|
726
1273
|
subject = loaded? ? records : annotate("loading for inspect")
|
727
1274
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
@@ -740,7 +1287,7 @@ module ActiveRecord
|
|
740
1287
|
end
|
741
1288
|
|
742
1289
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
743
|
-
ActiveRecord::Associations::AliasTracker.create(
|
1290
|
+
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
744
1291
|
end
|
745
1292
|
|
746
1293
|
class StrictLoadingScope # :nodoc:
|
@@ -756,11 +1303,9 @@ module ActiveRecord
|
|
756
1303
|
def preload_associations(records) # :nodoc:
|
757
1304
|
preload = preload_values
|
758
1305
|
preload += includes_values unless eager_loading?
|
759
|
-
preloader = nil
|
760
1306
|
scope = strict_loading_value ? StrictLoadingScope : nil
|
761
1307
|
preload.each do |associations|
|
762
|
-
|
763
|
-
preloader.preload records, associations, scope
|
1308
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
764
1309
|
end
|
765
1310
|
end
|
766
1311
|
|
@@ -770,13 +1315,13 @@ module ActiveRecord
|
|
770
1315
|
@loaded = true
|
771
1316
|
end
|
772
1317
|
|
773
|
-
|
774
|
-
|
1318
|
+
private
|
1319
|
+
def already_in_scope?(registry)
|
1320
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
775
1321
|
end
|
776
1322
|
|
777
|
-
|
778
|
-
|
779
|
-
@delegate_to_klass && klass.current_scope(true)
|
1323
|
+
def global_scope?(registry)
|
1324
|
+
registry.global_current_scope(klass, true)
|
780
1325
|
end
|
781
1326
|
|
782
1327
|
def current_scope_restoring_block(&block)
|
@@ -799,17 +1344,30 @@ module ActiveRecord
|
|
799
1344
|
klass.create!(attributes, &block)
|
800
1345
|
end
|
801
1346
|
|
802
|
-
def _scoping(scope)
|
803
|
-
previous
|
1347
|
+
def _scoping(scope, registry, all_queries = false)
|
1348
|
+
previous = registry.current_scope(klass, true)
|
1349
|
+
registry.set_current_scope(klass, scope)
|
1350
|
+
|
1351
|
+
if all_queries
|
1352
|
+
previous_global = registry.global_current_scope(klass, true)
|
1353
|
+
registry.set_global_current_scope(klass, scope)
|
1354
|
+
end
|
804
1355
|
yield
|
805
1356
|
ensure
|
806
|
-
klass
|
1357
|
+
registry.set_current_scope(klass, previous)
|
1358
|
+
if all_queries
|
1359
|
+
registry.set_global_current_scope(klass, previous_global)
|
1360
|
+
end
|
807
1361
|
end
|
808
1362
|
|
809
1363
|
def _substitute_values(values)
|
810
1364
|
values.map do |name, value|
|
811
1365
|
attr = table[name]
|
812
|
-
|
1366
|
+
if Arel.arel_node?(value)
|
1367
|
+
if value.is_a?(Arel::Nodes::SqlLiteral)
|
1368
|
+
value = Arel::Nodes::Grouping.new(value)
|
1369
|
+
end
|
1370
|
+
else
|
813
1371
|
type = klass.type_for_attribute(attr.name)
|
814
1372
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
815
1373
|
end
|
@@ -826,46 +1384,75 @@ module ActiveRecord
|
|
826
1384
|
|
827
1385
|
def exec_queries(&block)
|
828
1386
|
skip_query_cache_if_necessary do
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
1387
|
+
rows = if scheduled?
|
1388
|
+
future = @future_result
|
1389
|
+
@future_result = nil
|
1390
|
+
future.result
|
1391
|
+
else
|
1392
|
+
exec_main_query
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
records = instantiate_records(rows, &block)
|
1396
|
+
preload_associations(records) unless skip_preloading_value
|
1397
|
+
|
1398
|
+
records.each(&:readonly!) if readonly_value
|
1399
|
+
records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
|
1400
|
+
|
1401
|
+
records
|
1402
|
+
end
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
def exec_main_query(async: false)
|
1406
|
+
if @none
|
1407
|
+
if async
|
1408
|
+
return FutureResult.wrap([])
|
1409
|
+
else
|
1410
|
+
return []
|
1411
|
+
end
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
skip_query_cache_if_necessary do
|
1415
|
+
if where_clause.contradiction?
|
1416
|
+
[].freeze
|
1417
|
+
elsif eager_loading?
|
1418
|
+
klass.with_connection do |c|
|
833
1419
|
apply_join_dependency do |relation, join_dependency|
|
834
1420
|
if relation.null_relation?
|
835
|
-
[]
|
1421
|
+
[].freeze
|
836
1422
|
else
|
837
1423
|
relation = join_dependency.apply_column_aliases(relation)
|
838
|
-
|
839
|
-
|
840
|
-
end
|
1424
|
+
@_join_dependency = join_dependency
|
1425
|
+
c.select_all(relation.arel, "SQL", async: async)
|
1426
|
+
end
|
841
1427
|
end
|
842
|
-
else
|
843
|
-
klass.find_by_sql(arel, &block).freeze
|
844
1428
|
end
|
1429
|
+
else
|
1430
|
+
klass.with_connection do |c|
|
1431
|
+
klass._query_by_sql(c, arel, async: async)
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
end
|
1435
|
+
end
|
845
1436
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
records.
|
850
|
-
|
1437
|
+
def instantiate_records(rows, &block)
|
1438
|
+
return [].freeze if rows.empty?
|
1439
|
+
if eager_loading?
|
1440
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
1441
|
+
@_join_dependency = nil
|
851
1442
|
records
|
1443
|
+
else
|
1444
|
+
klass._load_from_sql(rows, &block).freeze
|
852
1445
|
end
|
853
1446
|
end
|
854
1447
|
|
855
|
-
def skip_query_cache_if_necessary
|
1448
|
+
def skip_query_cache_if_necessary(&block)
|
856
1449
|
if skip_query_cache_value
|
857
|
-
uncached
|
858
|
-
yield
|
859
|
-
end
|
1450
|
+
uncached(&block)
|
860
1451
|
else
|
861
1452
|
yield
|
862
1453
|
end
|
863
1454
|
end
|
864
1455
|
|
865
|
-
def build_preloader
|
866
|
-
ActiveRecord::Associations::Preloader.new
|
867
|
-
end
|
868
|
-
|
869
1456
|
def references_eager_loaded_tables?
|
870
1457
|
joined_tables = build_joins([]).flat_map do |join|
|
871
1458
|
if join.is_a?(Arel::Nodes::StringJoin)
|
@@ -889,5 +1476,9 @@ module ActiveRecord
|
|
889
1476
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
890
1477
|
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
891
1478
|
end
|
1479
|
+
|
1480
|
+
def limited_count
|
1481
|
+
limit_value ? count : limit(2).count
|
1482
|
+
end
|
892
1483
|
end
|
893
1484
|
end
|