activerecord 7.1.5.2 → 8.1.2.1
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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
|
14
14
|
sql
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
|
|
17
|
+
def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil, allow_retry = false) # :nodoc:
|
|
18
18
|
# Arel::TreeManager -> Arel::Node
|
|
19
19
|
if arel_or_sql_string.respond_to?(:ast)
|
|
20
20
|
arel_or_sql_string = arel_or_sql_string.ast
|
|
@@ -27,6 +27,7 @@ module ActiveRecord
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
collector = collector()
|
|
30
|
+
collector.retryable = true
|
|
30
31
|
|
|
31
32
|
if prepared_statements
|
|
32
33
|
collector.preparable = true
|
|
@@ -41,10 +42,11 @@ module ActiveRecord
|
|
|
41
42
|
else
|
|
42
43
|
sql = visitor.compile(arel_or_sql_string, collector)
|
|
43
44
|
end
|
|
44
|
-
|
|
45
|
+
allow_retry = collector.retryable
|
|
46
|
+
[sql.freeze, binds, preparable, allow_retry]
|
|
45
47
|
else
|
|
46
48
|
arel_or_sql_string = arel_or_sql_string.dup.freeze unless arel_or_sql_string.frozen?
|
|
47
|
-
[arel_or_sql_string, binds, preparable]
|
|
49
|
+
[arel_or_sql_string, binds, preparable, allow_retry]
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
private :to_sql_and_binds
|
|
@@ -53,22 +55,29 @@ module ActiveRecord
|
|
|
53
55
|
# can be used to query the database repeatedly.
|
|
54
56
|
def cacheable_query(klass, arel) # :nodoc:
|
|
55
57
|
if prepared_statements
|
|
58
|
+
collector = collector()
|
|
59
|
+
collector.retryable = true
|
|
56
60
|
sql, binds = visitor.compile(arel.ast, collector)
|
|
57
|
-
query = klass.query(sql)
|
|
61
|
+
query = klass.query(sql, retryable: collector.retryable)
|
|
58
62
|
else
|
|
59
63
|
collector = klass.partial_query_collector
|
|
64
|
+
collector.retryable = true
|
|
60
65
|
parts, binds = visitor.compile(arel.ast, collector)
|
|
61
|
-
query = klass.partial_query(parts)
|
|
66
|
+
query = klass.partial_query(parts, retryable: collector.retryable)
|
|
62
67
|
end
|
|
63
68
|
[query, binds]
|
|
64
69
|
end
|
|
65
70
|
|
|
66
71
|
# Returns an ActiveRecord::Result instance.
|
|
67
|
-
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
|
|
72
|
+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)
|
|
68
73
|
arel = arel_from_relation(arel)
|
|
69
|
-
sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
|
|
74
|
+
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)
|
|
70
75
|
|
|
71
|
-
select(sql, name, binds,
|
|
76
|
+
select(sql, name, binds,
|
|
77
|
+
prepare: prepared_statements && preparable,
|
|
78
|
+
async: async && FutureResult::SelectAll,
|
|
79
|
+
allow_retry: allow_retry
|
|
80
|
+
)
|
|
72
81
|
rescue ::RangeError
|
|
73
82
|
ActiveRecord::Result.empty(async: async)
|
|
74
83
|
end
|
|
@@ -96,16 +105,16 @@ module ActiveRecord
|
|
|
96
105
|
select_all(arel, name, binds, async: async).then(&:rows)
|
|
97
106
|
end
|
|
98
107
|
|
|
99
|
-
def query_value(
|
|
100
|
-
single_value_from_rows(query(
|
|
108
|
+
def query_value(...) # :nodoc:
|
|
109
|
+
single_value_from_rows(query(...))
|
|
101
110
|
end
|
|
102
111
|
|
|
103
|
-
def query_values(
|
|
104
|
-
query(
|
|
112
|
+
def query_values(...) # :nodoc:
|
|
113
|
+
query(...).map(&:first)
|
|
105
114
|
end
|
|
106
115
|
|
|
107
|
-
def query(sql, name = nil) # :nodoc:
|
|
108
|
-
internal_exec_query(sql, name).rows
|
|
116
|
+
def query(sql, name = nil, allow_retry: true, materialize_transactions: true) # :nodoc:
|
|
117
|
+
internal_exec_query(sql, name, allow_retry:, materialize_transactions:).rows
|
|
109
118
|
end
|
|
110
119
|
|
|
111
120
|
# Determines whether the SQL statement is a write query.
|
|
@@ -157,14 +166,14 @@ module ActiveRecord
|
|
|
157
166
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
158
167
|
# the executed +sql+ statement.
|
|
159
168
|
def exec_delete(sql, name = nil, binds = [])
|
|
160
|
-
|
|
169
|
+
affected_rows(internal_execute(sql, name, binds))
|
|
161
170
|
end
|
|
162
171
|
|
|
163
172
|
# Executes update +sql+ statement in the context of this connection using
|
|
164
173
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
165
174
|
# the executed +sql+ statement.
|
|
166
175
|
def exec_update(sql, name = nil, binds = [])
|
|
167
|
-
|
|
176
|
+
affected_rows(internal_execute(sql, name, binds))
|
|
168
177
|
end
|
|
169
178
|
|
|
170
179
|
def exec_insert_all(sql, name) # :nodoc:
|
|
@@ -214,21 +223,30 @@ module ActiveRecord
|
|
|
214
223
|
end
|
|
215
224
|
|
|
216
225
|
def truncate_tables(*table_names) # :nodoc:
|
|
217
|
-
table_names -= [schema_migration.table_name, internal_metadata.table_name]
|
|
226
|
+
table_names -= [pool.schema_migration.table_name, pool.internal_metadata.table_name]
|
|
218
227
|
|
|
219
228
|
return if table_names.empty?
|
|
220
229
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
execute_batch(statements, "Truncate Tables")
|
|
225
|
-
end
|
|
230
|
+
disable_referential_integrity do
|
|
231
|
+
statements = build_truncate_statements(table_names)
|
|
232
|
+
execute_batch(statements, "Truncate Tables")
|
|
226
233
|
end
|
|
227
234
|
end
|
|
228
235
|
|
|
229
236
|
# Runs the given block in a database transaction, and returns the result
|
|
230
237
|
# of the block.
|
|
231
238
|
#
|
|
239
|
+
# == Transaction callbacks
|
|
240
|
+
#
|
|
241
|
+
# #transaction yields an ActiveRecord::Transaction object on which it is
|
|
242
|
+
# possible to register callback:
|
|
243
|
+
#
|
|
244
|
+
# ActiveRecord::Base.transaction do |transaction|
|
|
245
|
+
# transaction.before_commit { puts "before commit!" }
|
|
246
|
+
# transaction.after_commit { puts "after commit!" }
|
|
247
|
+
# transaction.after_rollback { puts "after rollback!" }
|
|
248
|
+
# end
|
|
249
|
+
#
|
|
232
250
|
# == Nested transactions support
|
|
233
251
|
#
|
|
234
252
|
# #transaction calls can be nested. By default, this makes all database
|
|
@@ -296,9 +314,9 @@ module ActiveRecord
|
|
|
296
314
|
# #transaction will raise exceptions when it tries to release the
|
|
297
315
|
# already-automatically-released savepoints:
|
|
298
316
|
#
|
|
299
|
-
# Model.
|
|
300
|
-
# Model.
|
|
301
|
-
# Model.
|
|
317
|
+
# Model.lease_connection.transaction do # BEGIN
|
|
318
|
+
# Model.lease_connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
|
319
|
+
# Model.lease_connection.create_table(...)
|
|
302
320
|
# # active_record_1 now automatically released
|
|
303
321
|
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
|
|
304
322
|
# end
|
|
@@ -335,13 +353,29 @@ module ActiveRecord
|
|
|
335
353
|
# isolation level.
|
|
336
354
|
# :args: (requires_new: nil, isolation: nil, &block)
|
|
337
355
|
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
|
|
356
|
+
# If we're running inside the single, non-joinable transaction that
|
|
357
|
+
# ActiveRecord::TestFixtures starts around each example (depth == 1),
|
|
358
|
+
# an `isolation:` hint must be validated then ignored so that the
|
|
359
|
+
# adapter isn't asked to change the isolation level mid-transaction.
|
|
360
|
+
if isolation && !requires_new && open_transactions == 1 && !current_transaction.joinable?
|
|
361
|
+
iso = isolation.to_sym
|
|
362
|
+
|
|
363
|
+
unless transaction_isolation_levels.include?(iso)
|
|
364
|
+
raise ActiveRecord::TransactionIsolationError,
|
|
365
|
+
"invalid transaction isolation level: #{iso.inspect}"
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
current_transaction.isolation = iso
|
|
369
|
+
isolation = nil
|
|
370
|
+
end
|
|
371
|
+
|
|
338
372
|
if !requires_new && current_transaction.joinable?
|
|
339
|
-
if isolation
|
|
373
|
+
if isolation && current_transaction.isolation != isolation
|
|
340
374
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
|
341
375
|
end
|
|
342
|
-
yield
|
|
376
|
+
yield current_transaction.user_transaction
|
|
343
377
|
else
|
|
344
|
-
|
|
378
|
+
within_new_transaction(isolation: isolation, joinable: joinable, &block)
|
|
345
379
|
end
|
|
346
380
|
rescue ActiveRecord::Rollback
|
|
347
381
|
# rollbacks are silently swallowed
|
|
@@ -354,10 +388,10 @@ module ActiveRecord
|
|
|
354
388
|
:disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
|
|
355
389
|
to: :transaction_manager
|
|
356
390
|
|
|
357
|
-
def
|
|
391
|
+
def mark_transaction_written # :nodoc:
|
|
358
392
|
transaction = current_transaction
|
|
359
393
|
if transaction.open?
|
|
360
|
-
transaction.written ||=
|
|
394
|
+
transaction.written ||= true
|
|
361
395
|
end
|
|
362
396
|
end
|
|
363
397
|
|
|
@@ -394,13 +428,24 @@ module ActiveRecord
|
|
|
394
428
|
# Begins the transaction (and turns off auto-committing).
|
|
395
429
|
def begin_db_transaction() end
|
|
396
430
|
|
|
431
|
+
def begin_deferred_transaction(isolation_level = nil) # :nodoc:
|
|
432
|
+
if isolation_level
|
|
433
|
+
begin_isolated_db_transaction(isolation_level)
|
|
434
|
+
else
|
|
435
|
+
begin_db_transaction
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
TRANSACTION_ISOLATION_LEVELS = {
|
|
440
|
+
read_uncommitted: "READ UNCOMMITTED",
|
|
441
|
+
read_committed: "READ COMMITTED",
|
|
442
|
+
repeatable_read: "REPEATABLE READ",
|
|
443
|
+
serializable: "SERIALIZABLE"
|
|
444
|
+
}.freeze
|
|
445
|
+
private_constant :TRANSACTION_ISOLATION_LEVELS
|
|
446
|
+
|
|
397
447
|
def transaction_isolation_levels
|
|
398
|
-
|
|
399
|
-
read_uncommitted: "READ UNCOMMITTED",
|
|
400
|
-
read_committed: "READ COMMITTED",
|
|
401
|
-
repeatable_read: "REPEATABLE READ",
|
|
402
|
-
serializable: "SERIALIZABLE"
|
|
403
|
-
}
|
|
448
|
+
TRANSACTION_ISOLATION_LEVELS
|
|
404
449
|
end
|
|
405
450
|
|
|
406
451
|
# Begins the transaction with the isolation level set. Raises an error by
|
|
@@ -410,6 +455,15 @@ module ActiveRecord
|
|
|
410
455
|
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
|
411
456
|
end
|
|
412
457
|
|
|
458
|
+
# Hook point called after an isolated DB transaction is committed
|
|
459
|
+
# or rolled back.
|
|
460
|
+
# Most adapters don't need to implement anything because the isolation
|
|
461
|
+
# level is set on a per transaction basis.
|
|
462
|
+
# But some databases like SQLite set it on a per connection level
|
|
463
|
+
# and need to explicitly reset it after commit or rollback.
|
|
464
|
+
def reset_isolation_level
|
|
465
|
+
end
|
|
466
|
+
|
|
413
467
|
# Commits the transaction (and turns on auto-committing).
|
|
414
468
|
def commit_db_transaction() end
|
|
415
469
|
|
|
@@ -456,11 +510,9 @@ module ActiveRecord
|
|
|
456
510
|
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
|
|
457
511
|
statements = table_deletes + fixture_inserts
|
|
458
512
|
|
|
459
|
-
|
|
513
|
+
transaction(requires_new: true) do
|
|
460
514
|
disable_referential_integrity do
|
|
461
|
-
|
|
462
|
-
execute_batch(statements, "Fixtures Load")
|
|
463
|
-
end
|
|
515
|
+
execute_batch(statements, "Fixtures Load")
|
|
464
516
|
end
|
|
465
517
|
end
|
|
466
518
|
end
|
|
@@ -469,20 +521,6 @@ module ActiveRecord
|
|
|
469
521
|
"DEFAULT VALUES"
|
|
470
522
|
end
|
|
471
523
|
|
|
472
|
-
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
|
473
|
-
#
|
|
474
|
-
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
|
475
|
-
# should look like an integer, or an Arel SQL literal.
|
|
476
|
-
#
|
|
477
|
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
|
478
|
-
def sanitize_limit(limit)
|
|
479
|
-
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
|
480
|
-
limit
|
|
481
|
-
else
|
|
482
|
-
Integer(limit)
|
|
483
|
-
end
|
|
484
|
-
end
|
|
485
|
-
|
|
486
524
|
# Fixture value is quoted by Arel, however scalar values
|
|
487
525
|
# are not quotable. In this case we want to convert
|
|
488
526
|
# the column value to YAML.
|
|
@@ -495,7 +533,7 @@ module ActiveRecord
|
|
|
495
533
|
end
|
|
496
534
|
|
|
497
535
|
# This is a safe default, even if not high precision on all databases
|
|
498
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
|
536
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
|
499
537
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
500
538
|
|
|
501
539
|
# Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
|
|
@@ -507,35 +545,78 @@ module ActiveRecord
|
|
|
507
545
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
508
546
|
end
|
|
509
547
|
|
|
510
|
-
|
|
511
|
-
|
|
548
|
+
# Same as raw_execute but returns an ActiveRecord::Result object.
|
|
549
|
+
def raw_exec_query(...) # :nodoc:
|
|
550
|
+
cast_result(raw_execute(...))
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# Execute a query and returns an ActiveRecord::Result
|
|
554
|
+
def internal_exec_query(...) # :nodoc:
|
|
555
|
+
cast_result(internal_execute(...))
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def default_insert_value(column) # :nodoc:
|
|
559
|
+
DEFAULT_INSERT_VALUE
|
|
512
560
|
end
|
|
513
561
|
|
|
514
562
|
private
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
check_if_write_query(sql)
|
|
563
|
+
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
|
564
|
+
private_constant :DEFAULT_INSERT_VALUE
|
|
518
565
|
|
|
519
|
-
|
|
566
|
+
# Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
|
|
567
|
+
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
|
|
568
|
+
type_casted_binds = type_casted_binds(binds)
|
|
569
|
+
log(sql, name, binds, type_casted_binds, async: async, allow_retry: allow_retry) do |notification_payload|
|
|
570
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
571
|
+
result = perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
|
|
572
|
+
handle_warnings(result, sql)
|
|
573
|
+
result
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
520
577
|
|
|
521
|
-
|
|
578
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
|
|
579
|
+
raise NotImplementedError
|
|
522
580
|
end
|
|
523
581
|
|
|
524
|
-
def
|
|
525
|
-
statements.each do |statement|
|
|
526
|
-
internal_execute(statement, name)
|
|
527
|
-
end
|
|
582
|
+
def handle_warnings(raw_result, sql)
|
|
528
583
|
end
|
|
529
584
|
|
|
530
|
-
|
|
585
|
+
# Receive a native adapter result object and returns an ActiveRecord::Result object.
|
|
586
|
+
def cast_result(raw_result)
|
|
531
587
|
raise NotImplementedError
|
|
532
588
|
end
|
|
533
589
|
|
|
534
|
-
|
|
535
|
-
|
|
590
|
+
def affected_rows(raw_result)
|
|
591
|
+
raise NotImplementedError
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def preprocess_query(sql)
|
|
595
|
+
if write_query?(sql)
|
|
596
|
+
ensure_writes_are_allowed(sql)
|
|
597
|
+
mark_transaction_written
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# We call tranformers after the write checks so we don't add extra parsing work.
|
|
601
|
+
# This means we assume no transformer whille change a read for a write
|
|
602
|
+
# but it would be insane to do such a thing.
|
|
603
|
+
ActiveRecord.query_transformers.each do |transformer|
|
|
604
|
+
sql = transformer.call(sql, self)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
sql
|
|
608
|
+
end
|
|
536
609
|
|
|
537
|
-
|
|
538
|
-
|
|
610
|
+
# Same as #internal_exec_query, but yields a native adapter result
|
|
611
|
+
def internal_execute(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, &block)
|
|
612
|
+
sql = preprocess_query(sql)
|
|
613
|
+
raw_execute(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions, &block)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
617
|
+
statements.each do |statement|
|
|
618
|
+
raw_execute(statement, name, **kwargs)
|
|
619
|
+
end
|
|
539
620
|
end
|
|
540
621
|
|
|
541
622
|
def build_fixture_sql(fixtures, table_name)
|
|
@@ -551,8 +632,8 @@ module ActiveRecord
|
|
|
551
632
|
|
|
552
633
|
columns.map do |name, column|
|
|
553
634
|
if fixture.key?(name)
|
|
554
|
-
|
|
555
|
-
with_yaml_fallback(
|
|
635
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
636
|
+
with_yaml_fallback(column.fetch_cast_type(self).serialize(fixture[name]))
|
|
556
637
|
else
|
|
557
638
|
default_insert_value(column)
|
|
558
639
|
end
|
|
@@ -597,21 +678,19 @@ module ActiveRecord
|
|
|
597
678
|
end
|
|
598
679
|
end
|
|
599
680
|
|
|
600
|
-
def with_multi_statements
|
|
601
|
-
yield
|
|
602
|
-
end
|
|
603
|
-
|
|
604
681
|
def combine_multi_statements(total_sql)
|
|
605
682
|
total_sql.join(";\n")
|
|
606
683
|
end
|
|
607
684
|
|
|
608
685
|
# Returns an ActiveRecord::Result instance.
|
|
609
|
-
def select(sql, name = nil, binds = [], prepare: false, async: false)
|
|
686
|
+
def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
|
|
610
687
|
if async && async_enabled?
|
|
611
688
|
if current_transaction.joinable?
|
|
612
689
|
raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
|
|
613
690
|
end
|
|
614
691
|
|
|
692
|
+
# We make sure to run query transformers on the original thread
|
|
693
|
+
sql = preprocess_query(sql)
|
|
615
694
|
future_result = async.new(
|
|
616
695
|
pool,
|
|
617
696
|
sql,
|
|
@@ -619,19 +698,19 @@ module ActiveRecord
|
|
|
619
698
|
binds,
|
|
620
699
|
prepare: prepare,
|
|
621
700
|
)
|
|
622
|
-
if supports_concurrent_connections? && current_transaction.
|
|
701
|
+
if supports_concurrent_connections? && !current_transaction.joinable?
|
|
623
702
|
future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
|
|
624
703
|
else
|
|
625
704
|
future_result.execute!(self)
|
|
626
705
|
end
|
|
627
|
-
|
|
628
|
-
end
|
|
629
|
-
|
|
630
|
-
result = internal_exec_query(sql, name, binds, prepare: prepare)
|
|
631
|
-
if async
|
|
632
|
-
FutureResult.wrap(result)
|
|
706
|
+
future_result
|
|
633
707
|
else
|
|
634
|
-
result
|
|
708
|
+
result = internal_exec_query(sql, name, binds, prepare: prepare, allow_retry: allow_retry)
|
|
709
|
+
if async
|
|
710
|
+
FutureResult.wrap(result)
|
|
711
|
+
else
|
|
712
|
+
result
|
|
713
|
+
end
|
|
635
714
|
end
|
|
636
715
|
end
|
|
637
716
|
|