activerecord 7.1.6 → 7.2.3
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 +839 -2248
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +15 -8
- 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/belongs_to.rb +1 -0
- 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/collection_association.rb +16 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +59 -292
- 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 +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +5 -25
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +51 -60
- data/lib/active_record/attributes.rb +93 -68
- data/lib/active_record/autosave_association.rb +25 -32
- data/lib/active_record/base.rb +4 -5
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +294 -72
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +46 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +19 -18
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +30 -8
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +36 -26
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +133 -78
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +68 -49
- data/lib/active_record/core.rb +112 -44
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +42 -18
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +25 -5
- data/lib/active_record/encryption/encryptor.rb +35 -19
- 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/enum.rb +31 -13
- data/lib/active_record/errors.rb +49 -23
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- 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 +87 -77
- data/lib/active_record/model_schema.rb +31 -68
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +19 -0
- data/lib/active_record/querying.rb +25 -13
- data/lib/active_record/railtie.rb +39 -57
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +42 -44
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +127 -89
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +26 -12
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +238 -65
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +508 -74
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +48 -20
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +27 -7
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +69 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +8 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +73 -15
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +31 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +16 -10
|
@@ -7,6 +7,67 @@ module ActiveRecord
|
|
|
7
7
|
module ConnectionAdapters # :nodoc:
|
|
8
8
|
# = Active Record Connection Adapters \Quoting
|
|
9
9
|
module Quoting
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
module ClassMethods # :nodoc:
|
|
13
|
+
# Regexp for column names (with or without a table name prefix).
|
|
14
|
+
# Matches the following:
|
|
15
|
+
#
|
|
16
|
+
# "#{table_name}.#{column_name}"
|
|
17
|
+
# "#{column_name}"
|
|
18
|
+
def column_name_matcher
|
|
19
|
+
/
|
|
20
|
+
\A
|
|
21
|
+
(
|
|
22
|
+
(?:
|
|
23
|
+
# table_name.column_name | function(one or no argument)
|
|
24
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
|
25
|
+
)
|
|
26
|
+
(?:(?:\s+AS)?\s+\w+)?
|
|
27
|
+
)
|
|
28
|
+
(?:\s*,\s*\g<1>)*
|
|
29
|
+
\z
|
|
30
|
+
/ix
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Regexp for column names with order (with or without a table name prefix,
|
|
34
|
+
# with or without various order modifiers). Matches the following:
|
|
35
|
+
#
|
|
36
|
+
# "#{table_name}.#{column_name}"
|
|
37
|
+
# "#{table_name}.#{column_name} #{direction}"
|
|
38
|
+
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
|
39
|
+
# "#{table_name}.#{column_name} NULLS LAST"
|
|
40
|
+
# "#{column_name}"
|
|
41
|
+
# "#{column_name} #{direction}"
|
|
42
|
+
# "#{column_name} #{direction} NULLS FIRST"
|
|
43
|
+
# "#{column_name} NULLS LAST"
|
|
44
|
+
def column_name_with_order_matcher
|
|
45
|
+
/
|
|
46
|
+
\A
|
|
47
|
+
(
|
|
48
|
+
(?:
|
|
49
|
+
# table_name.column_name | function(one or no argument)
|
|
50
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
|
51
|
+
)
|
|
52
|
+
(?:\s+ASC|\s+DESC)?
|
|
53
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
54
|
+
)
|
|
55
|
+
(?:\s*,\s*\g<1>)*
|
|
56
|
+
\z
|
|
57
|
+
/ix
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Quotes the column name. Must be implemented by subclasses
|
|
61
|
+
def quote_column_name(column_name)
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Quotes the table name. Defaults to column name quoting.
|
|
66
|
+
def quote_table_name(table_name)
|
|
67
|
+
quote_column_name(table_name)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
10
71
|
# Quotes the column value to help prevent
|
|
11
72
|
# {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
|
|
12
73
|
def quote(value)
|
|
@@ -23,9 +84,6 @@ module ActiveRecord
|
|
|
23
84
|
when Type::Time::Value then "'#{quoted_time(value)}'"
|
|
24
85
|
when Date, Time then "'#{quoted_date(value)}'"
|
|
25
86
|
when Class then "'#{value}'"
|
|
26
|
-
when ActiveSupport::Duration
|
|
27
|
-
warn_quote_duration_deprecated
|
|
28
|
-
value.to_s
|
|
29
87
|
else raise TypeError, "can't quote #{value.class.name}"
|
|
30
88
|
end
|
|
31
89
|
end
|
|
@@ -48,21 +106,6 @@ module ActiveRecord
|
|
|
48
106
|
end
|
|
49
107
|
end
|
|
50
108
|
|
|
51
|
-
# Quote a value to be used as a bound parameter of unknown type. For example,
|
|
52
|
-
# MySQL might perform dangerous castings when comparing a string to a number,
|
|
53
|
-
# so this method will cast numbers to string.
|
|
54
|
-
#
|
|
55
|
-
# Deprecated: Consider `Arel.sql("... ? ...", value)` or
|
|
56
|
-
# +sanitize_sql+ instead.
|
|
57
|
-
def quote_bound_value(value)
|
|
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
109
|
# Cast a value to be used as a bound parameter of unknown type. For example,
|
|
67
110
|
# MySQL might perform dangerous castings when comparing a string to a number,
|
|
68
111
|
# so this method will cast numbers to string.
|
|
@@ -89,14 +132,14 @@ module ActiveRecord
|
|
|
89
132
|
s.gsub("\\", '\&\&').gsub("'", "''") # ' (for ruby-mode)
|
|
90
133
|
end
|
|
91
134
|
|
|
92
|
-
# Quotes the column name.
|
|
135
|
+
# Quotes the column name.
|
|
93
136
|
def quote_column_name(column_name)
|
|
94
|
-
column_name
|
|
137
|
+
self.class.quote_column_name(column_name)
|
|
95
138
|
end
|
|
96
139
|
|
|
97
|
-
# Quotes the table name.
|
|
140
|
+
# Quotes the table name.
|
|
98
141
|
def quote_table_name(table_name)
|
|
99
|
-
|
|
142
|
+
self.class.quote_table_name(table_name)
|
|
100
143
|
end
|
|
101
144
|
|
|
102
145
|
# Override to return the quoted table name for assignment. Defaults to
|
|
@@ -177,59 +220,6 @@ module ActiveRecord
|
|
|
177
220
|
comment
|
|
178
221
|
end
|
|
179
222
|
|
|
180
|
-
def column_name_matcher # :nodoc:
|
|
181
|
-
COLUMN_NAME
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def column_name_with_order_matcher # :nodoc:
|
|
185
|
-
COLUMN_NAME_WITH_ORDER
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
# Regexp for column names (with or without a table name prefix).
|
|
189
|
-
# Matches the following:
|
|
190
|
-
#
|
|
191
|
-
# "#{table_name}.#{column_name}"
|
|
192
|
-
# "#{column_name}"
|
|
193
|
-
COLUMN_NAME = /
|
|
194
|
-
\A
|
|
195
|
-
(
|
|
196
|
-
(?:
|
|
197
|
-
# table_name.column_name | function(one or no argument)
|
|
198
|
-
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
|
199
|
-
)
|
|
200
|
-
(?:(?:\s+AS)?\s+\w+)?
|
|
201
|
-
)
|
|
202
|
-
(?:\s*,\s*\g<1>)*
|
|
203
|
-
\z
|
|
204
|
-
/ix
|
|
205
|
-
|
|
206
|
-
# Regexp for column names with order (with or without a table name prefix,
|
|
207
|
-
# with or without various order modifiers). Matches the following:
|
|
208
|
-
#
|
|
209
|
-
# "#{table_name}.#{column_name}"
|
|
210
|
-
# "#{table_name}.#{column_name} #{direction}"
|
|
211
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
|
212
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
|
213
|
-
# "#{column_name}"
|
|
214
|
-
# "#{column_name} #{direction}"
|
|
215
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
|
216
|
-
# "#{column_name} NULLS LAST"
|
|
217
|
-
COLUMN_NAME_WITH_ORDER = /
|
|
218
|
-
\A
|
|
219
|
-
(
|
|
220
|
-
(?:
|
|
221
|
-
# table_name.column_name | function(one or no argument)
|
|
222
|
-
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
|
223
|
-
)
|
|
224
|
-
(?:\s+ASC|\s+DESC)?
|
|
225
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
226
|
-
)
|
|
227
|
-
(?:\s*,\s*\g<1>)*
|
|
228
|
-
\z
|
|
229
|
-
/ix
|
|
230
|
-
|
|
231
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
232
|
-
|
|
233
223
|
private
|
|
234
224
|
def type_casted_binds(binds)
|
|
235
225
|
binds.map do |value|
|
|
@@ -244,22 +234,6 @@ module ActiveRecord
|
|
|
244
234
|
def lookup_cast_type(sql_type)
|
|
245
235
|
type_map.lookup(sql_type)
|
|
246
236
|
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
|
|
263
237
|
end
|
|
264
238
|
end
|
|
265
239
|
end
|
|
@@ -160,6 +160,8 @@ module ActiveRecord
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def defined_for?(to_table: nil, validate: nil, **options)
|
|
163
|
+
options = options.slice(*self.options.keys)
|
|
164
|
+
|
|
163
165
|
(to_table.nil? || to_table.to_s == self.to_table) &&
|
|
164
166
|
(validate.nil? || validate == self.options.fetch(:validate, validate)) &&
|
|
165
167
|
options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
|
|
@@ -186,6 +188,8 @@ module ActiveRecord
|
|
|
186
188
|
end
|
|
187
189
|
|
|
188
190
|
def defined_for?(name:, expression: nil, validate: nil, **options)
|
|
191
|
+
options = options.slice(*self.options.keys)
|
|
192
|
+
|
|
189
193
|
self.name == name.to_s &&
|
|
190
194
|
(validate.nil? || validate == self.options.fetch(:validate, validate)) &&
|
|
191
195
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
|
@@ -348,7 +352,7 @@ module ActiveRecord
|
|
|
348
352
|
# Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
|
|
349
353
|
# is actually of this type:
|
|
350
354
|
#
|
|
351
|
-
# class SomeMigration < ActiveRecord::Migration[7.
|
|
355
|
+
# class SomeMigration < ActiveRecord::Migration[7.2]
|
|
352
356
|
# def up
|
|
353
357
|
# create_table :foo do |t|
|
|
354
358
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
|
@@ -431,7 +435,7 @@ module ActiveRecord
|
|
|
431
435
|
#
|
|
432
436
|
# == Examples
|
|
433
437
|
#
|
|
434
|
-
# # Assuming
|
|
438
|
+
# # Assuming `td` is an instance of TableDefinition
|
|
435
439
|
# td.column(:granted, :boolean, index: true)
|
|
436
440
|
#
|
|
437
441
|
# == Short-hand examples
|
|
@@ -186,6 +186,9 @@ module ActiveRecord
|
|
|
186
186
|
# Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
|
|
187
187
|
#
|
|
188
188
|
# A Symbol can be used to specify the type of the generated primary key column.
|
|
189
|
+
#
|
|
190
|
+
# A Hash can be used to specify the generated primary key column creation options.
|
|
191
|
+
# See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
|
|
189
192
|
# [<tt>:primary_key</tt>]
|
|
190
193
|
# The name of the primary key, if one is to be added automatically.
|
|
191
194
|
# Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
|
|
@@ -293,6 +296,11 @@ module ActiveRecord
|
|
|
293
296
|
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
|
294
297
|
validate_create_table_options!(options)
|
|
295
298
|
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
|
299
|
+
|
|
300
|
+
if force && options.key?(:if_not_exists)
|
|
301
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
|
302
|
+
end
|
|
303
|
+
|
|
296
304
|
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
|
297
305
|
|
|
298
306
|
if force
|
|
@@ -677,7 +685,7 @@ module ActiveRecord
|
|
|
677
685
|
#
|
|
678
686
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
|
679
687
|
# column does not exist. This will silently ignore the migration rather than raising
|
|
680
|
-
# if the column was already
|
|
688
|
+
# if the column was already removed.
|
|
681
689
|
#
|
|
682
690
|
# remove_column(:suppliers, :qualification, if_exists: true)
|
|
683
691
|
def remove_column(table_name, column_name, type = nil, **options)
|
|
@@ -876,9 +884,12 @@ module ActiveRecord
|
|
|
876
884
|
# ====== Creating an index with a specific algorithm
|
|
877
885
|
#
|
|
878
886
|
# add_index(:developers, :name, algorithm: :concurrently)
|
|
879
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
|
887
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
|
880
888
|
#
|
|
881
|
-
#
|
|
889
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
|
890
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
|
891
|
+
#
|
|
892
|
+
# Note: only supported by PostgreSQL and MySQL.
|
|
882
893
|
#
|
|
883
894
|
# Concurrently adding an index is not supported in a transaction.
|
|
884
895
|
#
|
|
@@ -1318,7 +1329,7 @@ module ActiveRecord
|
|
|
1318
1329
|
end
|
|
1319
1330
|
|
|
1320
1331
|
def dump_schema_information # :nodoc:
|
|
1321
|
-
versions = schema_migration.versions
|
|
1332
|
+
versions = pool.schema_migration.versions
|
|
1322
1333
|
insert_versions_sql(versions) if versions.any?
|
|
1323
1334
|
end
|
|
1324
1335
|
|
|
@@ -1328,8 +1339,9 @@ module ActiveRecord
|
|
|
1328
1339
|
|
|
1329
1340
|
def assume_migrated_upto_version(version)
|
|
1330
1341
|
version = version.to_i
|
|
1331
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1342
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1332
1343
|
|
|
1344
|
+
migration_context = pool.migration_context
|
|
1333
1345
|
migrated = migration_context.get_all_versions
|
|
1334
1346
|
versions = migration_context.migrations.map(&:version)
|
|
1335
1347
|
|
|
@@ -1839,7 +1851,7 @@ module ActiveRecord
|
|
|
1839
1851
|
end
|
|
1840
1852
|
|
|
1841
1853
|
def insert_versions_sql(versions)
|
|
1842
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
|
1854
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
|
1843
1855
|
|
|
1844
1856
|
if versions.is_a?(Array)
|
|
1845
1857
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/digest"
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
6
|
module ConnectionAdapters
|
|
5
7
|
# = Active Record Connection Adapters Transaction State
|
|
@@ -89,7 +91,9 @@ module ActiveRecord
|
|
|
89
91
|
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
|
90
92
|
@started = true
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
ActiveSupport::Notifications.instrument("start_transaction.active_record", @base_payload)
|
|
95
|
+
|
|
96
|
+
@payload = @base_payload.dup # We dup because the payload for a given event is mutated later to add the outcome.
|
|
93
97
|
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
|
94
98
|
@handle.start
|
|
95
99
|
end
|
|
@@ -104,7 +108,6 @@ module ActiveRecord
|
|
|
104
108
|
end
|
|
105
109
|
|
|
106
110
|
class NullTransaction # :nodoc:
|
|
107
|
-
def initialize; end
|
|
108
111
|
def state; end
|
|
109
112
|
def closed?; true; end
|
|
110
113
|
def open?; false; end
|
|
@@ -115,17 +118,43 @@ module ActiveRecord
|
|
|
115
118
|
def dirty!; end
|
|
116
119
|
def invalidated?; false; end
|
|
117
120
|
def invalidate!; end
|
|
121
|
+
def materialized?; false; end
|
|
122
|
+
def before_commit; yield; end
|
|
123
|
+
def after_commit; yield; end
|
|
124
|
+
def after_rollback; end
|
|
125
|
+
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
|
118
126
|
end
|
|
119
127
|
|
|
120
128
|
class Transaction # :nodoc:
|
|
121
|
-
|
|
129
|
+
class Callback # :nodoc:
|
|
130
|
+
def initialize(event, callback)
|
|
131
|
+
@event = event
|
|
132
|
+
@callback = callback
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def before_commit
|
|
136
|
+
@callback.call if @event == :before_commit
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def after_commit
|
|
140
|
+
@callback.call if @event == :after_commit
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def after_rollback
|
|
144
|
+
@callback.call if @event == :after_rollback
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
attr_reader :connection, :state, :savepoint_name, :isolation_level, :user_transaction
|
|
122
149
|
attr_accessor :written
|
|
123
150
|
|
|
124
151
|
delegate :invalidate!, :invalidated?, to: :@state
|
|
125
152
|
|
|
126
153
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
|
154
|
+
super()
|
|
127
155
|
@connection = connection
|
|
128
156
|
@state = TransactionState.new
|
|
157
|
+
@callbacks = nil
|
|
129
158
|
@records = nil
|
|
130
159
|
@isolation_level = isolation
|
|
131
160
|
@materialized = false
|
|
@@ -133,7 +162,8 @@ module ActiveRecord
|
|
|
133
162
|
@run_commit_callbacks = run_commit_callbacks
|
|
134
163
|
@lazy_enrollment_records = nil
|
|
135
164
|
@dirty = false
|
|
136
|
-
@
|
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
|
137
167
|
end
|
|
138
168
|
|
|
139
169
|
def dirty!
|
|
@@ -144,6 +174,14 @@ module ActiveRecord
|
|
|
144
174
|
@dirty
|
|
145
175
|
end
|
|
146
176
|
|
|
177
|
+
def open?
|
|
178
|
+
true
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def closed?
|
|
182
|
+
false
|
|
183
|
+
end
|
|
184
|
+
|
|
147
185
|
def add_record(record, ensure_finalize = true)
|
|
148
186
|
@records ||= []
|
|
149
187
|
if ensure_finalize
|
|
@@ -154,6 +192,30 @@ module ActiveRecord
|
|
|
154
192
|
end
|
|
155
193
|
end
|
|
156
194
|
|
|
195
|
+
def before_commit(&block)
|
|
196
|
+
if @state.finalized?
|
|
197
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def after_commit(&block)
|
|
204
|
+
if @state.finalized?
|
|
205
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
(@callbacks ||= []) << Callback.new(:after_commit, block)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def after_rollback(&block)
|
|
212
|
+
if @state.finalized?
|
|
213
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
(@callbacks ||= []) << Callback.new(:after_rollback, block)
|
|
217
|
+
end
|
|
218
|
+
|
|
157
219
|
def records
|
|
158
220
|
if @lazy_enrollment_records
|
|
159
221
|
@records.concat @lazy_enrollment_records.values
|
|
@@ -190,66 +252,85 @@ module ActiveRecord
|
|
|
190
252
|
end
|
|
191
253
|
|
|
192
254
|
def rollback_records
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
255
|
+
if records
|
|
256
|
+
begin
|
|
257
|
+
ite = unique_records
|
|
196
258
|
|
|
197
|
-
|
|
259
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
|
198
260
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
261
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
|
262
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
|
263
|
+
end
|
|
264
|
+
ensure
|
|
265
|
+
ite&.each do |i|
|
|
266
|
+
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
205
269
|
end
|
|
270
|
+
|
|
271
|
+
@callbacks&.each(&:after_rollback)
|
|
206
272
|
end
|
|
207
273
|
|
|
208
274
|
def before_commit_records
|
|
209
|
-
return unless records
|
|
210
|
-
|
|
211
275
|
if @run_commit_callbacks
|
|
212
|
-
if
|
|
213
|
-
|
|
276
|
+
if records
|
|
277
|
+
if ActiveRecord.before_committed_on_all_records
|
|
278
|
+
ite = unique_records
|
|
214
279
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
280
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
|
281
|
+
candidates[record] = record
|
|
282
|
+
end
|
|
218
283
|
|
|
219
|
-
|
|
220
|
-
|
|
284
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
|
285
|
+
record.before_committed! if should_run_callbacks
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
records.uniq.each(&:before_committed!)
|
|
221
289
|
end
|
|
222
|
-
else
|
|
223
|
-
records.uniq.each(&:before_committed!)
|
|
224
290
|
end
|
|
291
|
+
|
|
292
|
+
@callbacks&.each(&:before_commit)
|
|
225
293
|
end
|
|
294
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
|
295
|
+
# remaining callbacks to the parent transaction
|
|
226
296
|
end
|
|
227
297
|
|
|
228
298
|
def commit_records
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
299
|
+
if records
|
|
300
|
+
begin
|
|
301
|
+
ite = unique_records
|
|
232
302
|
|
|
233
|
-
|
|
234
|
-
|
|
303
|
+
if @run_commit_callbacks
|
|
304
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
|
235
305
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
306
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
|
307
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
|
308
|
+
end
|
|
309
|
+
else
|
|
310
|
+
while record = ite.shift
|
|
311
|
+
# if not running callbacks, only adds the record to the parent transaction
|
|
312
|
+
connection.add_transaction_record(record)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
ensure
|
|
316
|
+
ite&.each { |i| i.committed!(should_run_callbacks: false) }
|
|
243
317
|
end
|
|
244
318
|
end
|
|
245
|
-
|
|
246
|
-
|
|
319
|
+
|
|
320
|
+
if @run_commit_callbacks
|
|
321
|
+
@callbacks&.each(&:after_commit)
|
|
322
|
+
elsif @callbacks
|
|
323
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
|
324
|
+
end
|
|
247
325
|
end
|
|
248
326
|
|
|
249
327
|
def full_rollback?; true; end
|
|
250
328
|
def joinable?; @joinable; end
|
|
251
|
-
|
|
252
|
-
|
|
329
|
+
|
|
330
|
+
protected
|
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
|
333
|
+
end
|
|
253
334
|
|
|
254
335
|
private
|
|
255
336
|
def unique_records
|
|
@@ -349,7 +430,7 @@ module ActiveRecord
|
|
|
349
430
|
|
|
350
431
|
def rollback
|
|
351
432
|
unless @state.invalidated?
|
|
352
|
-
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
|
433
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
|
353
434
|
end
|
|
354
435
|
@state.rollback!
|
|
355
436
|
@instrumenter.finish(:rollback) if materialized?
|
|
@@ -532,9 +613,7 @@ module ActiveRecord
|
|
|
532
613
|
@connection.lock.synchronize do
|
|
533
614
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
|
534
615
|
begin
|
|
535
|
-
|
|
536
|
-
completed = true
|
|
537
|
-
ret
|
|
616
|
+
yield transaction.user_transaction
|
|
538
617
|
rescue Exception => error
|
|
539
618
|
rollback_transaction
|
|
540
619
|
after_failure_actions(transaction, error)
|
|
@@ -542,24 +621,8 @@ module ActiveRecord
|
|
|
542
621
|
raise
|
|
543
622
|
ensure
|
|
544
623
|
unless error
|
|
545
|
-
# In 7.1 we enforce timeout >= 0.4.0 which no longer use throw, so we can
|
|
546
|
-
# go back to the original behavior of committing on non-local return.
|
|
547
|
-
# If users are using throw, we assume it's not an error case.
|
|
548
|
-
completed = true if ActiveRecord.commit_transaction_on_non_local_return
|
|
549
|
-
|
|
550
624
|
if Thread.current.status == "aborting"
|
|
551
625
|
rollback_transaction
|
|
552
|
-
elsif !completed && transaction.written
|
|
553
|
-
ActiveRecord.deprecator.warn(<<~EOW)
|
|
554
|
-
A transaction is being rolled back because the transaction block was
|
|
555
|
-
exited using `return`, `break` or `throw`.
|
|
556
|
-
In Rails 7.2 this transaction will be committed instead.
|
|
557
|
-
To opt-in to the new behavior now and suppress this warning
|
|
558
|
-
you can set:
|
|
559
|
-
|
|
560
|
-
Rails.application.config.active_record.commit_transaction_on_non_local_return = true
|
|
561
|
-
EOW
|
|
562
|
-
rollback_transaction
|
|
563
626
|
else
|
|
564
627
|
begin
|
|
565
628
|
commit_transaction
|
|
@@ -590,7 +653,7 @@ module ActiveRecord
|
|
|
590
653
|
end
|
|
591
654
|
|
|
592
655
|
private
|
|
593
|
-
NULL_TRANSACTION = NullTransaction.new
|
|
656
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
|
594
657
|
|
|
595
658
|
# Deallocate invalidated prepared statements outside of the transaction
|
|
596
659
|
def after_failure_actions(transaction, error)
|