activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +9 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +55 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +162 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -20,8 +20,16 @@ module ActiveRecord
|
|
20
20
|
# Indicates whether to use a stable #cache_key method that is accompanied
|
21
21
|
# by a changing version in the #cache_version method.
|
22
22
|
#
|
23
|
-
# This is +
|
23
|
+
# This is +true+, by default on Rails 5.2 and above.
|
24
24
|
class_attribute :cache_versioning, instance_writer: false, default: false
|
25
|
+
|
26
|
+
##
|
27
|
+
# :singleton-method:
|
28
|
+
# Indicates whether to use a stable #cache_key method that is accompanied
|
29
|
+
# by a changing version in the #cache_version method on collections.
|
30
|
+
#
|
31
|
+
# This is +false+, by default until Rails 6.1.
|
32
|
+
class_attribute :collection_cache_versioning, instance_writer: false, default: false
|
25
33
|
end
|
26
34
|
|
27
35
|
# Returns a +String+, which Action Pack uses for constructing a URL to this
|
@@ -60,27 +68,18 @@ module ActiveRecord
|
|
60
68
|
# the cache key will also include a version.
|
61
69
|
#
|
62
70
|
# Product.cache_versioning = false
|
63
|
-
#
|
64
|
-
def cache_key
|
71
|
+
# Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
|
72
|
+
def cache_key
|
65
73
|
if new_record?
|
66
74
|
"#{model_name.cache_key}/new"
|
67
75
|
else
|
68
|
-
if cache_version
|
76
|
+
if cache_version
|
69
77
|
"#{model_name.cache_key}/#{id}"
|
70
78
|
else
|
71
|
-
timestamp =
|
72
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
73
|
-
Specifying a timestamp name for #cache_key has been deprecated in favor of
|
74
|
-
the explicit #cache_version method that can be overwritten.
|
75
|
-
MSG
|
76
|
-
|
77
|
-
max_updated_column_timestamp(timestamp_names)
|
78
|
-
else
|
79
|
-
max_updated_column_timestamp
|
80
|
-
end
|
79
|
+
timestamp = max_updated_column_timestamp
|
81
80
|
|
82
81
|
if timestamp
|
83
|
-
timestamp = timestamp.utc.
|
82
|
+
timestamp = timestamp.utc.to_fs(cache_timestamp_format)
|
84
83
|
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
85
84
|
else
|
86
85
|
"#{model_name.cache_key}/#{id}"
|
@@ -94,10 +93,20 @@ module ActiveRecord
|
|
94
93
|
# cache_version, but this method can be overwritten to return something else.
|
95
94
|
#
|
96
95
|
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
|
97
|
-
# +false
|
96
|
+
# +false+.
|
98
97
|
def cache_version
|
99
|
-
|
100
|
-
|
98
|
+
return unless cache_versioning
|
99
|
+
|
100
|
+
if has_attribute?("updated_at")
|
101
|
+
timestamp = updated_at_before_type_cast
|
102
|
+
if can_use_fast_cache_version?(timestamp)
|
103
|
+
raw_timestamp_to_cache_version(timestamp)
|
104
|
+
|
105
|
+
elsif timestamp = updated_at
|
106
|
+
timestamp.utc.to_fs(cache_timestamp_format)
|
107
|
+
end
|
108
|
+
elsif self.class.has_attribute?("updated_at")
|
109
|
+
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
101
110
|
end
|
102
111
|
end
|
103
112
|
|
@@ -150,6 +159,48 @@ module ActiveRecord
|
|
150
159
|
end
|
151
160
|
end
|
152
161
|
end
|
162
|
+
|
163
|
+
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
|
164
|
+
collection.send(:compute_cache_key, timestamp_column)
|
165
|
+
end
|
153
166
|
end
|
167
|
+
|
168
|
+
private
|
169
|
+
# Detects if the value before type cast
|
170
|
+
# can be used to generate a cache_version.
|
171
|
+
#
|
172
|
+
# The fast cache version only works with a
|
173
|
+
# string value directly from the database.
|
174
|
+
#
|
175
|
+
# We also must check if the timestamp format has been changed
|
176
|
+
# or if the timezone is not set to UTC then
|
177
|
+
# we cannot apply our transformations correctly.
|
178
|
+
def can_use_fast_cache_version?(timestamp)
|
179
|
+
timestamp.is_a?(String) &&
|
180
|
+
cache_timestamp_format == :usec &&
|
181
|
+
ActiveRecord.default_timezone == :utc &&
|
182
|
+
!updated_at_came_from_user?
|
183
|
+
end
|
184
|
+
|
185
|
+
# Converts a raw database string to `:usec`
|
186
|
+
# format.
|
187
|
+
#
|
188
|
+
# Example:
|
189
|
+
#
|
190
|
+
# timestamp = "2018-10-15 20:02:15.266505"
|
191
|
+
# raw_timestamp_to_cache_version(timestamp)
|
192
|
+
# # => "20181015200215266505"
|
193
|
+
#
|
194
|
+
# PostgreSQL truncates trailing zeros,
|
195
|
+
# https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
|
196
|
+
# to account for this we pad the output with zeros
|
197
|
+
def raw_timestamp_to_cache_version(timestamp)
|
198
|
+
key = timestamp.delete("- :.")
|
199
|
+
if key.length < 20
|
200
|
+
key.ljust(20, "0")
|
201
|
+
else
|
202
|
+
key
|
203
|
+
end
|
204
|
+
end
|
154
205
|
end
|
155
206
|
end
|
@@ -6,40 +6,55 @@ require "active_record/scoping/named"
|
|
6
6
|
module ActiveRecord
|
7
7
|
# This class is used to create a table that keeps track of values and keys such
|
8
8
|
# as which environment migrations were run in.
|
9
|
+
#
|
10
|
+
# This is enabled by default. To disable this functionality set
|
11
|
+
# `use_metadata_table` to false in your database configuration.
|
9
12
|
class InternalMetadata < ActiveRecord::Base # :nodoc:
|
13
|
+
self.record_timestamps = true
|
14
|
+
|
10
15
|
class << self
|
16
|
+
def enabled?
|
17
|
+
ActiveRecord::Base.connection.use_metadata_table?
|
18
|
+
end
|
19
|
+
|
11
20
|
def primary_key
|
12
21
|
"key"
|
13
22
|
end
|
14
23
|
|
15
24
|
def table_name
|
16
|
-
"#{table_name_prefix}#{
|
25
|
+
"#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
|
17
26
|
end
|
18
27
|
|
19
28
|
def []=(key, value)
|
20
|
-
|
29
|
+
return unless enabled?
|
30
|
+
|
31
|
+
find_or_initialize_by(key: key).update!(value: value)
|
21
32
|
end
|
22
33
|
|
23
34
|
def [](key)
|
24
|
-
|
25
|
-
end
|
35
|
+
return unless enabled?
|
26
36
|
|
27
|
-
|
28
|
-
connection.table_exists?(table_name)
|
37
|
+
where(key: key).pick(:value)
|
29
38
|
end
|
30
39
|
|
31
40
|
# Creates an internal metadata table with columns +key+ and +value+
|
32
41
|
def create_table
|
33
|
-
unless
|
34
|
-
key_options = connection.internal_string_options_for_primary_key
|
42
|
+
return unless enabled?
|
35
43
|
|
44
|
+
unless connection.table_exists?(table_name)
|
36
45
|
connection.create_table(table_name, id: false) do |t|
|
37
|
-
t.string :key,
|
46
|
+
t.string :key, **connection.internal_string_options_for_primary_key
|
38
47
|
t.string :value
|
39
48
|
t.timestamps
|
40
49
|
end
|
41
50
|
end
|
42
51
|
end
|
52
|
+
|
53
|
+
def drop_table
|
54
|
+
return unless enabled?
|
55
|
+
|
56
|
+
connection.drop_table table_name, if_exists: true
|
57
|
+
end
|
43
58
|
end
|
44
59
|
end
|
45
60
|
end
|
@@ -1,47 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
module LegacyYamlAdapter
|
5
|
-
def self.convert(
|
4
|
+
module LegacyYamlAdapter # :nodoc:
|
5
|
+
def self.convert(coder)
|
6
6
|
return coder unless coder.is_a?(Psych::Coder)
|
7
7
|
|
8
8
|
case coder["active_record_yaml_version"]
|
9
9
|
when 1, 2 then coder
|
10
10
|
else
|
11
|
-
|
12
|
-
Rails420.convert(klass, coder)
|
13
|
-
else
|
14
|
-
Rails41.convert(klass, coder)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Rails420
|
20
|
-
def self.convert(klass, coder)
|
21
|
-
attribute_set = coder["attributes"]
|
22
|
-
|
23
|
-
klass.attribute_names.each do |attr_name|
|
24
|
-
attribute = attribute_set[attr_name]
|
25
|
-
if attribute.type.is_a?(Delegator)
|
26
|
-
type_from_klass = klass.type_for_attribute(attr_name)
|
27
|
-
attribute_set[attr_name] = attribute.with_type(type_from_klass)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
coder
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
module Rails41
|
36
|
-
def self.convert(klass, coder)
|
37
|
-
attributes = klass.attributes_builder
|
38
|
-
.build_from_database(coder["attributes"])
|
39
|
-
new_record = coder["attributes"][klass.primary_key].blank?
|
40
|
-
|
41
|
-
{
|
42
|
-
"attributes" => attributes,
|
43
|
-
"new_record" => new_record,
|
44
|
-
}
|
11
|
+
raise("Active Record doesn't know how to load YAML with this format.")
|
45
12
|
end
|
46
13
|
end
|
47
14
|
end
|
@@ -56,12 +56,21 @@ module ActiveRecord
|
|
56
56
|
class_attribute :lock_optimistically, instance_writer: false, default: true
|
57
57
|
end
|
58
58
|
|
59
|
-
def locking_enabled?
|
59
|
+
def locking_enabled? # :nodoc:
|
60
60
|
self.class.locking_enabled?
|
61
61
|
end
|
62
62
|
|
63
|
+
def increment!(*, **) # :nodoc:
|
64
|
+
super.tap do
|
65
|
+
if locking_enabled?
|
66
|
+
self[self.class.locking_column] += 1
|
67
|
+
clear_attribute_change(self.class.locking_column)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
63
72
|
private
|
64
|
-
def _create_record(attribute_names = self.attribute_names
|
73
|
+
def _create_record(attribute_names = self.attribute_names)
|
65
74
|
if locking_enabled?
|
66
75
|
# We always want to persist the locking version, even if we don't detect
|
67
76
|
# a change from the default, since the database might have no default
|
@@ -71,9 +80,8 @@ module ActiveRecord
|
|
71
80
|
end
|
72
81
|
|
73
82
|
def _touch_row(attribute_names, time)
|
83
|
+
@_touch_attr_names << self.class.locking_column if locking_enabled?
|
74
84
|
super
|
75
|
-
ensure
|
76
|
-
clear_attribute_change(self.class.locking_column) if locking_enabled?
|
77
85
|
end
|
78
86
|
|
79
87
|
def _update_row(attribute_names, attempted_action = "update")
|
@@ -81,15 +89,19 @@ module ActiveRecord
|
|
81
89
|
|
82
90
|
begin
|
83
91
|
locking_column = self.class.locking_column
|
84
|
-
|
92
|
+
lock_attribute_was = @attributes[locking_column]
|
93
|
+
|
94
|
+
update_constraints = _primary_key_constraints_hash
|
95
|
+
update_constraints[locking_column] = _lock_value_for_database(locking_column)
|
96
|
+
|
97
|
+
attribute_names = attribute_names.dup if attribute_names.frozen?
|
85
98
|
attribute_names << locking_column
|
86
99
|
|
87
100
|
self[locking_column] += 1
|
88
101
|
|
89
102
|
affected_rows = self.class._update_record(
|
90
103
|
attributes_with_values(attribute_names),
|
91
|
-
|
92
|
-
locking_column => previous_lock_value
|
104
|
+
update_constraints
|
93
105
|
)
|
94
106
|
|
95
107
|
if affected_rows != 1
|
@@ -100,7 +112,7 @@ module ActiveRecord
|
|
100
112
|
|
101
113
|
# If something went wrong, revert the locking_column value.
|
102
114
|
rescue Exception
|
103
|
-
|
115
|
+
@attributes[locking_column] = lock_attribute_was
|
104
116
|
raise
|
105
117
|
end
|
106
118
|
end
|
@@ -110,10 +122,10 @@ module ActiveRecord
|
|
110
122
|
|
111
123
|
locking_column = self.class.locking_column
|
112
124
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
)
|
125
|
+
delete_constraints = _primary_key_constraints_hash
|
126
|
+
delete_constraints[locking_column] = _lock_value_for_database(locking_column)
|
127
|
+
|
128
|
+
affected_rows = self.class._delete_record(delete_constraints)
|
117
129
|
|
118
130
|
if affected_rows != 1
|
119
131
|
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
@@ -122,6 +134,14 @@ module ActiveRecord
|
|
122
134
|
affected_rows
|
123
135
|
end
|
124
136
|
|
137
|
+
def _lock_value_for_database(locking_column)
|
138
|
+
if will_save_change_to_attribute?(locking_column)
|
139
|
+
@attributes[locking_column].value_for_database
|
140
|
+
else
|
141
|
+
@attributes[locking_column].original_value_for_database
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
125
145
|
module ClassMethods
|
126
146
|
DEFAULT_LOCKING_COLUMN = "lock_version"
|
127
147
|
|
@@ -156,21 +176,12 @@ module ActiveRecord
|
|
156
176
|
super
|
157
177
|
end
|
158
178
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
163
|
-
# sub class being decorated. As such, changes to `lock_optimistically`, or
|
164
|
-
# `locking_column` would not be picked up.
|
165
|
-
def inherited(subclass)
|
166
|
-
subclass.class_eval do
|
167
|
-
is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
|
168
|
-
decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
|
169
|
-
LockingType.new(type)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
super
|
179
|
+
def define_attribute(name, cast_type, **) # :nodoc:
|
180
|
+
if lock_optimistically && name == locking_column
|
181
|
+
cast_type = LockingType.new(cast_type)
|
173
182
|
end
|
183
|
+
super
|
184
|
+
end
|
174
185
|
end
|
175
186
|
end
|
176
187
|
|
@@ -178,6 +189,10 @@ module ActiveRecord
|
|
178
189
|
# `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
|
179
190
|
# during update record.
|
180
191
|
class LockingType < DelegateClass(Type::Value) # :nodoc:
|
192
|
+
def self.new(subtype)
|
193
|
+
self === subtype ? subtype : super
|
194
|
+
end
|
195
|
+
|
181
196
|
def deserialize(value)
|
182
197
|
super.to_i
|
183
198
|
end
|
@@ -14,9 +14,9 @@ module ActiveRecord
|
|
14
14
|
# of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
|
15
15
|
#
|
16
16
|
# Account.transaction do
|
17
|
-
# # select * from accounts where name = 'shugo' limit 1 for update
|
18
|
-
# shugo = Account.
|
19
|
-
# yuko = Account.
|
17
|
+
# # select * from accounts where name = 'shugo' limit 1 for update nowait
|
18
|
+
# shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
|
19
|
+
# yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
|
20
20
|
# shugo.balance -= 100
|
21
21
|
# shugo.save!
|
22
22
|
# yuko.balance += 100
|
@@ -53,8 +53,12 @@ module ActiveRecord
|
|
53
53
|
# end
|
54
54
|
#
|
55
55
|
# Database-specific information on row locking:
|
56
|
-
#
|
57
|
-
#
|
56
|
+
#
|
57
|
+
# [MySQL]
|
58
|
+
# https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
|
59
|
+
#
|
60
|
+
# [PostgreSQL]
|
61
|
+
# https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
58
62
|
module Pessimistic
|
59
63
|
# Obtain a row lock on this record. Reloads the record to obtain the requested
|
60
64
|
# lock. Pass an SQL locking clause to append the end of the SELECT statement
|
@@ -77,9 +81,15 @@ module ActiveRecord
|
|
77
81
|
|
78
82
|
# Wraps the passed block in a transaction, locking the object
|
79
83
|
# before yielding. You can pass the SQL locking clause
|
80
|
-
# as argument (see <tt
|
81
|
-
|
82
|
-
|
84
|
+
# as an optional argument (see <tt>#lock!</tt>).
|
85
|
+
#
|
86
|
+
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
|
87
|
+
# and <tt>joinable:</tt> to the wrapping transaction (see
|
88
|
+
# <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
|
89
|
+
def with_lock(*args)
|
90
|
+
transaction_opts = args.extract_options!
|
91
|
+
lock = args.present? ? args.first : true
|
92
|
+
transaction(**transaction_opts) do
|
83
93
|
lock!(lock)
|
84
94
|
yield
|
85
95
|
end
|
@@ -4,6 +4,8 @@ module ActiveRecord
|
|
4
4
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
5
|
IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
|
6
6
|
|
7
|
+
class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
|
8
|
+
|
7
9
|
def self.runtime=(value)
|
8
10
|
ActiveRecord::RuntimeRegistry.sql_runtime = value
|
9
11
|
end
|
@@ -17,6 +19,16 @@ module ActiveRecord
|
|
17
19
|
rt
|
18
20
|
end
|
19
21
|
|
22
|
+
def strict_loading_violation(event)
|
23
|
+
debug do
|
24
|
+
owner = event.payload[:owner]
|
25
|
+
association = event.payload[:reflection].klass
|
26
|
+
name = event.payload[:reflection].name
|
27
|
+
|
28
|
+
color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
20
32
|
def sql(event)
|
21
33
|
self.class.runtime += event.duration
|
22
34
|
return unless logger.debug?
|
@@ -25,20 +37,31 @@ module ActiveRecord
|
|
25
37
|
|
26
38
|
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
27
39
|
|
28
|
-
name
|
40
|
+
name = if payload[:async]
|
41
|
+
"ASYNC #{payload[:name]} (#{payload[:lock_wait].round(1)}ms) (db time #{event.duration.round(1)}ms)"
|
42
|
+
else
|
43
|
+
"#{payload[:name]} (#{event.duration.round(1)}ms)"
|
44
|
+
end
|
29
45
|
name = "CACHE #{name}" if payload[:cached]
|
30
46
|
sql = payload[:sql]
|
31
47
|
binds = nil
|
32
48
|
|
33
|
-
|
49
|
+
if payload[:binds]&.any?
|
34
50
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
35
|
-
|
36
|
-
|
37
|
-
|
51
|
+
|
52
|
+
binds = []
|
53
|
+
payload[:binds].each_with_index do |attr, i|
|
54
|
+
attribute_name = attr.respond_to?(:name) ? attr.name : attr[i].name
|
55
|
+
filtered_params = filter(attribute_name, casted_params[i])
|
56
|
+
|
57
|
+
binds << render_bind(attr, filtered_params)
|
58
|
+
end
|
59
|
+
binds = binds.inspect
|
60
|
+
binds.prepend(" ")
|
38
61
|
end
|
39
62
|
|
40
63
|
name = colorize_payload_name(name, payload[:name])
|
41
|
-
sql = color(sql, sql_color(sql), true)
|
64
|
+
sql = color(sql, sql_color(sql), true) if colorize_logging
|
42
65
|
|
43
66
|
debug " #{name} #{sql}#{binds}"
|
44
67
|
end
|
@@ -49,13 +72,18 @@ module ActiveRecord
|
|
49
72
|
end
|
50
73
|
|
51
74
|
def render_bind(attr, value)
|
52
|
-
|
75
|
+
case attr
|
76
|
+
when ActiveModel::Attribute
|
77
|
+
if attr.type.binary? && attr.value
|
78
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
79
|
+
end
|
80
|
+
when Array
|
53
81
|
attr = attr.first
|
54
|
-
|
55
|
-
|
82
|
+
else
|
83
|
+
attr = nil
|
56
84
|
end
|
57
85
|
|
58
|
-
[attr
|
86
|
+
[attr&.name, value]
|
59
87
|
end
|
60
88
|
|
61
89
|
def colorize_payload_name(name, payload_name)
|
@@ -94,42 +122,25 @@ module ActiveRecord
|
|
94
122
|
def debug(progname = nil, &block)
|
95
123
|
return unless super
|
96
124
|
|
97
|
-
if ActiveRecord
|
125
|
+
if ActiveRecord.verbose_query_logs
|
98
126
|
log_query_source
|
99
127
|
end
|
100
128
|
end
|
101
129
|
|
102
130
|
def log_query_source
|
103
|
-
|
104
|
-
|
105
|
-
if source_line
|
106
|
-
if defined?(::Rails.root)
|
107
|
-
app_root = "#{::Rails.root.to_s}/".freeze
|
108
|
-
source_line = source_line.sub(app_root, "")
|
109
|
-
end
|
131
|
+
source = extract_query_source_location(caller)
|
110
132
|
|
111
|
-
|
133
|
+
if source
|
134
|
+
logger.debug(" ↳ #{source}")
|
112
135
|
end
|
113
136
|
end
|
114
137
|
|
115
|
-
def
|
116
|
-
|
117
|
-
frame.absolute_path && !ignored_callstack(frame.absolute_path)
|
118
|
-
end
|
119
|
-
|
120
|
-
offending_line = line || callstack.first
|
121
|
-
|
122
|
-
[
|
123
|
-
offending_line.path,
|
124
|
-
offending_line.lineno
|
125
|
-
]
|
138
|
+
def extract_query_source_location(locations)
|
139
|
+
backtrace_cleaner.clean(locations.lazy).first
|
126
140
|
end
|
127
141
|
|
128
|
-
|
129
|
-
|
130
|
-
def ignored_callstack(path)
|
131
|
-
path.start_with?(RAILS_GEM_ROOT) ||
|
132
|
-
path.start_with?(RbConfig::CONFIG["rubylibdir"])
|
142
|
+
def filter(name, value)
|
143
|
+
ActiveRecord::Base.inspection_filter.filter_param(name, value)
|
133
144
|
end
|
134
145
|
end
|
135
146
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Middleware
|
5
|
+
class DatabaseSelector
|
6
|
+
class Resolver
|
7
|
+
# The session class is used by the DatabaseSelector::Resolver to save
|
8
|
+
# timestamps of the last write in the session.
|
9
|
+
#
|
10
|
+
# The last_write is used to determine whether it's safe to read
|
11
|
+
# from the replica or the request needs to be sent to the primary.
|
12
|
+
class Session # :nodoc:
|
13
|
+
def self.call(request)
|
14
|
+
new(request.session)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Converts time to a timestamp that represents milliseconds since
|
18
|
+
# epoch.
|
19
|
+
def self.convert_time_to_timestamp(time)
|
20
|
+
time.to_i * 1000 + time.usec / 1000
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts milliseconds since epoch timestamp into a time object.
|
24
|
+
def self.convert_timestamp_to_time(timestamp)
|
25
|
+
timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(session)
|
29
|
+
@session = session
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :session
|
33
|
+
|
34
|
+
def last_write_timestamp
|
35
|
+
self.class.convert_timestamp_to_time(session[:last_write])
|
36
|
+
end
|
37
|
+
|
38
|
+
def update_last_write_timestamp
|
39
|
+
session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
|
40
|
+
end
|
41
|
+
|
42
|
+
def save(response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|