activerecord 6.1.7 → 7.1.5
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 +2030 -1020
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +51 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +39 -35
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- 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 +3 -2
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/preloader/association.rb +210 -52
- 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 +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +446 -306
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +27 -12
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +161 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +65 -31
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- 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 +367 -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 +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- 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 +367 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
- 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 +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- 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 +39 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- 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 +89 -52
- 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/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.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- 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 +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
- 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 +258 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +108 -137
- data/lib/active_record/core.rb +242 -233
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +88 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +66 -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 +1 -1
- 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 +155 -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 +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_serializer.rb +92 -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 +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +172 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +147 -86
- data/lib/active_record/future_result.rb +174 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +37 -22
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- 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 +112 -14
- data/lib/active_record/migration/compatibility.rb +233 -46
- data/lib/active_record/migration/default_strategy.rb +23 -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 +361 -173
- data/lib/active_record/model_schema.rb +125 -101
- data/lib/active_record/nested_attributes.rb +50 -20
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +409 -88
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +220 -44
- data/lib/active_record/railties/controller_runtime.rb +15 -10
- data/lib/active_record/railties/databases.rake +188 -252
- 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 +248 -81
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +246 -90
- data/lib/active_record/relation/delegation.rb +28 -14
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- 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 +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +670 -129
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +287 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +32 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -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 +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +251 -140
- 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 +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +117 -96
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- 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 -5
- 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/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- 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 +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- 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/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -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/node.rb +111 -2
- 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 +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +5 -13
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +141 -20
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- 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.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 +96 -16
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module MessagePack # :nodoc:
|
5
|
+
FORMAT_VERSION = 1
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def dump(input)
|
9
|
+
encoder = Encoder.new
|
10
|
+
[FORMAT_VERSION, encoder.encode(input), encoder.entries]
|
11
|
+
end
|
12
|
+
|
13
|
+
def load(dumped)
|
14
|
+
format_version, top_level, entries = dumped
|
15
|
+
unless format_version == FORMAT_VERSION
|
16
|
+
raise "Invalid format version: #{format_version.inspect}"
|
17
|
+
end
|
18
|
+
Decoder.new(entries).decode(top_level)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Extensions
|
23
|
+
extend self
|
24
|
+
|
25
|
+
def install(registry)
|
26
|
+
registry.register_type 119, ActiveModel::Type::Binary::Data,
|
27
|
+
packer: :to_s,
|
28
|
+
unpacker: :new
|
29
|
+
|
30
|
+
registry.register_type 120, ActiveRecord::Base,
|
31
|
+
packer: method(:write_record),
|
32
|
+
unpacker: method(:read_record),
|
33
|
+
recursive: true
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_record(record, packer)
|
37
|
+
packer.write(ActiveRecord::MessagePack.dump(record))
|
38
|
+
end
|
39
|
+
|
40
|
+
def read_record(unpacker)
|
41
|
+
ActiveRecord::MessagePack.load(unpacker.read)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Encoder
|
46
|
+
attr_reader :entries
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@entries = []
|
50
|
+
@refs = {}.compare_by_identity
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode(input)
|
54
|
+
if input.is_a?(Array)
|
55
|
+
input.map { |record| encode_record(record) }
|
56
|
+
elsif input
|
57
|
+
encode_record(input)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def encode_record(record)
|
62
|
+
ref = @refs[record]
|
63
|
+
|
64
|
+
if !ref
|
65
|
+
ref = @refs[record] = @entries.size
|
66
|
+
@entries << build_entry(record)
|
67
|
+
add_cached_associations(record, @entries.last)
|
68
|
+
end
|
69
|
+
|
70
|
+
ref
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_entry(record)
|
74
|
+
[
|
75
|
+
ActiveSupport::MessagePack::Extensions.dump_class(record.class),
|
76
|
+
record.attributes_for_database,
|
77
|
+
record.new_record?
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_cached_associations(record, entry)
|
82
|
+
record.class.reflections.each_value do |reflection|
|
83
|
+
if record.association_cached?(reflection.name) && record.association(reflection.name).loaded?
|
84
|
+
entry << reflection.name << encode(record.association(reflection.name).target)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Decoder
|
91
|
+
def initialize(entries)
|
92
|
+
@records = entries.map { |entry| build_record(entry) }
|
93
|
+
@records.zip(entries) { |record, entry| resolve_cached_associations(record, entry) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def decode(ref)
|
97
|
+
if ref.is_a?(Array)
|
98
|
+
ref.map { |r| @records[r] }
|
99
|
+
elsif ref
|
100
|
+
@records[ref]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_record(entry)
|
105
|
+
class_name, attributes_hash, is_new_record, * = entry
|
106
|
+
klass = ActiveSupport::MessagePack::Extensions.load_class(class_name)
|
107
|
+
attributes = klass.attributes_builder.build_from_database(attributes_hash)
|
108
|
+
klass.allocate.init_with_attributes(attributes, is_new_record)
|
109
|
+
end
|
110
|
+
|
111
|
+
def resolve_cached_associations(record, entry)
|
112
|
+
i = 3 # entry == [class_name, attributes_hash, is_new_record, *associations]
|
113
|
+
while i < entry.length
|
114
|
+
begin
|
115
|
+
record.association(entry[i]).target = decode(entry[i + 1])
|
116
|
+
rescue ActiveRecord::AssociationNotFoundError
|
117
|
+
# The association no longer exists, so just skip it.
|
118
|
+
end
|
119
|
+
i += 2
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -48,25 +48,25 @@ module ActiveRecord
|
|
48
48
|
context.save(response)
|
49
49
|
end
|
50
50
|
|
51
|
+
def reading_request?(request)
|
52
|
+
request.get? || request.head?
|
53
|
+
end
|
54
|
+
|
51
55
|
private
|
52
56
|
def read_from_primary(&blk)
|
53
|
-
ActiveRecord::Base.connected_to(role: ActiveRecord
|
54
|
-
instrumenter.instrument("database_selector.active_record.read_from_primary")
|
55
|
-
yield
|
56
|
-
end
|
57
|
+
ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: true) do
|
58
|
+
instrumenter.instrument("database_selector.active_record.read_from_primary", &blk)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
62
|
def read_from_replica(&blk)
|
61
|
-
ActiveRecord::Base.connected_to(role: ActiveRecord
|
62
|
-
instrumenter.instrument("database_selector.active_record.read_from_replica")
|
63
|
-
yield
|
64
|
-
end
|
63
|
+
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role, prevent_writes: true) do
|
64
|
+
instrumenter.instrument("database_selector.active_record.read_from_replica", &blk)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
def write_to_primary
|
69
|
-
ActiveRecord::Base.connected_to(role: ActiveRecord
|
68
|
+
def write_to_primary
|
69
|
+
ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: false) do
|
70
70
|
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
|
71
71
|
yield
|
72
72
|
ensure
|
@@ -4,8 +4,10 @@ require "active_record/middleware/database_selector/resolver"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Middleware
|
7
|
+
# = Database Selector \Middleware
|
8
|
+
#
|
7
9
|
# The DatabaseSelector Middleware provides a framework for automatically
|
8
|
-
# swapping from the primary to the replica database connection. Rails
|
10
|
+
# swapping from the primary to the replica database connection. \Rails
|
9
11
|
# provides a basic framework to determine when to swap and allows for
|
10
12
|
# applications to write custom strategy classes to override the default
|
11
13
|
# behavior.
|
@@ -15,18 +17,26 @@ module ActiveRecord
|
|
15
17
|
# resolver context class that sets a value that helps the resolver class
|
16
18
|
# decide when to switch.
|
17
19
|
#
|
18
|
-
# Rails default middleware uses the request's session to set a timestamp
|
20
|
+
# \Rails default middleware uses the request's session to set a timestamp
|
19
21
|
# that informs the application when to read from a primary or read from a
|
20
22
|
# replica.
|
21
23
|
#
|
22
|
-
# To use the DatabaseSelector in your application with default settings
|
23
|
-
# the
|
24
|
+
# To use the DatabaseSelector in your application with default settings,
|
25
|
+
# run the provided generator.
|
24
26
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
27
|
+
# $ bin/rails g active_record:multi_db
|
28
|
+
#
|
29
|
+
# This will create a file named +config/initializers/multi_db.rb+ with the
|
30
|
+
# following contents:
|
31
|
+
#
|
32
|
+
# Rails.application.configure do
|
33
|
+
# config.active_record.database_selector = { delay: 2.seconds }
|
34
|
+
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
35
|
+
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
36
|
+
# end
|
28
37
|
#
|
29
|
-
#
|
38
|
+
# Alternatively you can set the options in your environment config or
|
39
|
+
# any other config file loaded on boot.
|
30
40
|
#
|
31
41
|
# The default behavior can be changed by setting the config options to a
|
32
42
|
# custom class:
|
@@ -34,6 +44,10 @@ module ActiveRecord
|
|
34
44
|
# config.active_record.database_selector = { delay: 2.seconds }
|
35
45
|
# config.active_record.database_resolver = MyResolver
|
36
46
|
# config.active_record.database_resolver_context = MyResolver::MySession
|
47
|
+
#
|
48
|
+
# Note: If you are using <tt>rails new my_app --minimal</tt> you will need
|
49
|
+
# to call <tt>require "active_support/core_ext/integer/time"</tt> to load
|
50
|
+
# the core extension in order to use +2.seconds+
|
37
51
|
class DatabaseSelector
|
38
52
|
def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
|
39
53
|
@app = app
|
@@ -59,7 +73,7 @@ module ActiveRecord
|
|
59
73
|
context = context_klass.call(request)
|
60
74
|
resolver = resolver_klass.call(context, options)
|
61
75
|
|
62
|
-
response = if reading_request?(request)
|
76
|
+
response = if resolver.reading_request?(request)
|
63
77
|
resolver.read(&blk)
|
64
78
|
else
|
65
79
|
resolver.write(&blk)
|
@@ -68,10 +82,6 @@ module ActiveRecord
|
|
68
82
|
resolver.update_context(response)
|
69
83
|
response
|
70
84
|
end
|
71
|
-
|
72
|
-
def reading_request?(request)
|
73
|
-
request.get? || request.head?
|
74
|
-
end
|
75
85
|
end
|
76
86
|
end
|
77
87
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Middleware
|
5
|
+
# = Shard Selector \Middleware
|
6
|
+
#
|
7
|
+
# The ShardSelector Middleware provides a framework for automatically
|
8
|
+
# swapping shards. \Rails provides a basic framework to determine which
|
9
|
+
# shard to switch to and allows for applications to write custom strategies
|
10
|
+
# for swapping if needed.
|
11
|
+
#
|
12
|
+
# The ShardSelector takes a set of options (currently only +lock+ is supported)
|
13
|
+
# that can be used by the middleware to alter behavior. +lock+ is
|
14
|
+
# true by default and will prohibit the request from switching shards once
|
15
|
+
# inside the block. If +lock+ is false, then shard swapping will be allowed.
|
16
|
+
# For tenant based sharding, +lock+ should always be true to prevent application
|
17
|
+
# code from mistakenly switching between tenants.
|
18
|
+
#
|
19
|
+
# Options can be set in the config:
|
20
|
+
#
|
21
|
+
# config.active_record.shard_selector = { lock: true }
|
22
|
+
#
|
23
|
+
# Applications must also provide the code for the resolver as it depends on application
|
24
|
+
# specific models. An example resolver would look like this:
|
25
|
+
#
|
26
|
+
# config.active_record.shard_resolver = ->(request) {
|
27
|
+
# subdomain = request.subdomain
|
28
|
+
# tenant = Tenant.find_by_subdomain!(subdomain)
|
29
|
+
# tenant.shard
|
30
|
+
# }
|
31
|
+
class ShardSelector
|
32
|
+
def initialize(app, resolver, options = {})
|
33
|
+
@app = app
|
34
|
+
@resolver = resolver
|
35
|
+
@options = options
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :resolver, :options
|
39
|
+
|
40
|
+
def call(env)
|
41
|
+
request = ActionDispatch::Request.new(env)
|
42
|
+
|
43
|
+
shard = selected_shard(request)
|
44
|
+
|
45
|
+
set_shard(shard) do
|
46
|
+
@app.call(env)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def selected_shard(request)
|
52
|
+
resolver.call(request)
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_shard(shard, &block)
|
56
|
+
ActiveRecord::Base.connected_to(shard: shard.to_sym) do
|
57
|
+
ActiveRecord::Base.prohibit_shard_swapping(options.fetch(:lock, true), &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -2,35 +2,44 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class Migration
|
5
|
-
#
|
5
|
+
# = \Migration Command Recorder
|
6
|
+
#
|
7
|
+
# +ActiveRecord::Migration::CommandRecorder+ records commands done during
|
6
8
|
# a migration and knows how to reverse those commands. The CommandRecorder
|
7
9
|
# knows how to invert the following commands:
|
8
10
|
#
|
9
11
|
# * add_column
|
10
12
|
# * add_foreign_key
|
11
13
|
# * add_check_constraint
|
14
|
+
# * add_exclusion_constraint
|
15
|
+
# * add_unique_constraint
|
12
16
|
# * add_index
|
13
17
|
# * add_reference
|
14
18
|
# * add_timestamps
|
15
|
-
# *
|
16
|
-
# * change_column_default (must supply a :from and :to option)
|
19
|
+
# * change_column_default (must supply a +:from+ and +:to+ option)
|
17
20
|
# * change_column_null
|
18
|
-
# * change_column_comment (must supply a
|
19
|
-
# * change_table_comment (must supply a
|
21
|
+
# * change_column_comment (must supply a +:from+ and +:to+ option)
|
22
|
+
# * change_table_comment (must supply a +:from+ and +:to+ option)
|
23
|
+
# * create_enum
|
20
24
|
# * create_join_table
|
21
25
|
# * create_table
|
22
26
|
# * disable_extension
|
27
|
+
# * drop_enum (must supply a list of values)
|
23
28
|
# * drop_join_table
|
24
29
|
# * drop_table (must supply a block)
|
25
30
|
# * enable_extension
|
26
31
|
# * remove_column (must supply a type)
|
27
|
-
# * remove_columns (must
|
32
|
+
# * remove_columns (must supply a +:type+ option)
|
28
33
|
# * remove_foreign_key (must supply a second table)
|
29
34
|
# * remove_check_constraint
|
35
|
+
# * remove_exclusion_constraint
|
36
|
+
# * remove_unique_constraint
|
30
37
|
# * remove_index
|
31
38
|
# * remove_reference
|
32
39
|
# * remove_timestamps
|
33
40
|
# * rename_column
|
41
|
+
# * rename_enum (must supply a +:to+ option)
|
42
|
+
# * rename_enum_value (must supply a +:from+ and +:to+ option)
|
34
43
|
# * rename_index
|
35
44
|
# * rename_table
|
36
45
|
class CommandRecorder
|
@@ -42,7 +51,10 @@ module ActiveRecord
|
|
42
51
|
:change_column, :execute, :remove_columns, :change_column_null,
|
43
52
|
:add_foreign_key, :remove_foreign_key,
|
44
53
|
:change_column_comment, :change_table_comment,
|
45
|
-
:add_check_constraint, :remove_check_constraint
|
54
|
+
:add_check_constraint, :remove_check_constraint,
|
55
|
+
:add_exclusion_constraint, :remove_exclusion_constraint,
|
56
|
+
:add_unique_constraint, :remove_unique_constraint,
|
57
|
+
:create_enum, :drop_enum, :rename_enum, :add_enum_value, :rename_enum_value,
|
46
58
|
]
|
47
59
|
include JoinTable
|
48
60
|
|
@@ -112,13 +124,21 @@ module ActiveRecord
|
|
112
124
|
record(:"#{method}", args, &block) # record(:create_table, args, &block)
|
113
125
|
end # end
|
114
126
|
EOV
|
115
|
-
ruby2_keywords(method)
|
127
|
+
ruby2_keywords(method)
|
116
128
|
end
|
117
129
|
alias :add_belongs_to :add_reference
|
118
130
|
alias :remove_belongs_to :remove_reference
|
119
131
|
|
120
132
|
def change_table(table_name, **options) # :nodoc:
|
121
|
-
|
133
|
+
if delegate.supports_bulk_alter? && options[:bulk]
|
134
|
+
recorder = self.class.new(self.delegate)
|
135
|
+
recorder.reverting = @reverting
|
136
|
+
yield recorder.delegate.update_table_definition(table_name, recorder)
|
137
|
+
commands = recorder.commands
|
138
|
+
@commands << [:change_table, [table_name], -> t { bulk_change_table(table_name, commands) }]
|
139
|
+
else
|
140
|
+
yield delegate.update_table_definition(table_name, self)
|
141
|
+
end
|
122
142
|
end
|
123
143
|
|
124
144
|
def replay(migration)
|
@@ -140,7 +160,10 @@ module ActiveRecord
|
|
140
160
|
add_reference: :remove_reference,
|
141
161
|
add_foreign_key: :remove_foreign_key,
|
142
162
|
add_check_constraint: :remove_check_constraint,
|
143
|
-
|
163
|
+
add_exclusion_constraint: :remove_exclusion_constraint,
|
164
|
+
add_unique_constraint: :remove_unique_constraint,
|
165
|
+
enable_extension: :disable_extension,
|
166
|
+
create_enum: :drop_enum
|
144
167
|
}.each do |cmd, inv|
|
145
168
|
[[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
|
146
169
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
@@ -154,9 +177,9 @@ module ActiveRecord
|
|
154
177
|
|
155
178
|
include StraightReversions
|
156
179
|
|
157
|
-
def invert_transaction(args)
|
180
|
+
def invert_transaction(args, &block)
|
158
181
|
sub_recorder = CommandRecorder.new(delegate)
|
159
|
-
sub_recorder.revert
|
182
|
+
sub_recorder.revert(&block)
|
160
183
|
|
161
184
|
invertions_proc = proc {
|
162
185
|
sub_recorder.replay(self)
|
@@ -165,7 +188,17 @@ module ActiveRecord
|
|
165
188
|
[:transaction, args, invertions_proc]
|
166
189
|
end
|
167
190
|
|
191
|
+
def invert_create_table(args, &block)
|
192
|
+
if args.last.is_a?(Hash)
|
193
|
+
args.last.delete(:if_not_exists)
|
194
|
+
end
|
195
|
+
super
|
196
|
+
end
|
197
|
+
|
168
198
|
def invert_drop_table(args, &block)
|
199
|
+
if args.last.is_a?(Hash)
|
200
|
+
args.last.delete(:if_exists)
|
201
|
+
end
|
169
202
|
if args.size == 1 && block == nil
|
170
203
|
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
|
171
204
|
end
|
@@ -173,7 +206,10 @@ module ActiveRecord
|
|
173
206
|
end
|
174
207
|
|
175
208
|
def invert_rename_table(args)
|
176
|
-
|
209
|
+
old_name, new_name, options = args
|
210
|
+
args = [new_name, old_name]
|
211
|
+
args << options if options
|
212
|
+
[:rename_table, args]
|
177
213
|
end
|
178
214
|
|
179
215
|
def invert_remove_column(args)
|
@@ -235,6 +271,11 @@ module ActiveRecord
|
|
235
271
|
[:change_column_null, args]
|
236
272
|
end
|
237
273
|
|
274
|
+
def invert_add_foreign_key(args)
|
275
|
+
args.last.delete(:validate) if args.last.is_a?(Hash)
|
276
|
+
super
|
277
|
+
end
|
278
|
+
|
238
279
|
def invert_remove_foreign_key(args)
|
239
280
|
options = args.extract_options!
|
240
281
|
from_table, to_table = args
|
@@ -269,11 +310,68 @@ module ActiveRecord
|
|
269
310
|
[:change_table_comment, [table, from: options[:to], to: options[:from]]]
|
270
311
|
end
|
271
312
|
|
313
|
+
def invert_add_check_constraint(args)
|
314
|
+
if (options = args.last).is_a?(Hash)
|
315
|
+
options.delete(:validate)
|
316
|
+
options[:if_exists] = options.delete(:if_not_exists) if options.key?(:if_not_exists)
|
317
|
+
end
|
318
|
+
super
|
319
|
+
end
|
320
|
+
|
272
321
|
def invert_remove_check_constraint(args)
|
273
322
|
raise ActiveRecord::IrreversibleMigration, "remove_check_constraint is only reversible if given an expression." if args.size < 2
|
323
|
+
|
324
|
+
if (options = args.last).is_a?(Hash)
|
325
|
+
options[:if_not_exists] = options.delete(:if_exists) if options.key?(:if_exists)
|
326
|
+
end
|
327
|
+
super
|
328
|
+
end
|
329
|
+
|
330
|
+
def invert_remove_exclusion_constraint(args)
|
331
|
+
raise ActiveRecord::IrreversibleMigration, "remove_exclusion_constraint is only reversible if given an expression." if args.size < 2
|
332
|
+
super
|
333
|
+
end
|
334
|
+
|
335
|
+
def invert_add_unique_constraint(args)
|
336
|
+
options = args.dup.extract_options!
|
337
|
+
|
338
|
+
raise ActiveRecord::IrreversibleMigration, "add_unique_constraint is not reversible if given an using_index." if options[:using_index]
|
274
339
|
super
|
275
340
|
end
|
276
341
|
|
342
|
+
def invert_remove_unique_constraint(args)
|
343
|
+
_table, columns = args.dup.tap(&:extract_options!)
|
344
|
+
|
345
|
+
raise ActiveRecord::IrreversibleMigration, "remove_unique_constraint is only reversible if given an column_name." if columns.blank?
|
346
|
+
super
|
347
|
+
end
|
348
|
+
|
349
|
+
def invert_drop_enum(args)
|
350
|
+
_enum, values = args.dup.tap(&:extract_options!)
|
351
|
+
raise ActiveRecord::IrreversibleMigration, "drop_enum is only reversible if given a list of enum values." unless values
|
352
|
+
super
|
353
|
+
end
|
354
|
+
|
355
|
+
def invert_rename_enum(args)
|
356
|
+
name, options = args
|
357
|
+
|
358
|
+
unless options.is_a?(Hash) && options.has_key?(:to)
|
359
|
+
raise ActiveRecord::IrreversibleMigration, "rename_enum is only reversible if given a :to option."
|
360
|
+
end
|
361
|
+
|
362
|
+
[:rename_enum, [options[:to], to: name]]
|
363
|
+
end
|
364
|
+
|
365
|
+
def invert_rename_enum_value(args)
|
366
|
+
type_name, options = args
|
367
|
+
|
368
|
+
unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
|
369
|
+
raise ActiveRecord::IrreversibleMigration, "rename_enum_value is only reversible if given a :from and :to option."
|
370
|
+
end
|
371
|
+
|
372
|
+
[:rename_enum_value, [type_name, from: options[:to], to: options[:from]]]
|
373
|
+
end
|
374
|
+
|
277
375
|
def respond_to_missing?(method, _)
|
278
376
|
super || delegate.respond_to?(method)
|
279
377
|
end
|
@@ -286,7 +384,7 @@ module ActiveRecord
|
|
286
384
|
super
|
287
385
|
end
|
288
386
|
end
|
289
|
-
ruby2_keywords(:method_missing)
|
387
|
+
ruby2_keywords(:method_missing)
|
290
388
|
end
|
291
389
|
end
|
292
390
|
end
|