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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -15
@@ -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.connection.exec_query('SELECT id, title, body FROM posts')
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
- @columns = columns
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.dup
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
- @hash_rows ||=
156
- begin
157
- # We freeze the strings to prevent them getting duped when
158
- # used as keys in ActiveRecord::Base's @attributes hash
159
- columns = @columns.map(&:-@)
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
- EMPTY = new([].freeze, [].freeze, {}.freeze).freeze
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 a SQL string since it won't contain
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 a SQL string since it won't contain
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: connection.column_name_with_order_matcher
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
- replace_named_bind_variables(statement, values.first)
166
+ with_connection do |c|
167
+ replace_named_bind_variables(c, statement, values.first)
168
+ end
167
169
  elsif statement.include?("?")
168
- replace_bind_variables(statement, values)
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
- statement % values.collect { |value| connection.quote_string(value.to_s) }
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: connection.column_name_matcher) # :nodoc:
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, c)
206
+ replace_bind_variable(connection, bound.shift)
202
207
  end
203
208
  end
204
209
 
205
- def replace_bind_variable(value, c = connection)
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(value, c)
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 postgresql casts
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(value, c = connection)
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
- c.quote(c.cast_bound_value(nil))
236
+ connection.quote(connection.cast_bound_value(nil))
232
237
  else
233
- values.map! { |v| c.quote(c.cast_bound_value(v)) }.join(",")
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
- c.quote(c.cast_bound_value(value))
242
+ connection.quote(connection.cast_bound_value(value))
238
243
  end
239
244
  end
240
245
 
@@ -52,14 +52,16 @@ module ActiveRecord
52
52
  end
53
53
 
54
54
  def define(info, &block) # :nodoc:
55
- instance_eval(&block)
55
+ connection_pool.with_connection do |connection|
56
+ instance_eval(&block)
56
57
 
57
- connection.schema_migration.create_table
58
- if info[:version].present?
59
- connection.assume_migrated_upto_version(info[:version])
60
- end
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
- connection.internal_metadata.create_table_and_set_flags(connection.migration_context.current_environment)
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(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
45
- connection.create_schema_dumper(generate_options(config)).dump(stream)
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.each do |table_name|
131
- table(table_name, stream) unless ignored?(table_name)
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.use_foreign_keys?
136
- sorted_tables.each do |tbl|
137
- foreign_keys(tbl, stream) unless ignored?(tbl)
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 :connection, :arel_table
12
+ attr_reader :arel_table
13
13
 
14
- def initialize(connection)
15
- @connection = connection
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
- connection.insert(im, "#{self.class} Create", primary_key, version)
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
- connection.delete(dm, "#{self.class} Destroy")
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
- versions.each do |version|
34
- delete_version(version)
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
- unless connection.table_exists?(table_name)
48
- connection.create_table(table_name, id: false) do |t|
49
- t.string :version, **connection.internal_string_options_for_primary_key
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
- connection.drop_table table_name, if_exists: true
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
- connection.select_values(sm, "#{self.class} Load")
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
- connection.select_values(sm, "#{self.class} Count").first
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.data_source_exists?(table_name)
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(Book.connection) do |params|
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([], Book.connection)
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(Book.connection) do |params|
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"], Book.connection)
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
- if reflection = klass&._reflect_on_association(table_name)
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)