activerecord 7.0.8.7 → 7.1.0.beta1
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 +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- 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.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.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/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -15,7 +15,12 @@ module ActiveRecord
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
|
18
|
+
# Arel::TreeManager -> Arel::Node
|
18
19
|
if arel_or_sql_string.respond_to?(:ast)
|
20
|
+
arel_or_sql_string = arel_or_sql_string.ast
|
21
|
+
end
|
22
|
+
|
23
|
+
if Arel.arel_node?(arel_or_sql_string) && !(String === arel_or_sql_string)
|
19
24
|
unless binds.empty?
|
20
25
|
raise "Passing bind parameters with an arel AST is forbidden. " \
|
21
26
|
"The values must be stored on the AST directly"
|
@@ -25,7 +30,7 @@ module ActiveRecord
|
|
25
30
|
|
26
31
|
if prepared_statements
|
27
32
|
collector.preparable = true
|
28
|
-
sql, binds = visitor.compile(arel_or_sql_string
|
33
|
+
sql, binds = visitor.compile(arel_or_sql_string, collector)
|
29
34
|
|
30
35
|
if binds.length > bind_params_length
|
31
36
|
unprepared_statement do
|
@@ -34,7 +39,7 @@ module ActiveRecord
|
|
34
39
|
end
|
35
40
|
preparable = collector.preparable
|
36
41
|
else
|
37
|
-
sql = visitor.compile(arel_or_sql_string
|
42
|
+
sql = visitor.compile(arel_or_sql_string, collector)
|
38
43
|
end
|
39
44
|
[sql.freeze, binds, preparable]
|
40
45
|
else
|
@@ -65,18 +70,18 @@ module ActiveRecord
|
|
65
70
|
|
66
71
|
select(sql, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
|
67
72
|
rescue ::RangeError
|
68
|
-
ActiveRecord::Result.empty
|
73
|
+
ActiveRecord::Result.empty(async: async)
|
69
74
|
end
|
70
75
|
|
71
76
|
# Returns a record hash with the column names as keys and column values
|
72
77
|
# as values.
|
73
|
-
def select_one(arel, name = nil, binds = [])
|
74
|
-
select_all(arel, name, binds).first
|
78
|
+
def select_one(arel, name = nil, binds = [], async: false)
|
79
|
+
select_all(arel, name, binds, async: async).then(&:first)
|
75
80
|
end
|
76
81
|
|
77
82
|
# Returns a single value from a record
|
78
|
-
def select_value(arel, name = nil, binds = [])
|
79
|
-
|
83
|
+
def select_value(arel, name = nil, binds = [], async: false)
|
84
|
+
select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
|
80
85
|
end
|
81
86
|
|
82
87
|
# Returns an array of the values of the first column in a select:
|
@@ -87,8 +92,8 @@ module ActiveRecord
|
|
87
92
|
|
88
93
|
# Returns an array of arrays containing the field values.
|
89
94
|
# Order is the same as that returned by +columns+.
|
90
|
-
def select_rows(arel, name = nil, binds = [])
|
91
|
-
select_all(arel, name, binds).rows
|
95
|
+
def select_rows(arel, name = nil, binds = [], async: false)
|
96
|
+
select_all(arel, name, binds, async: async).then(&:rows)
|
92
97
|
end
|
93
98
|
|
94
99
|
def query_value(sql, name = nil) # :nodoc:
|
@@ -100,7 +105,7 @@ module ActiveRecord
|
|
100
105
|
end
|
101
106
|
|
102
107
|
def query(sql, name = nil) # :nodoc:
|
103
|
-
|
108
|
+
internal_exec_query(sql, name).rows
|
104
109
|
end
|
105
110
|
|
106
111
|
# Determines whether the SQL statement is a write query.
|
@@ -110,47 +115,63 @@ module ActiveRecord
|
|
110
115
|
|
111
116
|
# Executes the SQL statement in the context of this connection and returns
|
112
117
|
# the raw result from the connection adapter.
|
118
|
+
#
|
119
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
120
|
+
# executing the SQL statement in case of a connection-related exception.
|
121
|
+
# This option should only be enabled for known idempotent queries.
|
122
|
+
#
|
123
|
+
# Note: the query is assumed to have side effects and the query cache
|
124
|
+
# will be cleared. If the query is read-only, consider using #select_all
|
125
|
+
# instead.
|
126
|
+
#
|
113
127
|
# Note: depending on your database connector, the result returned by this
|
114
|
-
# method may be manually memory managed. Consider using
|
128
|
+
# method may be manually memory managed. Consider using #exec_query
|
115
129
|
# wrapper instead.
|
116
|
-
def execute(sql, name = nil)
|
117
|
-
|
130
|
+
def execute(sql, name = nil, allow_retry: false)
|
131
|
+
internal_execute(sql, name, allow_retry: allow_retry)
|
118
132
|
end
|
119
133
|
|
120
134
|
# Executes +sql+ statement in the context of this connection using
|
121
135
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
122
136
|
# the executed +sql+ statement.
|
137
|
+
#
|
138
|
+
# Note: the query is assumed to have side effects and the query cache
|
139
|
+
# will be cleared. If the query is read-only, consider using #select_all
|
140
|
+
# instead.
|
123
141
|
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
124
|
-
|
142
|
+
internal_exec_query(sql, name, binds, prepare: prepare)
|
125
143
|
end
|
126
144
|
|
127
145
|
# Executes insert +sql+ statement in the context of this connection using
|
128
146
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
129
147
|
# the executed +sql+ statement.
|
130
|
-
|
131
|
-
|
132
|
-
|
148
|
+
# Some adapters support the `returning` keyword argument which allows to control the result of the query:
|
149
|
+
# `nil` is the default value and maintains default behavior. If an array of column names is passed -
|
150
|
+
# the result will contain values of the specified columns from the inserted row.
|
151
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
|
152
|
+
sql, binds = sql_for_insert(sql, pk, binds, returning)
|
153
|
+
internal_exec_query(sql, name, binds)
|
133
154
|
end
|
134
155
|
|
135
156
|
# Executes delete +sql+ statement in the context of this connection using
|
136
157
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
137
158
|
# the executed +sql+ statement.
|
138
159
|
def exec_delete(sql, name = nil, binds = [])
|
139
|
-
|
160
|
+
internal_exec_query(sql, name, binds)
|
140
161
|
end
|
141
162
|
|
142
163
|
# Executes update +sql+ statement in the context of this connection using
|
143
164
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
144
165
|
# the executed +sql+ statement.
|
145
166
|
def exec_update(sql, name = nil, binds = [])
|
146
|
-
|
167
|
+
internal_exec_query(sql, name, binds)
|
147
168
|
end
|
148
169
|
|
149
170
|
def exec_insert_all(sql, name) # :nodoc:
|
150
|
-
|
171
|
+
internal_exec_query(sql, name)
|
151
172
|
end
|
152
173
|
|
153
|
-
def explain(arel, binds = []) # :nodoc:
|
174
|
+
def explain(arel, binds = [], options = []) # :nodoc:
|
154
175
|
raise NotImplementedError
|
155
176
|
end
|
156
177
|
|
@@ -162,10 +183,14 @@ module ActiveRecord
|
|
162
183
|
#
|
163
184
|
# If the next id was calculated in advance (as in Oracle), it should be
|
164
185
|
# passed in as +id_value+.
|
165
|
-
|
186
|
+
# Some adapters support the `returning` keyword argument which allows defining the return value of the method:
|
187
|
+
# `nil` is the default value and maintains default behavior. If an array of column names is passed -
|
188
|
+
# an array of is returned from the method representing values of the specified columns from the inserted row.
|
189
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
|
166
190
|
sql, binds = to_sql_and_binds(arel, binds)
|
167
|
-
value = exec_insert(sql, name, binds, pk, sequence_name)
|
168
|
-
id_value
|
191
|
+
value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
|
192
|
+
return id_value if id_value
|
193
|
+
returning.nil? ? last_inserted_id(value) : returning_column_values(value)
|
169
194
|
end
|
170
195
|
alias create insert
|
171
196
|
|
@@ -187,7 +212,7 @@ module ActiveRecord
|
|
187
212
|
end
|
188
213
|
|
189
214
|
def truncate_tables(*table_names) # :nodoc:
|
190
|
-
table_names -= [schema_migration.table_name,
|
215
|
+
table_names -= [schema_migration.table_name, internal_metadata.table_name]
|
191
216
|
|
192
217
|
return if table_names.empty?
|
193
218
|
|
@@ -304,8 +329,9 @@ module ActiveRecord
|
|
304
329
|
# * You are joining an existing open transaction
|
305
330
|
# * You are creating a nested (savepoint) transaction
|
306
331
|
#
|
307
|
-
# The mysql2 and postgresql adapters support setting the transaction
|
332
|
+
# The mysql2, trilogy, and postgresql adapters support setting the transaction
|
308
333
|
# isolation level.
|
334
|
+
# :args: (requires_new: nil, isolation: nil, &block)
|
309
335
|
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
|
310
336
|
if !requires_new && current_transaction.joinable?
|
311
337
|
if isolation
|
@@ -323,7 +349,8 @@ module ActiveRecord
|
|
323
349
|
|
324
350
|
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
|
325
351
|
:commit_transaction, :rollback_transaction, :materialize_transactions,
|
326
|
-
:disable_lazy_transactions!, :enable_lazy_transactions!,
|
352
|
+
:disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
|
353
|
+
to: :transaction_manager
|
327
354
|
|
328
355
|
def mark_transaction_written_if_write(sql) # :nodoc:
|
329
356
|
transaction = current_transaction
|
@@ -336,8 +363,24 @@ module ActiveRecord
|
|
336
363
|
current_transaction.open?
|
337
364
|
end
|
338
365
|
|
339
|
-
def reset_transaction # :nodoc:
|
366
|
+
def reset_transaction(restore: false) # :nodoc:
|
367
|
+
# Store the existing transaction state to the side
|
368
|
+
old_state = @transaction_manager if restore && @transaction_manager&.restorable?
|
369
|
+
|
340
370
|
@transaction_manager = ConnectionAdapters::TransactionManager.new(self)
|
371
|
+
|
372
|
+
if block_given?
|
373
|
+
# Reconfigure the connection without any transaction state in the way
|
374
|
+
result = yield
|
375
|
+
|
376
|
+
# Now the connection's fully established, we can swap back
|
377
|
+
if old_state
|
378
|
+
@transaction_manager = old_state
|
379
|
+
@transaction_manager.restore_transactions
|
380
|
+
end
|
381
|
+
|
382
|
+
result
|
383
|
+
end
|
341
384
|
end
|
342
385
|
|
343
386
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
@@ -372,10 +415,18 @@ module ActiveRecord
|
|
372
415
|
# done if the transaction block raises an exception or returns false.
|
373
416
|
def rollback_db_transaction
|
374
417
|
exec_rollback_db_transaction
|
418
|
+
rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
|
419
|
+
# Connection's gone; that counts as a rollback
|
375
420
|
end
|
376
421
|
|
377
422
|
def exec_rollback_db_transaction() end # :nodoc:
|
378
423
|
|
424
|
+
def restart_db_transaction
|
425
|
+
exec_restart_db_transaction
|
426
|
+
end
|
427
|
+
|
428
|
+
def exec_restart_db_transaction() end # :nodoc:
|
429
|
+
|
379
430
|
def rollback_to_savepoint(name = nil)
|
380
431
|
exec_rollback_to_savepoint(name)
|
381
432
|
end
|
@@ -393,7 +444,7 @@ module ActiveRecord
|
|
393
444
|
# something beyond a simple insert (e.g. Oracle).
|
394
445
|
# Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
|
395
446
|
# We keep this method to provide fallback
|
396
|
-
# for databases like
|
447
|
+
# for databases like SQLite that do not support bulk inserts.
|
397
448
|
def insert_fixture(fixture, table_name)
|
398
449
|
execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
|
399
450
|
end
|
@@ -454,13 +505,30 @@ module ActiveRecord
|
|
454
505
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
455
506
|
end
|
456
507
|
|
508
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
509
|
+
raise NotImplementedError
|
510
|
+
end
|
511
|
+
|
457
512
|
private
|
513
|
+
def internal_execute(sql, name = "SCHEMA", allow_retry: false, materialize_transactions: true)
|
514
|
+
sql = transform_query(sql)
|
515
|
+
check_if_write_query(sql)
|
516
|
+
|
517
|
+
mark_transaction_written_if_write(sql)
|
518
|
+
|
519
|
+
raw_execute(sql, name, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
520
|
+
end
|
521
|
+
|
458
522
|
def execute_batch(statements, name = nil)
|
459
523
|
statements.each do |statement|
|
460
|
-
|
524
|
+
internal_execute(statement, name)
|
461
525
|
end
|
462
526
|
end
|
463
527
|
|
528
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
529
|
+
raise NotImplementedError
|
530
|
+
end
|
531
|
+
|
464
532
|
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
465
533
|
private_constant :DEFAULT_INSERT_VALUE
|
466
534
|
|
@@ -557,10 +625,15 @@ module ActiveRecord
|
|
557
625
|
return future_result
|
558
626
|
end
|
559
627
|
|
560
|
-
|
628
|
+
result = internal_exec_query(sql, name, binds, prepare: prepare)
|
629
|
+
if async
|
630
|
+
FutureResult::Complete.new(result)
|
631
|
+
else
|
632
|
+
result
|
633
|
+
end
|
561
634
|
end
|
562
635
|
|
563
|
-
def sql_for_insert(sql,
|
636
|
+
def sql_for_insert(sql, _pk, binds, _returning)
|
564
637
|
[sql, binds]
|
565
638
|
end
|
566
639
|
|
@@ -568,6 +641,10 @@ module ActiveRecord
|
|
568
641
|
single_value_from_rows(result.rows)
|
569
642
|
end
|
570
643
|
|
644
|
+
def returning_column_values(result)
|
645
|
+
[last_inserted_id(result)]
|
646
|
+
end
|
647
|
+
|
571
648
|
def single_value_from_rows(rows)
|
572
649
|
row = rows.first
|
573
650
|
row && row.first
|
@@ -5,10 +5,13 @@ require "concurrent/map"
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters # :nodoc:
|
7
7
|
module QueryCache
|
8
|
+
DEFAULT_SIZE = 100 # :nodoc:
|
9
|
+
|
8
10
|
class << self
|
9
11
|
def included(base) # :nodoc:
|
10
|
-
dirties_query_cache base, :create, :insert, :update, :delete, :truncate,
|
11
|
-
:rollback_to_savepoint, :rollback_db_transaction, :
|
12
|
+
dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate,
|
13
|
+
:truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
|
14
|
+
:exec_insert_all
|
12
15
|
|
13
16
|
base.set_callback :checkout, :after, :configure_query_cache!
|
14
17
|
base.set_callback :checkin, :after, :disable_query_cache!
|
@@ -17,7 +20,7 @@ module ActiveRecord
|
|
17
20
|
def dirties_query_cache(base, *method_names)
|
18
21
|
method_names.each do |method_name|
|
19
22
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
20
|
-
def #{method_name}(
|
23
|
+
def #{method_name}(...)
|
21
24
|
ActiveRecord::Base.clear_query_caches_for_current_thread
|
22
25
|
super
|
23
26
|
end
|
@@ -51,8 +54,9 @@ module ActiveRecord
|
|
51
54
|
|
52
55
|
def initialize(*)
|
53
56
|
super
|
54
|
-
@query_cache =
|
57
|
+
@query_cache = {}
|
55
58
|
@query_cache_enabled = false
|
59
|
+
@query_cache_max_size = nil
|
56
60
|
end
|
57
61
|
|
58
62
|
# Enable the query cache within the block.
|
@@ -93,7 +97,7 @@ module ActiveRecord
|
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
96
|
-
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
|
100
|
+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false) # :nodoc:
|
97
101
|
arel = arel_from_relation(arel)
|
98
102
|
|
99
103
|
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
|
@@ -113,31 +117,52 @@ module ActiveRecord
|
|
113
117
|
|
114
118
|
private
|
115
119
|
def lookup_sql_cache(sql, name, binds)
|
120
|
+
key = binds.empty? ? sql : [sql, binds]
|
121
|
+
hit = false
|
122
|
+
result = nil
|
123
|
+
|
116
124
|
@lock.synchronize do
|
117
|
-
if @query_cache
|
118
|
-
|
119
|
-
|
120
|
-
cache_notification_info(sql, name, binds)
|
121
|
-
)
|
122
|
-
@query_cache[sql][binds]
|
125
|
+
if (result = @query_cache.delete(key))
|
126
|
+
hit = true
|
127
|
+
@query_cache[key] = result
|
123
128
|
end
|
124
129
|
end
|
130
|
+
|
131
|
+
if hit
|
132
|
+
ActiveSupport::Notifications.instrument(
|
133
|
+
"sql.active_record",
|
134
|
+
cache_notification_info(sql, name, binds)
|
135
|
+
)
|
136
|
+
|
137
|
+
result
|
138
|
+
end
|
125
139
|
end
|
126
140
|
|
127
141
|
def cache_sql(sql, name, binds)
|
142
|
+
key = binds.empty? ? sql : [sql, binds]
|
143
|
+
result = nil
|
144
|
+
hit = false
|
145
|
+
|
128
146
|
@lock.synchronize do
|
129
|
-
result =
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
@query_cache
|
136
|
-
else
|
137
|
-
@query_cache[sql][binds] = yield
|
147
|
+
if (result = @query_cache.delete(key))
|
148
|
+
hit = true
|
149
|
+
@query_cache[key] = result
|
150
|
+
else
|
151
|
+
result = @query_cache[key] = yield
|
152
|
+
if @query_cache_max_size && @query_cache.size > @query_cache_max_size
|
153
|
+
@query_cache.shift
|
138
154
|
end
|
139
|
-
|
155
|
+
end
|
140
156
|
end
|
157
|
+
|
158
|
+
if hit
|
159
|
+
ActiveSupport::Notifications.instrument(
|
160
|
+
"sql.active_record",
|
161
|
+
cache_notification_info(sql, name, binds)
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
result.dup
|
141
166
|
end
|
142
167
|
|
143
168
|
# Database adapters can override this method to
|
@@ -154,7 +179,20 @@ module ActiveRecord
|
|
154
179
|
end
|
155
180
|
|
156
181
|
def configure_query_cache!
|
157
|
-
|
182
|
+
case query_cache = pool.db_config.query_cache
|
183
|
+
when 0, false
|
184
|
+
return
|
185
|
+
when Integer
|
186
|
+
@query_cache_max_size = query_cache
|
187
|
+
when nil
|
188
|
+
@query_cache_max_size = DEFAULT_SIZE
|
189
|
+
else
|
190
|
+
@query_cache_max_size = nil # no limit
|
191
|
+
end
|
192
|
+
|
193
|
+
if pool.query_cache_enabled
|
194
|
+
enable_query_cache!
|
195
|
+
end
|
158
196
|
end
|
159
197
|
end
|
160
198
|
end
|
@@ -5,6 +5,7 @@ require "active_support/multibyte/chars"
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters # :nodoc:
|
8
|
+
# = Active Record Connection Adapters \Quoting
|
8
9
|
module Quoting
|
9
10
|
# Quotes the column value to help prevent
|
10
11
|
# {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
|
@@ -17,11 +18,14 @@ module ActiveRecord
|
|
17
18
|
when nil then "NULL"
|
18
19
|
# BigDecimals need to be put in a non-normalized form and quoted.
|
19
20
|
when BigDecimal then value.to_s("F")
|
20
|
-
when Numeric
|
21
|
+
when Numeric then value.to_s
|
21
22
|
when Type::Binary::Data then quoted_binary(value)
|
22
23
|
when Type::Time::Value then "'#{quoted_time(value)}'"
|
23
24
|
when Date, Time then "'#{quoted_date(value)}'"
|
24
25
|
when Class then "'#{value}'"
|
26
|
+
when ActiveSupport::Duration
|
27
|
+
warn_quote_duration_deprecated
|
28
|
+
value.to_s
|
25
29
|
else raise TypeError, "can't quote #{value.class.name}"
|
26
30
|
end
|
27
31
|
end
|
@@ -47,8 +51,23 @@ module ActiveRecord
|
|
47
51
|
# Quote a value to be used as a bound parameter of unknown type. For example,
|
48
52
|
# MySQL might perform dangerous castings when comparing a string to a number,
|
49
53
|
# so this method will cast numbers to string.
|
54
|
+
#
|
55
|
+
# Deprecated: Consider `Arel.sql("... ? ...", value)` or
|
56
|
+
# +sanitize_sql+ instead.
|
50
57
|
def quote_bound_value(value)
|
51
|
-
|
58
|
+
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
59
|
+
#quote_bound_value is deprecated and will be removed in Rails 7.2.
|
60
|
+
Consider Arel.sql(".. ? ..", value) or #sanitize_sql instead.
|
61
|
+
MSG
|
62
|
+
|
63
|
+
quote(cast_bound_value(value))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Cast a value to be used as a bound parameter of unknown type. For example,
|
67
|
+
# MySQL might perform dangerous castings when comparing a string to a number,
|
68
|
+
# so this method will cast numbers to string.
|
69
|
+
def cast_bound_value(value) # :nodoc:
|
70
|
+
value
|
52
71
|
end
|
53
72
|
|
54
73
|
# If you are having to call this function, you are likely doing something
|
@@ -83,7 +102,7 @@ module ActiveRecord
|
|
83
102
|
# Override to return the quoted table name for assignment. Defaults to
|
84
103
|
# table quoting.
|
85
104
|
#
|
86
|
-
# This works for
|
105
|
+
# This works for MySQL where table.column can be used to
|
87
106
|
# resolve ambiguity.
|
88
107
|
#
|
89
108
|
# We override this in the sqlite3 and postgresql adapters to use only
|
@@ -121,7 +140,7 @@ module ActiveRecord
|
|
121
140
|
# if the value is a Time responding to usec.
|
122
141
|
def quoted_date(value)
|
123
142
|
if value.acts_like?(:time)
|
124
|
-
if
|
143
|
+
if default_timezone == :utc
|
125
144
|
value = value.getutc if !value.utc?
|
126
145
|
else
|
127
146
|
value = value.getlocal
|
@@ -176,7 +195,7 @@ module ActiveRecord
|
|
176
195
|
(
|
177
196
|
(?:
|
178
197
|
# table_name.column_name | function(one or no argument)
|
179
|
-
((?:\w+\.)?\w+
|
198
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
180
199
|
)
|
181
200
|
(?:(?:\s+AS)?\s+\w+)?
|
182
201
|
)
|
@@ -200,7 +219,7 @@ module ActiveRecord
|
|
200
219
|
(
|
201
220
|
(?:
|
202
221
|
# table_name.column_name | function(one or no argument)
|
203
|
-
((?:\w+\.)?\w+
|
222
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
204
223
|
)
|
205
224
|
(?:\s+ASC|\s+DESC)?
|
206
225
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
@@ -225,6 +244,22 @@ module ActiveRecord
|
|
225
244
|
def lookup_cast_type(sql_type)
|
226
245
|
type_map.lookup(sql_type)
|
227
246
|
end
|
247
|
+
|
248
|
+
def warn_quote_duration_deprecated
|
249
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
250
|
+
Using ActiveSupport::Duration as an interpolated bind parameter in a SQL
|
251
|
+
string template is deprecated. To avoid this warning, you should explicitly
|
252
|
+
convert the duration to a more specific database type. For example, if you
|
253
|
+
want to use a duration as an integer number of seconds:
|
254
|
+
```
|
255
|
+
Record.where("duration = ?", 1.hour.to_i)
|
256
|
+
```
|
257
|
+
If you want to use a duration as an ISO 8601 string:
|
258
|
+
```
|
259
|
+
Record.where("duration = ?", 1.hour.iso8601)
|
260
|
+
```
|
261
|
+
MSG
|
262
|
+
end
|
228
263
|
end
|
229
264
|
end
|
230
265
|
end
|
@@ -2,21 +2,22 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
|
+
# = Active Record Connection Adapters \Savepoints
|
5
6
|
module Savepoints
|
6
7
|
def current_savepoint_name
|
7
8
|
current_transaction.savepoint_name
|
8
9
|
end
|
9
10
|
|
10
11
|
def create_savepoint(name = current_savepoint_name)
|
11
|
-
|
12
|
+
internal_execute("SAVEPOINT #{name}", "TRANSACTION")
|
12
13
|
end
|
13
14
|
|
14
15
|
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
15
|
-
|
16
|
+
internal_execute("ROLLBACK TO SAVEPOINT #{name}", "TRANSACTION")
|
16
17
|
end
|
17
18
|
|
18
19
|
def release_savepoint(name = current_savepoint_name)
|
19
|
-
|
20
|
+
internal_execute("RELEASE SAVEPOINT #{name}", "TRANSACTION")
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -14,8 +14,10 @@ module ActiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
|
17
|
-
:options_include_default?, :supports_indexes_in_create?, :
|
17
|
+
:options_include_default?, :supports_indexes_in_create?, :use_foreign_keys?,
|
18
18
|
:quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?,
|
19
|
+
:supports_index_include?, :supports_exclusion_constraints?, :supports_unique_keys?,
|
20
|
+
:supports_nulls_not_distinct?,
|
19
21
|
to: :@conn, private: true
|
20
22
|
|
21
23
|
private
|
@@ -51,7 +53,7 @@ module ActiveRecord
|
|
51
53
|
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
|
52
54
|
end
|
53
55
|
|
54
|
-
if
|
56
|
+
if use_foreign_keys?
|
55
57
|
statements.concat(o.foreign_keys.map { |fk| accept fk })
|
56
58
|
end
|
57
59
|
|
@@ -59,6 +61,14 @@ module ActiveRecord
|
|
59
61
|
statements.concat(o.check_constraints.map { |chk| accept chk })
|
60
62
|
end
|
61
63
|
|
64
|
+
if supports_exclusion_constraints?
|
65
|
+
statements.concat(o.exclusion_constraints.map { |exc| accept exc })
|
66
|
+
end
|
67
|
+
|
68
|
+
if supports_unique_keys?
|
69
|
+
statements.concat(o.unique_keys.map { |exc| accept exc })
|
70
|
+
end
|
71
|
+
|
62
72
|
create_sql << "(#{statements.join(', ')})" if statements.present?
|
63
73
|
add_table_options!(create_sql, o)
|
64
74
|
create_sql << " AS #{to_sql(o.as)}" if o.as
|
@@ -70,10 +80,12 @@ module ActiveRecord
|
|
70
80
|
end
|
71
81
|
|
72
82
|
def visit_ForeignKeyDefinition(o)
|
83
|
+
quoted_columns = Array(o.column).map { |c| quote_column_name(c) }
|
84
|
+
quoted_primary_keys = Array(o.primary_key).map { |c| quote_column_name(c) }
|
73
85
|
sql = +<<~SQL
|
74
86
|
CONSTRAINT #{quote_column_name(o.name)}
|
75
|
-
FOREIGN KEY (#{
|
76
|
-
REFERENCES #{quote_table_name(o.to_table)} (#{
|
87
|
+
FOREIGN KEY (#{quoted_columns.join(", ")})
|
88
|
+
REFERENCES #{quote_table_name(o.to_table)} (#{quoted_primary_keys.join(", ")})
|
77
89
|
SQL
|
78
90
|
sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
|
79
91
|
sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
|
@@ -100,6 +112,8 @@ module ActiveRecord
|
|
100
112
|
sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
|
101
113
|
sql << "USING #{index.using}" if supports_index_using? && index.using
|
102
114
|
sql << "(#{quoted_columns(index)})"
|
115
|
+
sql << "INCLUDE (#{quoted_include_columns(index.include)})" if supports_index_include? && index.include
|
116
|
+
sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && index.nulls_not_distinct
|
103
117
|
sql << "WHERE #{index.where}" if supports_partial_index? && index.where
|
104
118
|
|
105
119
|
sql.join(" ")
|