activerecord 5.2.4.2 → 6.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 +714 -570
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +12 -23
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +2 -2
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +104 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +129 -141
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +103 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +199 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -47
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +23 -27
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +213 -64
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +20 -15
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -65
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -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/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -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 +41 -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/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +110 -25
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -46,41 +46,157 @@ module ActiveRecord
|
|
46
46
|
#
|
47
47
|
# The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
|
48
48
|
# may be returned on an error.
|
49
|
-
def establish_connection(
|
50
|
-
|
49
|
+
def establish_connection(config_or_env = nil)
|
50
|
+
config_hash = resolve_config_for_connection(config_or_env)
|
51
|
+
connection_handler.establish_connection(config_hash)
|
52
|
+
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
# Connects a model to the databases specified. The +database+ keyword
|
55
|
+
# takes a hash consisting of a +role+ and a +database_key+.
|
56
|
+
#
|
57
|
+
# This will create a connection handler for switching between connections,
|
58
|
+
# look up the config hash using the +database_key+ and finally
|
59
|
+
# establishes a connection to that config.
|
60
|
+
#
|
61
|
+
# class AnimalsModel < ApplicationRecord
|
62
|
+
# self.abstract_class = true
|
63
|
+
#
|
64
|
+
# connects_to database: { writing: :primary, reading: :primary_replica }
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Returns an array of established connections.
|
68
|
+
def connects_to(database: {})
|
69
|
+
connections = []
|
55
70
|
|
56
|
-
|
57
|
-
|
58
|
-
|
71
|
+
database.each do |role, database_key|
|
72
|
+
config_hash = resolve_config_for_connection(database_key)
|
73
|
+
handler = lookup_connection_handler(role.to_sym)
|
59
74
|
|
60
|
-
|
75
|
+
connections << handler.establish_connection(config_hash)
|
76
|
+
end
|
77
|
+
|
78
|
+
connections
|
61
79
|
end
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
# Connects to a database or role (ex writing, reading, or another
|
82
|
+
# custom role) for the duration of the block.
|
83
|
+
#
|
84
|
+
# If a role is passed, Active Record will look up the connection
|
85
|
+
# based on the requested role:
|
86
|
+
#
|
87
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
88
|
+
# Dog.create! # creates dog using dog writing connection
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
92
|
+
# Dog.create! # throws exception because we're on a replica
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# ActiveRecord::Base.connected_to(role: :unknown_role) do
|
96
|
+
# # raises exception due to non-existent role
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# For cases where you may want to connect to a database outside of the model,
|
100
|
+
# you can use +connected_to+ with a +database+ argument. The +database+ argument
|
101
|
+
# expects a symbol that corresponds to the database key in your config.
|
102
|
+
#
|
103
|
+
# ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
|
104
|
+
# Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# This will connect to a new database for the queries inside the block. By
|
108
|
+
# default the `:writing` role will be used since all connections must be assigned
|
109
|
+
# a role. If you would like to use a different role you can pass a hash to database:
|
110
|
+
#
|
111
|
+
# ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
|
112
|
+
# # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
|
113
|
+
# Dog.run_a_long_query
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# When using the database key a new connection will be established every time. It is not
|
117
|
+
# recommended to use this outside of one-off scripts.
|
118
|
+
def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
|
119
|
+
if database && role
|
120
|
+
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
121
|
+
elsif database
|
122
|
+
if database.is_a?(Hash)
|
123
|
+
role, database = database.first
|
124
|
+
role = role.to_sym
|
125
|
+
end
|
68
126
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
127
|
+
config_hash = resolve_config_for_connection(database)
|
128
|
+
handler = lookup_connection_handler(role)
|
129
|
+
|
130
|
+
handler.establish_connection(config_hash)
|
74
131
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
cfg[@env]["url"] ||= url
|
81
|
-
end
|
132
|
+
with_handler(role, &blk)
|
133
|
+
elsif role
|
134
|
+
if role == writing_role
|
135
|
+
with_handler(role.to_sym) do
|
136
|
+
connection_handler.while_preventing_writes(prevent_writes, &blk)
|
82
137
|
end
|
138
|
+
else
|
139
|
+
with_handler(role.to_sym, &blk)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
raise ArgumentError, "must provide a `database` or a `role`."
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns true if role is the current connected role.
|
147
|
+
#
|
148
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
149
|
+
# ActiveRecord::Base.connected_to?(role: :writing) #=> true
|
150
|
+
# ActiveRecord::Base.connected_to?(role: :reading) #=> false
|
151
|
+
# end
|
152
|
+
def connected_to?(role:)
|
153
|
+
current_role == role.to_sym
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns the symbol representing the current connected role.
|
157
|
+
#
|
158
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
159
|
+
# ActiveRecord::Base.current_role #=> :writing
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
163
|
+
# ActiveRecord::Base.current_role #=> :reading
|
164
|
+
# end
|
165
|
+
def current_role
|
166
|
+
connection_handlers.key(connection_handler)
|
167
|
+
end
|
168
|
+
|
169
|
+
def lookup_connection_handler(handler_key) # :nodoc:
|
170
|
+
handler_key ||= ActiveRecord::Base.writing_role
|
171
|
+
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
172
|
+
end
|
173
|
+
|
174
|
+
def with_handler(handler_key, &blk) # :nodoc:
|
175
|
+
handler = lookup_connection_handler(handler_key)
|
176
|
+
swap_connection_handler(handler, &blk)
|
177
|
+
end
|
178
|
+
|
179
|
+
def resolve_config_for_connection(config_or_env) # :nodoc:
|
180
|
+
raise "Anonymous class is not allowed." unless name
|
181
|
+
|
182
|
+
config_or_env ||= DEFAULT_ENV.call.to_sym
|
183
|
+
pool_name = primary_class? ? "primary" : name
|
184
|
+
self.connection_specification_name = pool_name
|
185
|
+
|
186
|
+
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
|
187
|
+
config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
|
188
|
+
config_hash[:name] = pool_name
|
189
|
+
|
190
|
+
config_hash
|
191
|
+
end
|
192
|
+
|
193
|
+
# Clears the query cache for all connections associated with the current thread.
|
194
|
+
def clear_query_caches_for_current_thread
|
195
|
+
ActiveRecord::Base.connection_handlers.each_value do |handler|
|
196
|
+
handler.connection_pool_list.each do |pool|
|
197
|
+
pool.connection.clear_query_cache if pool.active_connection?
|
83
198
|
end
|
199
|
+
end
|
84
200
|
end
|
85
201
|
|
86
202
|
# Returns the connection currently associated with the class. This can
|
@@ -100,6 +216,10 @@ module ActiveRecord
|
|
100
216
|
@connection_specification_name
|
101
217
|
end
|
102
218
|
|
219
|
+
def primary_class? # :nodoc:
|
220
|
+
self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
|
221
|
+
end
|
222
|
+
|
103
223
|
# Returns the configuration of the associated connection as a hash:
|
104
224
|
#
|
105
225
|
# ActiveRecord::Base.connection_config
|
@@ -141,5 +261,14 @@ module ActiveRecord
|
|
141
261
|
|
142
262
|
delegate :clear_active_connections!, :clear_reloadable_connections!,
|
143
263
|
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def swap_connection_handler(handler, &blk) # :nodoc:
|
268
|
+
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
269
|
+
yield
|
270
|
+
ensure
|
271
|
+
ActiveRecord::Base.connection_handler = old_handler
|
272
|
+
end
|
144
273
|
end
|
145
274
|
end
|
data/lib/active_record/core.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/hash/indifferent_access"
|
4
4
|
require "active_support/core_ext/string/filters"
|
5
|
+
require "active_support/parameter_filter"
|
5
6
|
require "concurrent/map"
|
6
7
|
|
7
8
|
module ActiveRecord
|
@@ -26,7 +27,7 @@ module ActiveRecord
|
|
26
27
|
|
27
28
|
##
|
28
29
|
# Contains the database configuration - as is typically stored in config/database.yml -
|
29
|
-
# as
|
30
|
+
# as an ActiveRecord::DatabaseConfigurations object.
|
30
31
|
#
|
31
32
|
# For example, the following database.yml...
|
32
33
|
#
|
@@ -40,22 +41,18 @@ module ActiveRecord
|
|
40
41
|
#
|
41
42
|
# ...would result in ActiveRecord::Base.configurations to look like this:
|
42
43
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# 'adapter' => 'sqlite3',
|
50
|
-
# 'database' => 'db/production.sqlite3'
|
51
|
-
# }
|
52
|
-
# }
|
44
|
+
# #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
|
45
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
|
46
|
+
# @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
|
47
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
|
48
|
+
# @spec_name="primary", @config={"adapter"=>"mysql2", "database"=>"db/production.sqlite3"}>
|
49
|
+
# ]>
|
53
50
|
def self.configurations=(config)
|
54
|
-
@@configurations = ActiveRecord::
|
51
|
+
@@configurations = ActiveRecord::DatabaseConfigurations.new(config)
|
55
52
|
end
|
56
53
|
self.configurations = {}
|
57
54
|
|
58
|
-
# Returns fully resolved
|
55
|
+
# Returns fully resolved ActiveRecord::DatabaseConfigurations object
|
59
56
|
def self.configurations
|
60
57
|
@@configurations
|
61
58
|
end
|
@@ -99,7 +96,7 @@ module ActiveRecord
|
|
99
96
|
##
|
100
97
|
# :singleton-method:
|
101
98
|
# Specify whether schema dump should happen at the end of the
|
102
|
-
# db:migrate
|
99
|
+
# db:migrate rails command. This is true by default, which is useful for the
|
103
100
|
# development environment. This should ideally be false in the production
|
104
101
|
# environment where dumping schema is rarely needed.
|
105
102
|
mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
|
@@ -125,25 +122,28 @@ module ActiveRecord
|
|
125
122
|
|
126
123
|
mattr_accessor :belongs_to_required_by_default, instance_accessor: false
|
127
124
|
|
125
|
+
mattr_accessor :connection_handlers, instance_accessor: false, default: {}
|
126
|
+
|
127
|
+
mattr_accessor :writing_role, instance_accessor: false, default: :writing
|
128
|
+
|
129
|
+
mattr_accessor :reading_role, instance_accessor: false, default: :reading
|
130
|
+
|
128
131
|
class_attribute :default_connection_handler, instance_writer: false
|
129
132
|
|
133
|
+
self.filter_attributes = []
|
134
|
+
|
130
135
|
def self.connection_handler
|
131
|
-
|
136
|
+
Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
|
132
137
|
end
|
133
138
|
|
134
139
|
def self.connection_handler=(handler)
|
135
|
-
|
140
|
+
Thread.current.thread_variable_set("ar_connection_handler", handler)
|
136
141
|
end
|
137
142
|
|
138
143
|
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
|
139
144
|
end
|
140
145
|
|
141
|
-
module ClassMethods
|
142
|
-
def allocate
|
143
|
-
define_attribute_methods
|
144
|
-
super
|
145
|
-
end
|
146
|
-
|
146
|
+
module ClassMethods
|
147
147
|
def initialize_find_by_cache # :nodoc:
|
148
148
|
@find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
|
149
149
|
end
|
@@ -160,7 +160,7 @@ module ActiveRecord
|
|
160
160
|
return super if block_given? ||
|
161
161
|
primary_key.nil? ||
|
162
162
|
scope_attributes? ||
|
163
|
-
columns_hash.
|
163
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
164
164
|
|
165
165
|
id = ids.first
|
166
166
|
|
@@ -172,20 +172,16 @@ module ActiveRecord
|
|
172
172
|
where(key => params.bind).limit(1)
|
173
173
|
}
|
174
174
|
|
175
|
-
record = statement.execute([id], connection)
|
175
|
+
record = statement.execute([id], connection)&.first
|
176
176
|
unless record
|
177
|
-
raise RecordNotFound.new("Couldn't find #{name} with '#{
|
178
|
-
name, primary_key, id)
|
177
|
+
raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
|
179
178
|
end
|
180
179
|
record
|
181
|
-
rescue ::RangeError
|
182
|
-
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
|
183
|
-
name, primary_key)
|
184
180
|
end
|
185
181
|
|
186
182
|
def find_by(*args) # :nodoc:
|
187
183
|
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
|
188
|
-
columns_hash.key?(inheritance_column) && base_class
|
184
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
189
185
|
|
190
186
|
hash = args.first
|
191
187
|
|
@@ -205,11 +201,9 @@ module ActiveRecord
|
|
205
201
|
where(wheres).limit(1)
|
206
202
|
}
|
207
203
|
begin
|
208
|
-
statement.execute(hash.values, connection)
|
204
|
+
statement.execute(hash.values, connection)&.first
|
209
205
|
rescue TypeError
|
210
206
|
raise ActiveRecord::StatementInvalid
|
211
|
-
rescue ::RangeError
|
212
|
-
nil
|
213
207
|
end
|
214
208
|
end
|
215
209
|
|
@@ -221,7 +215,7 @@ module ActiveRecord
|
|
221
215
|
generated_association_methods
|
222
216
|
end
|
223
217
|
|
224
|
-
def generated_association_methods
|
218
|
+
def generated_association_methods # :nodoc:
|
225
219
|
@generated_association_methods ||= begin
|
226
220
|
mod = const_set(:GeneratedAssociationMethods, Module.new)
|
227
221
|
private_constant :GeneratedAssociationMethods
|
@@ -231,8 +225,20 @@ module ActiveRecord
|
|
231
225
|
end
|
232
226
|
end
|
233
227
|
|
228
|
+
# Returns columns which shouldn't be exposed while calling +#inspect+.
|
229
|
+
def filter_attributes
|
230
|
+
if defined?(@filter_attributes)
|
231
|
+
@filter_attributes
|
232
|
+
else
|
233
|
+
superclass.filter_attributes
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Specifies columns which shouldn't be exposed while calling +#inspect+.
|
238
|
+
attr_writer :filter_attributes
|
239
|
+
|
234
240
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
235
|
-
def inspect
|
241
|
+
def inspect # :nodoc:
|
236
242
|
if self == Base
|
237
243
|
super
|
238
244
|
elsif abstract_class?
|
@@ -248,7 +254,7 @@ module ActiveRecord
|
|
248
254
|
end
|
249
255
|
|
250
256
|
# Overwrite the default class equality method to provide support for decorated models.
|
251
|
-
def ===(object)
|
257
|
+
def ===(object) # :nodoc:
|
252
258
|
object.is_a?(self)
|
253
259
|
end
|
254
260
|
|
@@ -262,7 +268,8 @@ module ActiveRecord
|
|
262
268
|
end
|
263
269
|
|
264
270
|
def arel_attribute(name, table = arel_table) # :nodoc:
|
265
|
-
name =
|
271
|
+
name = name.to_s
|
272
|
+
name = attribute_aliases[name] || name
|
266
273
|
table[name]
|
267
274
|
end
|
268
275
|
|
@@ -274,6 +281,10 @@ module ActiveRecord
|
|
274
281
|
TypeCaster::Map.new(self)
|
275
282
|
end
|
276
283
|
|
284
|
+
def _internal? # :nodoc:
|
285
|
+
false
|
286
|
+
end
|
287
|
+
|
277
288
|
private
|
278
289
|
|
279
290
|
def cached_find_by_statement(key, &block)
|
@@ -306,7 +317,7 @@ module ActiveRecord
|
|
306
317
|
# # Instantiates a single new object
|
307
318
|
# User.new(first_name: 'Jamie')
|
308
319
|
def initialize(attributes = nil)
|
309
|
-
|
320
|
+
@new_record = true
|
310
321
|
@attributes = self.class._default_attributes.deep_dup
|
311
322
|
|
312
323
|
init_internals
|
@@ -332,15 +343,21 @@ module ActiveRecord
|
|
332
343
|
# post = Post.allocate
|
333
344
|
# post.init_with(coder)
|
334
345
|
# post.title # => 'hello world'
|
335
|
-
def init_with(coder)
|
346
|
+
def init_with(coder, &block)
|
336
347
|
coder = LegacyYamlAdapter.convert(self.class, coder)
|
337
|
-
|
338
|
-
|
339
|
-
|
348
|
+
attributes = self.class.yaml_encoder.decode(coder)
|
349
|
+
init_with_attributes(attributes, coder["new_record"], &block)
|
350
|
+
end
|
340
351
|
|
341
|
-
|
352
|
+
##
|
353
|
+
# Initialize an empty model object from +attributes+.
|
354
|
+
# +attributes+ should be an attributes object, and unlike the
|
355
|
+
# `initialize` method, no assignment calls are made per attribute.
|
356
|
+
def init_with_attributes(attributes, new_record = false) # :nodoc:
|
357
|
+
@new_record = new_record
|
358
|
+
@attributes = attributes
|
342
359
|
|
343
|
-
|
360
|
+
init_internals
|
344
361
|
|
345
362
|
yield self if block_given?
|
346
363
|
|
@@ -379,13 +396,13 @@ module ActiveRecord
|
|
379
396
|
##
|
380
397
|
def initialize_dup(other) # :nodoc:
|
381
398
|
@attributes = @attributes.deep_dup
|
382
|
-
@attributes.reset(
|
399
|
+
@attributes.reset(@primary_key)
|
383
400
|
|
384
401
|
_run_initialize_callbacks
|
385
402
|
|
386
403
|
@new_record = true
|
387
404
|
@destroyed = false
|
388
|
-
@_start_transaction_state =
|
405
|
+
@_start_transaction_state = nil
|
389
406
|
@transaction_state = nil
|
390
407
|
|
391
408
|
super
|
@@ -446,6 +463,7 @@ module ActiveRecord
|
|
446
463
|
|
447
464
|
# Returns +true+ if the attributes hash has been frozen.
|
448
465
|
def frozen?
|
466
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
449
467
|
@attributes.frozen?
|
450
468
|
end
|
451
469
|
|
@@ -458,6 +476,14 @@ module ActiveRecord
|
|
458
476
|
end
|
459
477
|
end
|
460
478
|
|
479
|
+
def present? # :nodoc:
|
480
|
+
true
|
481
|
+
end
|
482
|
+
|
483
|
+
def blank? # :nodoc:
|
484
|
+
false
|
485
|
+
end
|
486
|
+
|
461
487
|
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
|
462
488
|
# attributes will be marked as read only since they cannot be saved.
|
463
489
|
def readonly?
|
@@ -480,7 +506,14 @@ module ActiveRecord
|
|
480
506
|
inspection = if defined?(@attributes) && @attributes
|
481
507
|
self.class.attribute_names.collect do |name|
|
482
508
|
if has_attribute?(name)
|
483
|
-
|
509
|
+
attr = _read_attribute(name)
|
510
|
+
value = if attr.nil?
|
511
|
+
attr.inspect
|
512
|
+
else
|
513
|
+
attr = format_for_inspect(attr)
|
514
|
+
inspection_filter.filter_param(name, attr)
|
515
|
+
end
|
516
|
+
"#{name}: #{value}"
|
484
517
|
end
|
485
518
|
end.compact.join(", ")
|
486
519
|
else
|
@@ -496,15 +529,16 @@ module ActiveRecord
|
|
496
529
|
return super if custom_inspect_method_defined?
|
497
530
|
pp.object_address_group(self) do
|
498
531
|
if defined?(@attributes) && @attributes
|
499
|
-
|
500
|
-
pp.seplist(
|
501
|
-
column_value = read_attribute(column_name)
|
532
|
+
attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
|
533
|
+
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
502
534
|
pp.breakable " "
|
503
535
|
pp.group(1) do
|
504
|
-
pp.text
|
536
|
+
pp.text attr_name
|
505
537
|
pp.text ":"
|
506
538
|
pp.breakable
|
507
|
-
|
539
|
+
value = _read_attribute(attr_name)
|
540
|
+
value = inspection_filter.filter_param(attr_name, value) unless value.nil?
|
541
|
+
pp.pp value
|
508
542
|
end
|
509
543
|
end
|
510
544
|
else
|
@@ -534,26 +568,36 @@ module ActiveRecord
|
|
534
568
|
end
|
535
569
|
|
536
570
|
def init_internals
|
571
|
+
@primary_key = self.class.primary_key
|
537
572
|
@readonly = false
|
538
573
|
@destroyed = false
|
539
574
|
@marked_for_destruction = false
|
540
575
|
@destroyed_by_association = nil
|
541
|
-
@
|
542
|
-
@_start_transaction_state = {}
|
576
|
+
@_start_transaction_state = nil
|
543
577
|
@transaction_state = nil
|
578
|
+
|
579
|
+
self.class.define_attribute_methods
|
544
580
|
end
|
545
581
|
|
546
582
|
def initialize_internals_callback
|
547
583
|
end
|
548
584
|
|
549
|
-
def
|
550
|
-
|
551
|
-
|
585
|
+
def custom_inspect_method_defined?
|
586
|
+
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
|
587
|
+
end
|
588
|
+
|
589
|
+
class InspectionMask < DelegateClass(::String)
|
590
|
+
def pretty_print(pp)
|
591
|
+
pp.text __getobj__
|
552
592
|
end
|
553
593
|
end
|
594
|
+
private_constant :InspectionMask
|
554
595
|
|
555
|
-
def
|
556
|
-
|
596
|
+
def inspection_filter
|
597
|
+
@inspection_filter ||= begin
|
598
|
+
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
599
|
+
ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
|
600
|
+
end
|
557
601
|
end
|
558
602
|
end
|
559
603
|
end
|