activerecord 7.1.5.1 → 7.2.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 +515 -2445
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- 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 +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 +33 -16
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- data/lib/active_record/base.rb +2 -3
- 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 +248 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- 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 +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -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/cidr.rb +1 -1
- 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 +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- 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 +44 -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 +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- 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 +56 -41
- data/lib/active_record/core.rb +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -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 +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +3 -3
- 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/marshalling.rb +1 -4
- 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 +85 -76
- data/lib/active_record/model_schema.rb +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +90 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- 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.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- 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 +7 -19
- data/lib/active_record/relation.rb +496 -72
- 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 +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- 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 +76 -70
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- 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 +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- 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/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- 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.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- metadata +20 -15
data/lib/active_record/result.rb
CHANGED
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
9
9
|
# on any database connection adapter. For example:
|
10
10
|
#
|
11
|
-
# result = ActiveRecord::Base.
|
11
|
+
# result = ActiveRecord::Base.lease_connection.exec_query('SELECT id, title, body FROM posts')
|
12
12
|
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
13
13
|
#
|
14
14
|
# # Get the column names of the result:
|
@@ -46,11 +46,14 @@ module ActiveRecord
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def initialize(columns, rows, column_types =
|
50
|
-
|
49
|
+
def initialize(columns, rows, column_types = nil)
|
50
|
+
# We freeze the strings to prevent them getting duped when
|
51
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
52
|
+
@columns = columns.each(&:-@).freeze
|
51
53
|
@rows = rows
|
52
54
|
@hash_rows = nil
|
53
|
-
@column_types = column_types
|
55
|
+
@column_types = column_types || EMPTY_HASH
|
56
|
+
@column_indexes = nil
|
54
57
|
end
|
55
58
|
|
56
59
|
# Returns true if this result set includes the column named +name+
|
@@ -131,7 +134,7 @@ module ActiveRecord
|
|
131
134
|
end
|
132
135
|
|
133
136
|
def initialize_copy(other)
|
134
|
-
@columns = columns
|
137
|
+
@columns = columns
|
135
138
|
@rows = rows.dup
|
136
139
|
@column_types = column_types.dup
|
137
140
|
@hash_rows = nil
|
@@ -142,6 +145,19 @@ module ActiveRecord
|
|
142
145
|
super
|
143
146
|
end
|
144
147
|
|
148
|
+
def column_indexes # :nodoc:
|
149
|
+
@column_indexes ||= begin
|
150
|
+
index = 0
|
151
|
+
hash = {}
|
152
|
+
length = columns.length
|
153
|
+
while index < length
|
154
|
+
hash[columns[index]] = index
|
155
|
+
index += 1
|
156
|
+
end
|
157
|
+
hash
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
145
161
|
private
|
146
162
|
def column_type(name, index, type_overrides)
|
147
163
|
type_overrides.fetch(name) do
|
@@ -152,47 +168,18 @@ module ActiveRecord
|
|
152
168
|
end
|
153
169
|
|
154
170
|
def hash_rows
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
length = columns.length
|
161
|
-
template = nil
|
162
|
-
|
163
|
-
@rows.map { |row|
|
164
|
-
if template
|
165
|
-
# We use transform_values to build subsequent rows from the
|
166
|
-
# hash of the first row. This is faster because we avoid any
|
167
|
-
# reallocs and in Ruby 2.7+ avoid hashing entirely.
|
168
|
-
index = -1
|
169
|
-
template.transform_values do
|
170
|
-
row[index += 1]
|
171
|
-
end
|
172
|
-
else
|
173
|
-
# In the past we used Hash[columns.zip(row)]
|
174
|
-
# though elegant, the verbose way is much more efficient
|
175
|
-
# both time and memory wise cause it avoids a big array allocation
|
176
|
-
# this method is called a lot and needs to be micro optimised
|
177
|
-
hash = {}
|
178
|
-
|
179
|
-
index = 0
|
180
|
-
while index < length
|
181
|
-
hash[columns[index]] = row[index]
|
182
|
-
index += 1
|
183
|
-
end
|
184
|
-
|
185
|
-
# It's possible to select the same column twice, in which case
|
186
|
-
# we can't use a template
|
187
|
-
template = hash if hash.length == length
|
188
|
-
|
189
|
-
hash
|
190
|
-
end
|
191
|
-
}
|
192
|
-
end
|
171
|
+
# We use transform_values to rows.
|
172
|
+
# This is faster because we avoid any reallocs and avoid hashing entirely.
|
173
|
+
@hash_rows ||= @rows.map do |row|
|
174
|
+
column_indexes.transform_values { |index| row[index] }
|
175
|
+
end
|
193
176
|
end
|
194
177
|
|
195
|
-
|
178
|
+
empty_array = [].freeze
|
179
|
+
EMPTY_HASH = {}.freeze
|
180
|
+
private_constant :EMPTY_HASH
|
181
|
+
|
182
|
+
EMPTY = new(empty_array, empty_array, EMPTY_HASH).freeze
|
196
183
|
private_constant :EMPTY
|
197
184
|
|
198
185
|
EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
|
@@ -25,15 +25,54 @@ module ActiveRecord
|
|
25
25
|
ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
|
26
26
|
end
|
27
27
|
|
28
|
+
def queries_count
|
29
|
+
ActiveSupport::IsolatedExecutionState[:active_record_queries_count] ||= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def queries_count=(count)
|
33
|
+
ActiveSupport::IsolatedExecutionState[:active_record_queries_count] = count
|
34
|
+
end
|
35
|
+
|
36
|
+
def cached_queries_count
|
37
|
+
ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] ||= 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def cached_queries_count=(count)
|
41
|
+
ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] = count
|
42
|
+
end
|
43
|
+
|
28
44
|
def reset
|
45
|
+
reset_runtimes
|
46
|
+
reset_queries_count
|
47
|
+
reset_cached_queries_count
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_runtimes
|
29
51
|
rt, self.sql_runtime = sql_runtime, 0.0
|
30
52
|
self.async_sql_runtime = 0.0
|
31
53
|
rt
|
32
54
|
end
|
55
|
+
|
56
|
+
def reset_queries_count
|
57
|
+
qc = queries_count
|
58
|
+
self.queries_count = 0
|
59
|
+
qc
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset_cached_queries_count
|
63
|
+
qc = cached_queries_count
|
64
|
+
self.cached_queries_count = 0
|
65
|
+
qc
|
66
|
+
end
|
33
67
|
end
|
34
68
|
end
|
35
69
|
|
36
70
|
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
|
71
|
+
unless ["SCHEMA", "TRANSACTION"].include?(payload[:name])
|
72
|
+
ActiveRecord::RuntimeRegistry.queries_count += 1
|
73
|
+
ActiveRecord::RuntimeRegistry.cached_queries_count += 1 if payload[:cached]
|
74
|
+
end
|
75
|
+
|
37
76
|
runtime = (finish - start) * 1_000.0
|
38
77
|
|
39
78
|
if payload[:async]
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
18
18
|
# # => "name='foo''bar' and group_id='4'"
|
19
19
|
#
|
20
|
-
# This method will NOT sanitize
|
20
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
21
21
|
# any conditions in it and will return the string as is.
|
22
22
|
#
|
23
23
|
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
@@ -52,7 +52,7 @@ module ActiveRecord
|
|
52
52
|
# Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
|
53
53
|
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
54
54
|
#
|
55
|
-
# This method will NOT sanitize
|
55
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
56
56
|
# any conditions in it and will return the string as is.
|
57
57
|
#
|
58
58
|
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
@@ -85,7 +85,7 @@ module ActiveRecord
|
|
85
85
|
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
86
86
|
disallow_raw_sql!(
|
87
87
|
[condition.first],
|
88
|
-
permit:
|
88
|
+
permit: adapter_class.column_name_with_order_matcher
|
89
89
|
)
|
90
90
|
|
91
91
|
# Ensure we aren't dealing with a subclass of String that might
|
@@ -163,17 +163,23 @@ module ActiveRecord
|
|
163
163
|
def sanitize_sql_array(ary)
|
164
164
|
statement, *values = ary
|
165
165
|
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
166
|
-
|
166
|
+
with_connection do |c|
|
167
|
+
replace_named_bind_variables(c, statement, values.first)
|
168
|
+
end
|
167
169
|
elsif statement.include?("?")
|
168
|
-
|
170
|
+
with_connection do |c|
|
171
|
+
replace_bind_variables(c, statement, values)
|
172
|
+
end
|
169
173
|
elsif statement.blank?
|
170
174
|
statement
|
171
175
|
else
|
172
|
-
|
176
|
+
with_connection do |c|
|
177
|
+
statement % values.collect { |value| c.quote_string(value.to_s) }
|
178
|
+
end
|
173
179
|
end
|
174
180
|
end
|
175
181
|
|
176
|
-
def disallow_raw_sql!(args, permit:
|
182
|
+
def disallow_raw_sql!(args, permit: adapter_class.column_name_matcher) # :nodoc:
|
177
183
|
unexpected = nil
|
178
184
|
args.each do |arg|
|
179
185
|
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
|
@@ -193,48 +199,47 @@ module ActiveRecord
|
|
193
199
|
end
|
194
200
|
|
195
201
|
private
|
196
|
-
def replace_bind_variables(statement, values)
|
202
|
+
def replace_bind_variables(connection, statement, values)
|
197
203
|
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
198
204
|
bound = values.dup
|
199
|
-
c = connection
|
200
205
|
statement.gsub(/\?/) do
|
201
|
-
replace_bind_variable(bound.shift
|
206
|
+
replace_bind_variable(connection, bound.shift)
|
202
207
|
end
|
203
208
|
end
|
204
209
|
|
205
|
-
def replace_bind_variable(
|
210
|
+
def replace_bind_variable(connection, value)
|
206
211
|
if ActiveRecord::Relation === value
|
207
212
|
value.to_sql
|
208
213
|
else
|
209
|
-
quote_bound_value(
|
214
|
+
quote_bound_value(connection, value)
|
210
215
|
end
|
211
216
|
end
|
212
217
|
|
213
|
-
def replace_named_bind_variables(statement, bind_vars)
|
218
|
+
def replace_named_bind_variables(connection, statement, bind_vars)
|
214
219
|
statement.gsub(/([:\\]?):([a-zA-Z]\w*)/) do |match|
|
215
|
-
if $1 == ":" # skip
|
220
|
+
if $1 == ":" # skip PostgreSQL casts
|
216
221
|
match # return the whole match
|
217
222
|
elsif $1 == "\\" # escaped literal colon
|
218
223
|
match[1..-1] # return match with escaping backlash char removed
|
219
224
|
elsif bind_vars.include?(match = $2.to_sym)
|
220
|
-
replace_bind_variable(bind_vars[match])
|
225
|
+
replace_bind_variable(connection, bind_vars[match])
|
221
226
|
else
|
222
227
|
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
223
228
|
end
|
224
229
|
end
|
225
230
|
end
|
226
231
|
|
227
|
-
def quote_bound_value(
|
232
|
+
def quote_bound_value(connection, value)
|
228
233
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
229
234
|
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
230
235
|
if values.empty?
|
231
|
-
|
236
|
+
connection.quote(connection.cast_bound_value(nil))
|
232
237
|
else
|
233
|
-
values.map! { |v|
|
238
|
+
values.map! { |v| connection.quote(connection.cast_bound_value(v)) }.join(",")
|
234
239
|
end
|
235
240
|
else
|
236
241
|
value = value.id_for_database if value.respond_to?(:id_for_database)
|
237
|
-
|
242
|
+
connection.quote(connection.cast_bound_value(value))
|
238
243
|
end
|
239
244
|
end
|
240
245
|
|
data/lib/active_record/schema.rb
CHANGED
@@ -52,14 +52,16 @@ module ActiveRecord
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def define(info, &block) # :nodoc:
|
55
|
-
|
55
|
+
connection_pool.with_connection do |connection|
|
56
|
+
instance_eval(&block)
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
connection_pool.schema_migration.create_table
|
59
|
+
if info[:version].present?
|
60
|
+
connection.assume_migrated_upto_version(info[:version])
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
+
connection_pool.internal_metadata.create_table_and_set_flags(connection_pool.migration_context.current_environment)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
@@ -41,8 +41,10 @@ module ActiveRecord
|
|
41
41
|
cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/
|
42
42
|
|
43
43
|
class << self
|
44
|
-
def dump(
|
45
|
-
connection
|
44
|
+
def dump(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)
|
45
|
+
pool.with_connection do |connection|
|
46
|
+
connection.create_schema_dumper(generate_options(config)).dump(stream)
|
47
|
+
end
|
46
48
|
stream
|
47
49
|
end
|
48
50
|
|
@@ -70,7 +72,7 @@ module ActiveRecord
|
|
70
72
|
|
71
73
|
def initialize(connection, options = {})
|
72
74
|
@connection = connection
|
73
|
-
@version = connection.migration_context.current_version rescue nil
|
75
|
+
@version = connection.pool.migration_context.current_version rescue nil
|
74
76
|
@options = options
|
75
77
|
@ignore_tables = [
|
76
78
|
ActiveRecord::Base.schema_migrations_table_name,
|
@@ -127,15 +129,24 @@ module ActiveRecord
|
|
127
129
|
def tables(stream)
|
128
130
|
sorted_tables = @connection.tables.sort
|
129
131
|
|
130
|
-
sorted_tables.
|
131
|
-
|
132
|
+
not_ignored_tables = sorted_tables.reject { |table_name| ignored?(table_name) }
|
133
|
+
|
134
|
+
not_ignored_tables.each_with_index do |table_name, index|
|
135
|
+
table(table_name, stream)
|
136
|
+
stream.puts if index < not_ignored_tables.count - 1
|
132
137
|
end
|
133
138
|
|
134
139
|
# dump foreign keys at the end to make sure all dependent tables exist.
|
135
|
-
if @connection.
|
136
|
-
|
137
|
-
|
140
|
+
if @connection.supports_foreign_keys?
|
141
|
+
foreign_keys_stream = StringIO.new
|
142
|
+
not_ignored_tables.each do |tbl|
|
143
|
+
foreign_keys(tbl, foreign_keys_stream)
|
138
144
|
end
|
145
|
+
|
146
|
+
foreign_keys_string = foreign_keys_stream.string
|
147
|
+
stream.puts if foreign_keys_string.length > 0
|
148
|
+
|
149
|
+
stream.print foreign_keys_string
|
139
150
|
end
|
140
151
|
end
|
141
152
|
|
@@ -196,7 +207,6 @@ module ActiveRecord
|
|
196
207
|
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
197
208
|
|
198
209
|
tbl.puts " end"
|
199
|
-
tbl.puts
|
200
210
|
|
201
211
|
stream.print tbl.string
|
202
212
|
rescue => e
|
@@ -9,29 +9,35 @@ module ActiveRecord
|
|
9
9
|
class NullSchemaMigration # :nodoc:
|
10
10
|
end
|
11
11
|
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :arel_table
|
13
13
|
|
14
|
-
def initialize(
|
15
|
-
@
|
14
|
+
def initialize(pool)
|
15
|
+
@pool = pool
|
16
16
|
@arel_table = Arel::Table.new(table_name)
|
17
17
|
end
|
18
18
|
|
19
19
|
def create_version(version)
|
20
20
|
im = Arel::InsertManager.new(arel_table)
|
21
21
|
im.insert(arel_table[primary_key] => version)
|
22
|
-
|
22
|
+
@pool.with_connection do |connection|
|
23
|
+
connection.insert(im, "#{self.class} Create", primary_key, version)
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
def delete_version(version)
|
26
28
|
dm = Arel::DeleteManager.new(arel_table)
|
27
29
|
dm.wheres = [arel_table[primary_key].eq(version)]
|
28
30
|
|
29
|
-
|
31
|
+
@pool.with_connection do |connection|
|
32
|
+
connection.delete(dm, "#{self.class} Destroy")
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
def delete_all_versions
|
33
|
-
|
34
|
-
|
37
|
+
@pool.with_connection do |connection|
|
38
|
+
versions.each do |version|
|
39
|
+
delete_version(version)
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
|
@@ -44,15 +50,19 @@ module ActiveRecord
|
|
44
50
|
end
|
45
51
|
|
46
52
|
def create_table
|
47
|
-
|
48
|
-
connection.
|
49
|
-
|
53
|
+
@pool.with_connection do |connection|
|
54
|
+
unless connection.table_exists?(table_name)
|
55
|
+
connection.create_table(table_name, id: false) do |t|
|
56
|
+
t.string :version, **connection.internal_string_options_for_primary_key
|
57
|
+
end
|
50
58
|
end
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
62
|
def drop_table
|
55
|
-
|
63
|
+
@pool.with_connection do |connection|
|
64
|
+
connection.drop_table table_name, if_exists: true
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
68
|
def normalize_migration_number(number)
|
@@ -68,7 +78,9 @@ module ActiveRecord
|
|
68
78
|
sm.project(arel_table[primary_key])
|
69
79
|
sm.order(arel_table[primary_key].asc)
|
70
80
|
|
71
|
-
|
81
|
+
@pool.with_connection do |connection|
|
82
|
+
connection.select_values(sm, "#{self.class} Load")
|
83
|
+
end
|
72
84
|
end
|
73
85
|
|
74
86
|
def integer_versions
|
@@ -79,11 +91,15 @@ module ActiveRecord
|
|
79
91
|
sm = Arel::SelectManager.new(arel_table)
|
80
92
|
sm.project(*Arel::Nodes::Count.new([Arel.star]))
|
81
93
|
|
82
|
-
|
94
|
+
@pool.with_connection do |connection|
|
95
|
+
connection.select_values(sm, "#{self.class} Count").first
|
96
|
+
end
|
83
97
|
end
|
84
98
|
|
85
99
|
def table_exists?
|
86
|
-
connection
|
100
|
+
@pool.with_connection do |connection|
|
101
|
+
connection.data_source_exists?(table_name)
|
102
|
+
end
|
87
103
|
end
|
88
104
|
end
|
89
105
|
end
|
@@ -13,6 +13,16 @@ module ActiveRecord
|
|
13
13
|
class_attribute :signed_id_verifier_secret, instance_writer: false
|
14
14
|
end
|
15
15
|
|
16
|
+
module RelationMethods # :nodoc:
|
17
|
+
def find_signed(...)
|
18
|
+
scoping { model.find_signed(...) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_signed!(...)
|
22
|
+
scoping { model.find_signed!(...) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
16
26
|
module ClassMethods
|
17
27
|
# Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
|
18
28
|
# This is particularly useful for things like password reset or email verification, where you want
|
@@ -76,7 +86,7 @@ module ActiveRecord
|
|
76
86
|
if secret.nil?
|
77
87
|
raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids"
|
78
88
|
else
|
79
|
-
ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON
|
89
|
+
ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON, url_safe: true
|
80
90
|
end
|
81
91
|
end
|
82
92
|
end
|
@@ -4,14 +4,14 @@ module ActiveRecord
|
|
4
4
|
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
|
5
5
|
# Initializing the cache is done by passing the statement in the create block:
|
6
6
|
#
|
7
|
-
# cache = StatementCache.create(
|
7
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
8
8
|
# Book.where(name: "my book").where("author_id > 3")
|
9
9
|
# end
|
10
10
|
#
|
11
11
|
# The cached statement is executed by using the
|
12
12
|
# {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
|
13
13
|
#
|
14
|
-
# cache.execute([],
|
14
|
+
# cache.execute([], ClothingItem.lease_connection)
|
15
15
|
#
|
16
16
|
# The relation returned by the block is cached, and for each
|
17
17
|
# {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
|
@@ -20,13 +20,13 @@ module ActiveRecord
|
|
20
20
|
# If you want to cache the statement without the values you can use the +bind+ method of the
|
21
21
|
# block parameter.
|
22
22
|
#
|
23
|
-
# cache = StatementCache.create(
|
23
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
24
24
|
# Book.where(name: params.bind)
|
25
25
|
# end
|
26
26
|
#
|
27
27
|
# And pass the bind values as the first argument of +execute+ call.
|
28
28
|
#
|
29
|
-
# cache.execute(["my book"],
|
29
|
+
# cache.execute(["my book"], ClothingItem.lease_connection)
|
30
30
|
class StatementCache # :nodoc:
|
31
31
|
class Substitute; end # :nodoc:
|
32
32
|
|
@@ -62,7 +62,7 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
class PartialQueryCollector
|
65
|
-
attr_accessor :preparable
|
65
|
+
attr_accessor :preparable, :retryable
|
66
66
|
|
67
67
|
def initialize
|
68
68
|
@parts = []
|
@@ -142,12 +142,12 @@ module ActiveRecord
|
|
142
142
|
@klass = klass
|
143
143
|
end
|
144
144
|
|
145
|
-
def execute(params, connection, &block)
|
145
|
+
def execute(params, connection, allow_retry: false, &block)
|
146
146
|
bind_values = bind_map.bind params
|
147
147
|
|
148
148
|
sql = query_builder.sql_for bind_values, connection
|
149
149
|
|
150
|
-
klass.find_by_sql(sql, bind_values, preparable: true, &block)
|
150
|
+
klass.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
151
151
|
rescue ::RangeError
|
152
152
|
[]
|
153
153
|
end
|
@@ -23,16 +23,7 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def associated_with?(table_name)
|
26
|
-
|
27
|
-
reflection
|
28
|
-
elsif ActiveRecord.allow_deprecated_singular_associations_name && reflection = klass&._reflect_on_association(table_name.singularize)
|
29
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
30
|
-
Referring to a singular association (e.g. `#{reflection.name}`) by its plural name (e.g. `#{reflection.plural_name}`) is deprecated.
|
31
|
-
|
32
|
-
To convert this deprecation warning to an error and enable more performant behavior, set config.active_record.allow_deprecated_singular_associations_name = false.
|
33
|
-
MSG
|
34
|
-
reflection
|
35
|
-
end
|
26
|
+
klass&._reflect_on_association(table_name)
|
36
27
|
end
|
37
28
|
|
38
29
|
def associated_table(table_name)
|