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
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Promise < BasicObject
|
5
|
+
undef_method :==, :!, :!=
|
6
|
+
|
7
|
+
def initialize(future_result, block) # :nodoc:
|
8
|
+
@future_result = future_result
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns whether the associated query is still being executed or not.
|
13
|
+
def pending?
|
14
|
+
@future_result.pending?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the query result.
|
18
|
+
# If the query wasn't completed yet, accessing +#value+ will block until the query completes.
|
19
|
+
# If the query failed, +#value+ will raise the corresponding error.
|
20
|
+
def value
|
21
|
+
return @value if defined? @value
|
22
|
+
|
23
|
+
result = @future_result.result
|
24
|
+
@value = if @block
|
25
|
+
@block.call(result)
|
26
|
+
else
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a new +ActiveRecord::Promise+ that will apply the passed block
|
32
|
+
# when the value is accessed:
|
33
|
+
#
|
34
|
+
# Post.async_pick(:title).then { |title| title.upcase }.value
|
35
|
+
# # => "POST TITLE"
|
36
|
+
def then(&block)
|
37
|
+
Promise.new(@future_result, @block ? @block >> block : block)
|
38
|
+
end
|
39
|
+
|
40
|
+
[:class, :respond_to?, :is_a?].each do |method|
|
41
|
+
define_method(method, ::Object.instance_method(method))
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect # :nodoc:
|
45
|
+
"#<ActiveRecord::Promise status=#{status}>"
|
46
|
+
end
|
47
|
+
|
48
|
+
def pretty_print(q) # :nodoc:
|
49
|
+
q.text(inspect)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def status
|
54
|
+
if @future_result.pending?
|
55
|
+
:pending
|
56
|
+
elsif @future_result.canceled?
|
57
|
+
:canceled
|
58
|
+
else
|
59
|
+
:complete
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Complete < self # :nodoc:
|
64
|
+
attr_reader :value
|
65
|
+
|
66
|
+
def initialize(value)
|
67
|
+
@value = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def then
|
71
|
+
Complete.new(yield @value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def pending?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def status
|
80
|
+
:complete
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -8,7 +8,13 @@ module ActiveRecord
|
|
8
8
|
# If it's not, it will execute the given block.
|
9
9
|
def cache(&block)
|
10
10
|
if connected? || !configurations.empty?
|
11
|
-
|
11
|
+
pool = connection_pool
|
12
|
+
was_enabled = pool.query_cache_enabled
|
13
|
+
begin
|
14
|
+
pool.enable_query_cache(&block)
|
15
|
+
ensure
|
16
|
+
pool.clear_query_cache unless was_enabled
|
17
|
+
end
|
12
18
|
else
|
13
19
|
yield
|
14
20
|
end
|
@@ -16,9 +22,12 @@ module ActiveRecord
|
|
16
22
|
|
17
23
|
# Disable the query cache within the block if Active Record is configured.
|
18
24
|
# If it's not, it will execute the given block.
|
19
|
-
|
25
|
+
#
|
26
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
|
27
|
+
# (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
|
28
|
+
def uncached(dirties: true, &block)
|
20
29
|
if connected? || !configurations.empty?
|
21
|
-
|
30
|
+
connection_pool.disable_query_cache(dirties: dirties, &block)
|
22
31
|
else
|
23
32
|
yield
|
24
33
|
end
|
@@ -26,32 +35,16 @@ module ActiveRecord
|
|
26
35
|
end
|
27
36
|
|
28
37
|
def self.run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
ActiveRecord::Base.connection_handlers.each do |key, handler|
|
33
|
-
pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
34
|
-
end
|
35
|
-
else
|
36
|
-
pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
38
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
|
39
|
+
next if pool.db_config&.query_cache == false
|
40
|
+
pool.enable_query_cache!
|
37
41
|
end
|
38
|
-
|
39
|
-
pools
|
40
42
|
end
|
41
43
|
|
42
44
|
def self.complete(pools)
|
43
|
-
pools.each
|
44
|
-
|
45
|
-
|
46
|
-
ActiveRecord::Base.connection_handlers.each do |_, handler|
|
47
|
-
handler.connection_pool_list.each do |pool|
|
48
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
49
|
-
end
|
50
|
-
end
|
51
|
-
else
|
52
|
-
ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
|
53
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
54
|
-
end
|
45
|
+
pools.each do |pool|
|
46
|
+
pool.disable_query_cache!
|
47
|
+
pool.clear_query_cache
|
55
48
|
end
|
56
49
|
end
|
57
50
|
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
4
|
+
require "active_record/query_logs_formatter"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
# = Active Record Query Logs
|
8
|
+
#
|
9
|
+
# Automatically append comments to SQL queries with runtime information tags. This can be used to trace troublesome
|
10
|
+
# SQL statements back to the application code that generated these statements.
|
11
|
+
#
|
12
|
+
# Query logs can be enabled via \Rails configuration in <tt>config/application.rb</tt> or an initializer:
|
13
|
+
#
|
14
|
+
# config.active_record.query_log_tags_enabled = true
|
15
|
+
#
|
16
|
+
# By default the name of the application, the name and action of the controller, or the name of the job are logged.
|
17
|
+
# The default format is {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/].
|
18
|
+
# The tags shown in a query comment can be configured via \Rails configuration:
|
19
|
+
#
|
20
|
+
# config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
|
21
|
+
#
|
22
|
+
# Active Record defines default tags available for use:
|
23
|
+
#
|
24
|
+
# * +application+
|
25
|
+
# * +pid+
|
26
|
+
# * +socket+
|
27
|
+
# * +db_host+
|
28
|
+
# * +database+
|
29
|
+
# * +source_location+
|
30
|
+
#
|
31
|
+
# Action Controller adds default tags when loaded:
|
32
|
+
#
|
33
|
+
# * +controller+
|
34
|
+
# * +action+
|
35
|
+
# * +namespaced_controller+
|
36
|
+
#
|
37
|
+
# Active Job adds default tags when loaded:
|
38
|
+
#
|
39
|
+
# * +job+
|
40
|
+
#
|
41
|
+
# New comment tags can be defined by adding them in a +Hash+ to the tags +Array+. Tags can have dynamic content by
|
42
|
+
# setting a +Proc+ or lambda value in the +Hash+, and can reference any value stored by \Rails in the +context+ object.
|
43
|
+
# ActiveSupport::CurrentAttributes can be used to store application values. Tags with +nil+ values are
|
44
|
+
# omitted from the query comment.
|
45
|
+
#
|
46
|
+
# Escaping is performed on the string returned, however untrusted user input should not be used.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# config.active_record.query_log_tags = [
|
51
|
+
# :namespaced_controller,
|
52
|
+
# :action,
|
53
|
+
# :job,
|
54
|
+
# {
|
55
|
+
# request_id: ->(context) { context[:controller]&.request&.request_id },
|
56
|
+
# job_id: ->(context) { context[:job]&.job_id },
|
57
|
+
# tenant_id: -> { Current.tenant&.id },
|
58
|
+
# static: "value",
|
59
|
+
# },
|
60
|
+
# ]
|
61
|
+
#
|
62
|
+
# By default the name of the application, the name and action of the controller, or the name of the job are logged
|
63
|
+
# using the {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/] format. This can be changed
|
64
|
+
# via {config.active_record.query_log_tags_format}[https://guides.rubyonrails.org/configuring.html#config-active-record-query-log-tags-format]
|
65
|
+
#
|
66
|
+
# Tag comments can be prepended to the query:
|
67
|
+
#
|
68
|
+
# ActiveRecord::QueryLogs.prepend_comment = true
|
69
|
+
#
|
70
|
+
# For applications where the content will not change during the lifetime of
|
71
|
+
# the request or job execution, the tags can be cached for reuse in every query:
|
72
|
+
#
|
73
|
+
# config.active_record.cache_query_log_tags = true
|
74
|
+
module QueryLogs
|
75
|
+
mattr_accessor :taggings, instance_accessor: false, default: {}
|
76
|
+
mattr_accessor :tags, instance_accessor: false, default: [ :application ]
|
77
|
+
mattr_accessor :prepend_comment, instance_accessor: false, default: false
|
78
|
+
mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
|
79
|
+
mattr_accessor :tags_formatter, instance_accessor: false
|
80
|
+
thread_mattr_accessor :cached_comment, instance_accessor: false
|
81
|
+
|
82
|
+
class << self
|
83
|
+
def call(sql, connection) # :nodoc:
|
84
|
+
comment = self.comment(connection)
|
85
|
+
|
86
|
+
if comment.blank?
|
87
|
+
sql
|
88
|
+
elsif prepend_comment
|
89
|
+
"#{comment} #{sql}"
|
90
|
+
else
|
91
|
+
"#{sql} #{comment}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def clear_cache # :nodoc:
|
96
|
+
self.cached_comment = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# Updates the formatter to be what the passed in format is.
|
100
|
+
def update_formatter(format)
|
101
|
+
self.tags_formatter =
|
102
|
+
case format
|
103
|
+
when :legacy
|
104
|
+
LegacyFormatter.new
|
105
|
+
when :sqlcommenter
|
106
|
+
SQLCommenter.new
|
107
|
+
else
|
108
|
+
raise ArgumentError, "Formatter is unsupported: #{formatter}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if Thread.respond_to?(:each_caller_location)
|
113
|
+
def query_source_location # :nodoc:
|
114
|
+
Thread.each_caller_location do |location|
|
115
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
116
|
+
return frame if frame
|
117
|
+
end
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
else
|
121
|
+
def query_source_location # :nodoc:
|
122
|
+
LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
127
|
+
|
128
|
+
private
|
129
|
+
# Returns an SQL comment +String+ containing the query log tags.
|
130
|
+
# Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
|
131
|
+
def comment(connection)
|
132
|
+
if cache_query_log_tags
|
133
|
+
self.cached_comment ||= uncached_comment(connection)
|
134
|
+
else
|
135
|
+
uncached_comment(connection)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def formatter
|
140
|
+
self.tags_formatter || self.update_formatter(:legacy)
|
141
|
+
end
|
142
|
+
|
143
|
+
def uncached_comment(connection)
|
144
|
+
content = tag_content(connection)
|
145
|
+
|
146
|
+
if content.present?
|
147
|
+
"/*#{escape_sql_comment(content)}*/"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def escape_sql_comment(content)
|
152
|
+
# Sanitize a string to appear within a SQL comment
|
153
|
+
# For compatibility, this also surrounding "/*+", "/*", and "*/"
|
154
|
+
# characters, possibly with single surrounding space.
|
155
|
+
# Then follows that by replacing any internal "*/" or "/ *" with
|
156
|
+
# "* /" or "/ *"
|
157
|
+
comment = content.to_s.dup
|
158
|
+
comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
|
159
|
+
comment.gsub!("*/", "* /")
|
160
|
+
comment.gsub!("/*", "/ *")
|
161
|
+
comment
|
162
|
+
end
|
163
|
+
|
164
|
+
def tag_content(connection)
|
165
|
+
context = ActiveSupport::ExecutionContext.to_h
|
166
|
+
context[:connection] ||= connection
|
167
|
+
|
168
|
+
pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
|
169
|
+
key, handler = tag
|
170
|
+
handler ||= taggings[key]
|
171
|
+
|
172
|
+
val = if handler.nil?
|
173
|
+
context[key]
|
174
|
+
elsif handler.respond_to?(:call)
|
175
|
+
if handler.arity == 0
|
176
|
+
handler.call
|
177
|
+
else
|
178
|
+
handler.call(context)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
handler
|
182
|
+
end
|
183
|
+
[key, val] unless val.nil?
|
184
|
+
end
|
185
|
+
self.formatter.format(pairs)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module QueryLogs
|
5
|
+
class LegacyFormatter # :nodoc:
|
6
|
+
def initialize
|
7
|
+
@key_value_separator = ":"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Formats the key value pairs into a string.
|
11
|
+
def format(pairs)
|
12
|
+
pairs.map! do |key, value|
|
13
|
+
"#{key}#{key_value_separator}#{format_value(value)}"
|
14
|
+
end.join(",")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
attr_reader :key_value_separator
|
19
|
+
|
20
|
+
def format_value(value)
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class SQLCommenter < LegacyFormatter # :nodoc:
|
26
|
+
def initialize
|
27
|
+
@key_value_separator = "="
|
28
|
+
end
|
29
|
+
|
30
|
+
def format(pairs)
|
31
|
+
pairs.sort_by! { |pair| pair.first.to_s }
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def format_value(value)
|
37
|
+
"'#{ERB::Util.url_encode(value)}'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,21 +3,23 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Querying
|
5
5
|
QUERYING_METHODS = [
|
6
|
-
:find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
|
6
|
+
:find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
|
7
7
|
:second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
|
8
8
|
:forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
|
9
9
|
:exists?, :any?, :many?, :none?, :one?,
|
10
10
|
:first_or_create, :first_or_create!, :first_or_initialize,
|
11
11
|
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
12
12
|
:create_or_find_by, :create_or_find_by!,
|
13
|
-
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
13
|
+
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
14
14
|
:find_each, :find_in_batches, :in_batches,
|
15
|
-
:select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
|
-
:where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
15
|
+
:select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
|
+
:where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
17
17
|
:and, :or, :annotate, :optimizer_hints, :extending,
|
18
18
|
:having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
|
19
19
|
:count, :average, :minimum, :maximum, :sum, :calculate,
|
20
|
-
:pluck, :pick, :ids, :strict_loading
|
20
|
+
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
|
21
|
+
:async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
|
22
|
+
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
21
23
|
].freeze # :nodoc:
|
22
24
|
delegate(*QUERYING_METHODS, to: :all)
|
23
25
|
|
@@ -39,12 +41,36 @@ module ActiveRecord
|
|
39
41
|
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
|
40
42
|
# # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "author"=>"Quentin"}>, ...]
|
41
43
|
#
|
42
|
-
# You can use the same string replacement techniques as you can with
|
44
|
+
# You can use the same string replacement techniques as you can with ActiveRecord::QueryMethods#where :
|
43
45
|
#
|
44
46
|
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
|
45
47
|
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
|
46
|
-
|
47
|
-
|
48
|
+
#
|
49
|
+
# Note that building your own SQL query string from user input may expose your application to
|
50
|
+
# injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
|
51
|
+
def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
52
|
+
result = with_connection do |c|
|
53
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
|
54
|
+
end
|
55
|
+
_load_from_sql(result, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
59
|
+
def async_find_by_sql(sql, binds = [], preparable: nil, &block)
|
60
|
+
result = with_connection do |c|
|
61
|
+
_query_by_sql(c, sql, binds, preparable: preparable, async: true)
|
62
|
+
end
|
63
|
+
|
64
|
+
result.then do |result|
|
65
|
+
_load_from_sql(result, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
70
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
71
|
+
end
|
72
|
+
|
73
|
+
def _load_from_sql(result_set, &block) # :nodoc:
|
48
74
|
column_types = result_set.column_types
|
49
75
|
|
50
76
|
unless column_types.empty?
|
@@ -81,7 +107,16 @@ module ActiveRecord
|
|
81
107
|
#
|
82
108
|
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
|
83
109
|
def count_by_sql(sql)
|
84
|
-
|
110
|
+
with_connection do |c|
|
111
|
+
c.select_value(sanitize_sql(sql), "#{name} Count").to_i
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
116
|
+
def async_count_by_sql(sql)
|
117
|
+
with_connection do |c|
|
118
|
+
c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
|
119
|
+
end
|
85
120
|
end
|
86
121
|
end
|
87
122
|
end
|