activerecord 5.2.2.1 → 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 +734 -508
- 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 +5 -15
- 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 +16 -28
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +3 -10
- data/lib/active_record/associations/has_many_through_association.rb +20 -25
- 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 +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +40 -32
- 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 +22 -8
- 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 +137 -26
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +114 -130
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- 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 +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- 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 +7 -11
- 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 +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -77
- 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 +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -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 +30 -16
- 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 +3 -3
- 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 +91 -64
- 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 +231 -25
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -22
- 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 +42 -44
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +28 -28
- data/lib/active_record/relation/merger.rb +17 -23
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- 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 +17 -10
- data/lib/active_record/relation/query_methods.rb +247 -73
- 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 +6 -7
- data/lib/active_record/scoping/named.rb +20 -15
- data/lib/active_record/statement_cache.rb +32 -5
- 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 +57 -66
- 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 +111 -26
- 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,19 +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
|
-
return super if scope_attributes? || reflect_on_all_aggregations.any?
|
183
|
+
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
|
184
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
188
185
|
|
189
186
|
hash = args.first
|
190
187
|
|
@@ -204,11 +201,9 @@ module ActiveRecord
|
|
204
201
|
where(wheres).limit(1)
|
205
202
|
}
|
206
203
|
begin
|
207
|
-
statement.execute(hash.values, connection)
|
204
|
+
statement.execute(hash.values, connection)&.first
|
208
205
|
rescue TypeError
|
209
206
|
raise ActiveRecord::StatementInvalid
|
210
|
-
rescue ::RangeError
|
211
|
-
nil
|
212
207
|
end
|
213
208
|
end
|
214
209
|
|
@@ -220,7 +215,7 @@ module ActiveRecord
|
|
220
215
|
generated_association_methods
|
221
216
|
end
|
222
217
|
|
223
|
-
def generated_association_methods
|
218
|
+
def generated_association_methods # :nodoc:
|
224
219
|
@generated_association_methods ||= begin
|
225
220
|
mod = const_set(:GeneratedAssociationMethods, Module.new)
|
226
221
|
private_constant :GeneratedAssociationMethods
|
@@ -230,8 +225,20 @@ module ActiveRecord
|
|
230
225
|
end
|
231
226
|
end
|
232
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
|
+
|
233
240
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
234
|
-
def inspect
|
241
|
+
def inspect # :nodoc:
|
235
242
|
if self == Base
|
236
243
|
super
|
237
244
|
elsif abstract_class?
|
@@ -247,7 +254,7 @@ module ActiveRecord
|
|
247
254
|
end
|
248
255
|
|
249
256
|
# Overwrite the default class equality method to provide support for decorated models.
|
250
|
-
def ===(object)
|
257
|
+
def ===(object) # :nodoc:
|
251
258
|
object.is_a?(self)
|
252
259
|
end
|
253
260
|
|
@@ -261,7 +268,8 @@ module ActiveRecord
|
|
261
268
|
end
|
262
269
|
|
263
270
|
def arel_attribute(name, table = arel_table) # :nodoc:
|
264
|
-
name =
|
271
|
+
name = name.to_s
|
272
|
+
name = attribute_aliases[name] || name
|
265
273
|
table[name]
|
266
274
|
end
|
267
275
|
|
@@ -273,6 +281,10 @@ module ActiveRecord
|
|
273
281
|
TypeCaster::Map.new(self)
|
274
282
|
end
|
275
283
|
|
284
|
+
def _internal? # :nodoc:
|
285
|
+
false
|
286
|
+
end
|
287
|
+
|
276
288
|
private
|
277
289
|
|
278
290
|
def cached_find_by_statement(key, &block)
|
@@ -305,7 +317,7 @@ module ActiveRecord
|
|
305
317
|
# # Instantiates a single new object
|
306
318
|
# User.new(first_name: 'Jamie')
|
307
319
|
def initialize(attributes = nil)
|
308
|
-
|
320
|
+
@new_record = true
|
309
321
|
@attributes = self.class._default_attributes.deep_dup
|
310
322
|
|
311
323
|
init_internals
|
@@ -331,15 +343,21 @@ module ActiveRecord
|
|
331
343
|
# post = Post.allocate
|
332
344
|
# post.init_with(coder)
|
333
345
|
# post.title # => 'hello world'
|
334
|
-
def init_with(coder)
|
346
|
+
def init_with(coder, &block)
|
335
347
|
coder = LegacyYamlAdapter.convert(self.class, coder)
|
336
|
-
|
337
|
-
|
338
|
-
|
348
|
+
attributes = self.class.yaml_encoder.decode(coder)
|
349
|
+
init_with_attributes(attributes, coder["new_record"], &block)
|
350
|
+
end
|
339
351
|
|
340
|
-
|
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
|
341
359
|
|
342
|
-
|
360
|
+
init_internals
|
343
361
|
|
344
362
|
yield self if block_given?
|
345
363
|
|
@@ -378,13 +396,13 @@ module ActiveRecord
|
|
378
396
|
##
|
379
397
|
def initialize_dup(other) # :nodoc:
|
380
398
|
@attributes = @attributes.deep_dup
|
381
|
-
@attributes.reset(
|
399
|
+
@attributes.reset(@primary_key)
|
382
400
|
|
383
401
|
_run_initialize_callbacks
|
384
402
|
|
385
403
|
@new_record = true
|
386
404
|
@destroyed = false
|
387
|
-
@_start_transaction_state =
|
405
|
+
@_start_transaction_state = nil
|
388
406
|
@transaction_state = nil
|
389
407
|
|
390
408
|
super
|
@@ -445,6 +463,7 @@ module ActiveRecord
|
|
445
463
|
|
446
464
|
# Returns +true+ if the attributes hash has been frozen.
|
447
465
|
def frozen?
|
466
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
448
467
|
@attributes.frozen?
|
449
468
|
end
|
450
469
|
|
@@ -457,6 +476,14 @@ module ActiveRecord
|
|
457
476
|
end
|
458
477
|
end
|
459
478
|
|
479
|
+
def present? # :nodoc:
|
480
|
+
true
|
481
|
+
end
|
482
|
+
|
483
|
+
def blank? # :nodoc:
|
484
|
+
false
|
485
|
+
end
|
486
|
+
|
460
487
|
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
|
461
488
|
# attributes will be marked as read only since they cannot be saved.
|
462
489
|
def readonly?
|
@@ -479,7 +506,14 @@ module ActiveRecord
|
|
479
506
|
inspection = if defined?(@attributes) && @attributes
|
480
507
|
self.class.attribute_names.collect do |name|
|
481
508
|
if has_attribute?(name)
|
482
|
-
|
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}"
|
483
517
|
end
|
484
518
|
end.compact.join(", ")
|
485
519
|
else
|
@@ -495,15 +529,16 @@ module ActiveRecord
|
|
495
529
|
return super if custom_inspect_method_defined?
|
496
530
|
pp.object_address_group(self) do
|
497
531
|
if defined?(@attributes) && @attributes
|
498
|
-
|
499
|
-
pp.seplist(
|
500
|
-
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|
|
501
534
|
pp.breakable " "
|
502
535
|
pp.group(1) do
|
503
|
-
pp.text
|
536
|
+
pp.text attr_name
|
504
537
|
pp.text ":"
|
505
538
|
pp.breakable
|
506
|
-
|
539
|
+
value = _read_attribute(attr_name)
|
540
|
+
value = inspection_filter.filter_param(attr_name, value) unless value.nil?
|
541
|
+
pp.pp value
|
507
542
|
end
|
508
543
|
end
|
509
544
|
else
|
@@ -533,26 +568,36 @@ module ActiveRecord
|
|
533
568
|
end
|
534
569
|
|
535
570
|
def init_internals
|
571
|
+
@primary_key = self.class.primary_key
|
536
572
|
@readonly = false
|
537
573
|
@destroyed = false
|
538
574
|
@marked_for_destruction = false
|
539
575
|
@destroyed_by_association = nil
|
540
|
-
@
|
541
|
-
@_start_transaction_state = {}
|
576
|
+
@_start_transaction_state = nil
|
542
577
|
@transaction_state = nil
|
578
|
+
|
579
|
+
self.class.define_attribute_methods
|
543
580
|
end
|
544
581
|
|
545
582
|
def initialize_internals_callback
|
546
583
|
end
|
547
584
|
|
548
|
-
def
|
549
|
-
|
550
|
-
|
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__
|
551
592
|
end
|
552
593
|
end
|
594
|
+
private_constant :InspectionMask
|
553
595
|
|
554
|
-
def
|
555
|
-
|
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
|
556
601
|
end
|
557
602
|
end
|
558
603
|
end
|