activerecord 7.2.2.1 → 8.1.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +564 -753
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -11
  7. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +10 -8
  14. data/lib/active_record/associations/collection_proxy.rb +22 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  19. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  20. data/lib/active_record/associations/join_dependency.rb +4 -2
  21. data/lib/active_record/associations/preloader/association.rb +2 -2
  22. data/lib/active_record/associations/preloader/batch.rb +7 -1
  23. data/lib/active_record/associations/preloader/branch.rb +1 -0
  24. data/lib/active_record/associations/singular_association.rb +8 -3
  25. data/lib/active_record/associations.rb +192 -24
  26. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  27. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  28. data/lib/active_record/attribute_methods/query.rb +34 -0
  29. data/lib/active_record/attribute_methods/serialization.rb +17 -4
  30. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  31. data/lib/active_record/attribute_methods.rb +24 -19
  32. data/lib/active_record/attributes.rb +40 -26
  33. data/lib/active_record/autosave_association.rb +91 -39
  34. data/lib/active_record/base.rb +3 -4
  35. data/lib/active_record/coders/json.rb +14 -5
  36. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  37. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  38. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
  39. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -117
  40. data/lib/active_record/connection_adapters/abstract/database_statements.rb +136 -74
  41. data/lib/active_record/connection_adapters/abstract/query_cache.rb +44 -11
  42. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  43. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  45. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  46. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -29
  47. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  48. data/lib/active_record/connection_adapters/abstract_adapter.rb +175 -87
  49. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +77 -58
  50. data/lib/active_record/connection_adapters/column.rb +17 -4
  51. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  52. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
  53. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  54. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  55. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
  56. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  57. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -11
  58. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  59. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  60. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  62. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  65. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  66. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  67. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +28 -45
  68. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  69. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +140 -64
  70. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -105
  71. data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
  72. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  73. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  74. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  75. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  76. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -13
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +112 -42
  78. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  79. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
  80. data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -19
  81. data/lib/active_record/connection_adapters.rb +1 -56
  82. data/lib/active_record/connection_handling.rb +37 -10
  83. data/lib/active_record/core.rb +61 -25
  84. data/lib/active_record/counter_cache.rb +34 -9
  85. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  86. data/lib/active_record/database_configurations/database_config.rb +9 -1
  87. data/lib/active_record/database_configurations/hash_config.rb +67 -9
  88. data/lib/active_record/database_configurations/url_config.rb +13 -3
  89. data/lib/active_record/database_configurations.rb +7 -3
  90. data/lib/active_record/delegated_type.rb +19 -19
  91. data/lib/active_record/dynamic_matchers.rb +54 -69
  92. data/lib/active_record/encryption/config.rb +3 -1
  93. data/lib/active_record/encryption/encryptable_record.rb +9 -9
  94. data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
  95. data/lib/active_record/encryption/encryptor.rb +49 -28
  96. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  97. data/lib/active_record/encryption/scheme.rb +9 -2
  98. data/lib/active_record/enum.rb +46 -42
  99. data/lib/active_record/errors.rb +36 -12
  100. data/lib/active_record/explain.rb +1 -1
  101. data/lib/active_record/explain_registry.rb +51 -2
  102. data/lib/active_record/filter_attribute_handler.rb +73 -0
  103. data/lib/active_record/fixture_set/table_row.rb +19 -2
  104. data/lib/active_record/fixtures.rb +2 -4
  105. data/lib/active_record/future_result.rb +13 -9
  106. data/lib/active_record/gem_version.rb +3 -3
  107. data/lib/active_record/inheritance.rb +1 -1
  108. data/lib/active_record/insert_all.rb +12 -7
  109. data/lib/active_record/locking/optimistic.rb +8 -1
  110. data/lib/active_record/locking/pessimistic.rb +5 -0
  111. data/lib/active_record/log_subscriber.rb +3 -13
  112. data/lib/active_record/middleware/shard_selector.rb +34 -17
  113. data/lib/active_record/migration/command_recorder.rb +44 -11
  114. data/lib/active_record/migration/compatibility.rb +37 -24
  115. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  116. data/lib/active_record/migration.rb +50 -43
  117. data/lib/active_record/model_schema.rb +38 -13
  118. data/lib/active_record/nested_attributes.rb +6 -6
  119. data/lib/active_record/persistence.rb +162 -133
  120. data/lib/active_record/query_cache.rb +22 -15
  121. data/lib/active_record/query_logs.rb +104 -52
  122. data/lib/active_record/query_logs_formatter.rb +17 -28
  123. data/lib/active_record/querying.rb +12 -12
  124. data/lib/active_record/railtie.rb +37 -32
  125. data/lib/active_record/railties/controller_runtime.rb +11 -6
  126. data/lib/active_record/railties/databases.rake +26 -37
  127. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  128. data/lib/active_record/railties/job_runtime.rb +10 -11
  129. data/lib/active_record/reflection.rb +53 -21
  130. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  131. data/lib/active_record/relation/batches.rb +147 -73
  132. data/lib/active_record/relation/calculations.rb +80 -63
  133. data/lib/active_record/relation/delegation.rb +25 -15
  134. data/lib/active_record/relation/finder_methods.rb +54 -37
  135. data/lib/active_record/relation/merger.rb +8 -8
  136. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  137. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  138. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  139. data/lib/active_record/relation/predicate_builder.rb +22 -7
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +156 -95
  142. data/lib/active_record/relation/spawn_methods.rb +7 -7
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +122 -80
  145. data/lib/active_record/result.rb +109 -24
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +9 -6
  148. data/lib/active_record/schema_dumper.rb +47 -22
  149. data/lib/active_record/schema_migration.rb +2 -1
  150. data/lib/active_record/scoping/named.rb +5 -2
  151. data/lib/active_record/scoping.rb +0 -1
  152. data/lib/active_record/secure_token.rb +3 -3
  153. data/lib/active_record/signed_id.rb +47 -18
  154. data/lib/active_record/statement_cache.rb +24 -20
  155. data/lib/active_record/store.rb +51 -22
  156. data/lib/active_record/structured_event_subscriber.rb +85 -0
  157. data/lib/active_record/table_metadata.rb +6 -23
  158. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  159. data/lib/active_record/tasks/database_tasks.rb +85 -85
  160. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  161. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  162. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  163. data/lib/active_record/test_databases.rb +14 -4
  164. data/lib/active_record/test_fixtures.rb +39 -2
  165. data/lib/active_record/testing/query_assertions.rb +8 -2
  166. data/lib/active_record/timestamp.rb +4 -2
  167. data/lib/active_record/token_for.rb +1 -1
  168. data/lib/active_record/transaction.rb +2 -5
  169. data/lib/active_record/transactions.rb +39 -16
  170. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  171. data/lib/active_record/type/internal/timezone.rb +7 -0
  172. data/lib/active_record/type/json.rb +15 -2
  173. data/lib/active_record/type/serialized.rb +11 -4
  174. data/lib/active_record/type/type_map.rb +1 -1
  175. data/lib/active_record/type_caster/connection.rb +2 -1
  176. data/lib/active_record/validations/associated.rb +1 -1
  177. data/lib/active_record/validations/uniqueness.rb +8 -8
  178. data/lib/active_record.rb +85 -50
  179. data/lib/arel/alias_predication.rb +2 -0
  180. data/lib/arel/collectors/bind.rb +2 -2
  181. data/lib/arel/collectors/sql_string.rb +1 -1
  182. data/lib/arel/collectors/substitute_binds.rb +2 -2
  183. data/lib/arel/crud.rb +8 -11
  184. data/lib/arel/delete_manager.rb +5 -0
  185. data/lib/arel/nodes/binary.rb +1 -1
  186. data/lib/arel/nodes/count.rb +2 -2
  187. data/lib/arel/nodes/delete_statement.rb +4 -2
  188. data/lib/arel/nodes/function.rb +4 -10
  189. data/lib/arel/nodes/named_function.rb +2 -2
  190. data/lib/arel/nodes/node.rb +2 -2
  191. data/lib/arel/nodes/sql_literal.rb +1 -1
  192. data/lib/arel/nodes/update_statement.rb +4 -2
  193. data/lib/arel/nodes.rb +0 -2
  194. data/lib/arel/select_manager.rb +13 -4
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/update_manager.rb +5 -0
  197. data/lib/arel/visitors/dot.rb +2 -3
  198. data/lib/arel/visitors/postgresql.rb +55 -0
  199. data/lib/arel/visitors/sqlite.rb +55 -8
  200. data/lib/arel/visitors/to_sql.rb +6 -22
  201. data/lib/arel.rb +3 -1
  202. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  203. metadata +17 -17
  204. data/lib/active_record/explain_subscriber.rb +0 -34
  205. data/lib/active_record/normalization.rb +0 -163
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -29,6 +29,11 @@ module ActiveRecord
29
29
  # ...
30
30
  # ]
31
31
  #
32
+ # # Get the number of rows affected by the query:
33
+ # result = ActiveRecord::Base.lease_connection.exec_query('INSERT INTO posts (title, body) VALUES ("title_3", "body_3"), ("title_4", "body_4")')
34
+ # result.affected_rows
35
+ # # => 2
36
+ #
32
37
  # # ActiveRecord::Result also includes Enumerable.
33
38
  # result.each do |row|
34
39
  # puts row['title'] + " " + row['body']
@@ -36,24 +41,79 @@ module ActiveRecord
36
41
  class Result
37
42
  include Enumerable
38
43
 
39
- attr_reader :columns, :rows, :column_types
44
+ class IndexedRow
45
+ def initialize(column_indexes, row)
46
+ @column_indexes = column_indexes
47
+ @row = row
48
+ end
49
+
50
+ def size
51
+ @column_indexes.size
52
+ end
53
+ alias_method :length, :size
54
+
55
+ def each_key(&block)
56
+ @column_indexes.each_key(&block)
57
+ end
58
+
59
+ def keys
60
+ @column_indexes.keys
61
+ end
62
+
63
+ def ==(other)
64
+ if other.is_a?(Hash)
65
+ to_hash == other
66
+ else
67
+ super
68
+ end
69
+ end
70
+
71
+ def key?(column)
72
+ @column_indexes.key?(column)
73
+ end
74
+
75
+ def fetch(column)
76
+ if index = @column_indexes[column]
77
+ @row[index]
78
+ elsif block_given?
79
+ yield
80
+ else
81
+ raise KeyError, "key not found: #{column.inspect}"
82
+ end
83
+ end
84
+
85
+ def [](column)
86
+ if index = @column_indexes[column]
87
+ @row[index]
88
+ end
89
+ end
90
+
91
+ def to_h
92
+ @column_indexes.transform_values { |index| @row[index] }
93
+ end
94
+ alias_method :to_hash, :to_h
95
+ end
40
96
 
41
- def self.empty(async: false) # :nodoc:
97
+ attr_reader :columns, :rows, :affected_rows
98
+
99
+ def self.empty(async: false, affected_rows: nil) # :nodoc:
42
100
  if async
43
- EMPTY_ASYNC
101
+ FutureResult.wrap(new(EMPTY_ARRAY, EMPTY_ARRAY, EMPTY_HASH, affected_rows: affected_rows)).freeze
44
102
  else
45
- EMPTY
103
+ new(EMPTY_ARRAY, EMPTY_ARRAY, EMPTY_HASH, affected_rows: affected_rows).freeze
46
104
  end
47
105
  end
48
106
 
49
- def initialize(columns, rows, column_types = nil)
107
+ def initialize(columns, rows, column_types = nil, affected_rows: nil)
50
108
  # We freeze the strings to prevent them getting duped when
51
109
  # used as keys in ActiveRecord::Base's @attributes hash
52
110
  @columns = columns.each(&:-@).freeze
53
111
  @rows = rows
54
112
  @hash_rows = nil
55
- @column_types = column_types || EMPTY_HASH
113
+ @column_types = column_types.freeze
114
+ @types_hash = nil
56
115
  @column_indexes = nil
116
+ @affected_rows = affected_rows
57
117
  end
58
118
 
59
119
  # Returns true if this result set includes the column named +name+
@@ -67,7 +127,9 @@ module ActiveRecord
67
127
  end
68
128
 
69
129
  # Calls the given block once for each element in row collection, passing
70
- # row as parameter.
130
+ # row as parameter. Each row is a Hash-like, read only object.
131
+ #
132
+ # To get real hashes, use +.to_a.each+.
71
133
  #
72
134
  # Returns an +Enumerator+ if no block is given.
73
135
  def each(&block)
@@ -99,6 +161,24 @@ module ActiveRecord
99
161
  n ? hash_rows.last(n) : hash_rows.last
100
162
  end
101
163
 
164
+ # Returns the +ActiveRecord::Type+ type of all columns.
165
+ # Note that not all database adapters return the result types,
166
+ # so the hash may be empty.
167
+ def column_types
168
+ if @column_types
169
+ @types_hash ||= begin
170
+ types = {}
171
+ @columns.each_with_index do |name, index|
172
+ type = @column_types[index] || Type.default_value
173
+ types[name] = types[index] = type
174
+ end
175
+ types.freeze
176
+ end
177
+ else
178
+ EMPTY_HASH
179
+ end
180
+ end
181
+
102
182
  def result # :nodoc:
103
183
  self
104
184
  end
@@ -107,7 +187,7 @@ module ActiveRecord
107
187
  self
108
188
  end
109
189
 
110
- def cast_values(type_overrides = {}) # :nodoc:
190
+ def cast_values(type_overrides = nil) # :nodoc:
111
191
  if columns.one?
112
192
  # Separated to avoid allocating an array per row
113
193
 
@@ -134,14 +214,14 @@ module ActiveRecord
134
214
  end
135
215
 
136
216
  def initialize_copy(other)
137
- @columns = columns
138
- @rows = rows.dup
139
- @column_types = column_types.dup
217
+ @rows = rows.dup
140
218
  @hash_rows = nil
141
219
  end
142
220
 
143
221
  def freeze # :nodoc:
144
222
  hash_rows.freeze
223
+ indexed_rows
224
+ column_types
145
225
  super
146
226
  end
147
227
 
@@ -149,21 +229,32 @@ module ActiveRecord
149
229
  @column_indexes ||= begin
150
230
  index = 0
151
231
  hash = {}
152
- length = columns.length
232
+ length = columns.length
153
233
  while index < length
154
234
  hash[columns[index]] = index
155
235
  index += 1
156
236
  end
157
- hash
237
+ hash.freeze
238
+ end
239
+ end
240
+
241
+ def indexed_rows # :nodoc:
242
+ @indexed_rows ||= begin
243
+ columns = column_indexes
244
+ @rows.map { |row| IndexedRow.new(columns, row) }.freeze
158
245
  end
159
246
  end
160
247
 
161
248
  private
162
249
  def column_type(name, index, type_overrides)
163
- type_overrides.fetch(name) do
164
- column_types.fetch(index) do
165
- column_types.fetch(name, Type.default_value)
250
+ if type_overrides
251
+ type_overrides.fetch(name) do
252
+ column_type(name, index, nil)
166
253
  end
254
+ elsif @column_types
255
+ @column_types[index] || Type.default_value
256
+ else
257
+ Type.default_value
167
258
  end
168
259
  end
169
260
 
@@ -175,14 +266,8 @@ module ActiveRecord
175
266
  end
176
267
  end
177
268
 
178
- empty_array = [].freeze
269
+ EMPTY_ARRAY = [].freeze
179
270
  EMPTY_HASH = {}.freeze
180
- private_constant :EMPTY_HASH
181
-
182
- EMPTY = new(empty_array, empty_array, EMPTY_HASH).freeze
183
- private_constant :EMPTY
184
-
185
- EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
186
- private_constant :EMPTY_ASYNC
271
+ private_constant :EMPTY_ARRAY, :EMPTY_HASH
187
272
  end
188
273
  end
@@ -3,80 +3,64 @@
3
3
  module ActiveRecord
4
4
  # This is a thread locals registry for Active Record. For example:
5
5
  #
6
- # ActiveRecord::RuntimeRegistry.sql_runtime
6
+ # ActiveRecord::RuntimeRegistry.stats.sql_runtime
7
7
  #
8
8
  # returns the connection handler local to the current unit of execution (either thread of fiber).
9
9
  module RuntimeRegistry # :nodoc:
10
- extend self
11
-
12
- def sql_runtime
13
- ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
10
+ class Stats
11
+ attr_accessor :sql_runtime, :async_sql_runtime, :queries_count, :cached_queries_count
12
+
13
+ def initialize
14
+ @sql_runtime = 0.0
15
+ @async_sql_runtime = 0.0
16
+ @queries_count = 0
17
+ @cached_queries_count = 0
18
+ end
19
+
20
+ def reset_runtimes
21
+ sql_runtime_was = @sql_runtime
22
+ @sql_runtime = 0.0
23
+ @async_sql_runtime = 0.0
24
+ sql_runtime_was
25
+ end
26
+
27
+ public alias_method :reset, :initialize
14
28
  end
15
29
 
16
- def sql_runtime=(runtime)
17
- ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
18
- end
19
-
20
- def async_sql_runtime
21
- ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] ||= 0.0
22
- end
30
+ extend self
23
31
 
24
- def async_sql_runtime=(runtime)
25
- ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
32
+ def call(name, start, finish, id, payload)
33
+ record(
34
+ payload[:name],
35
+ (finish - start) * 1_000.0,
36
+ cached: payload[:cached],
37
+ async: payload[:async],
38
+ lock_wait: payload[:lock_wait],
39
+ )
26
40
  end
27
41
 
28
- def queries_count
29
- ActiveSupport::IsolatedExecutionState[:active_record_queries_count] ||= 0
30
- end
42
+ def record(query_name, runtime, cached: false, async: false, lock_wait: nil)
43
+ stats = self.stats
31
44
 
32
- def queries_count=(count)
33
- ActiveSupport::IsolatedExecutionState[:active_record_queries_count] = count
34
- end
45
+ unless query_name == "TRANSACTION" || query_name == "SCHEMA"
46
+ stats.queries_count += 1
47
+ stats.cached_queries_count += 1 if cached
48
+ end
35
49
 
36
- def cached_queries_count
37
- ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] ||= 0
50
+ if async
51
+ stats.async_sql_runtime += (runtime - lock_wait)
52
+ end
53
+ stats.sql_runtime += runtime
38
54
  end
39
55
 
40
- def cached_queries_count=(count)
41
- ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] = count
56
+ def stats
57
+ ActiveSupport::IsolatedExecutionState[:active_record_runtime] ||= Stats.new
42
58
  end
43
59
 
44
60
  def reset
45
- reset_runtimes
46
- reset_queries_count
47
- reset_cached_queries_count
48
- end
49
-
50
- def reset_runtimes
51
- rt, self.sql_runtime = sql_runtime, 0.0
52
- self.async_sql_runtime = 0.0
53
- rt
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
61
+ stats.reset
66
62
  end
67
63
  end
68
64
  end
69
65
 
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
-
76
- runtime = (finish - start) * 1_000.0
77
-
78
- if payload[:async]
79
- ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
80
- end
81
- ActiveRecord::RuntimeRegistry.sql_runtime += runtime
82
- end
66
+ ActiveSupport::Notifications.monotonic_subscribe("sql.active_record", ActiveRecord::RuntimeRegistry)
@@ -105,12 +105,13 @@ module ActiveRecord
105
105
  # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
106
106
  # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
107
107
  def sanitize_sql_hash_for_assignment(attrs, table)
108
- c = connection
109
- attrs.map do |attr, value|
110
- type = type_for_attribute(attr)
111
- value = type.serialize(type.cast(value))
112
- "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
113
- end.join(", ")
108
+ with_connection do |c|
109
+ attrs.map do |attr, value|
110
+ type = type_for_attribute(attr)
111
+ value = type.serialize(type.cast(value))
112
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
113
+ end.join(", ")
114
+ end
114
115
  end
115
116
 
116
117
  # Sanitizes a +string+ so that it is safe to use within an SQL
@@ -160,6 +161,8 @@ module ActiveRecord
160
161
  #
161
162
  # sanitize_sql_array(["role = ?", 0])
162
163
  # # => "role = '0'"
164
+ #
165
+ # Before using this method, please consider if Arel.sql would be better for your use-case
163
166
  def sanitize_sql_array(ary)
164
167
  statement, *values = ary
165
168
  if values.first.is_a?(Hash) && /:\w+/.match?(statement)
@@ -63,6 +63,7 @@ module ActiveRecord
63
63
  extensions(stream)
64
64
  types(stream)
65
65
  tables(stream)
66
+ virtual_tables(stream)
66
67
  trailer(stream)
67
68
  stream
68
69
  end
@@ -126,6 +127,10 @@ module ActiveRecord
126
127
  def schemas(stream)
127
128
  end
128
129
 
130
+ # virtual tables are only supported by SQLite
131
+ def virtual_tables(stream)
132
+ end
133
+
129
134
  def tables(stream)
130
135
  sorted_tables = @connection.tables.sort
131
136
 
@@ -160,7 +165,7 @@ module ActiveRecord
160
165
  # first dump primary key column
161
166
  pk = @connection.primary_key(table)
162
167
 
163
- tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
168
+ tbl.print " create_table #{relation_name(remove_prefix_and_suffix(table)).inspect}"
164
169
 
165
170
  case pk
166
171
  when String
@@ -187,7 +192,7 @@ module ActiveRecord
187
192
  tbl.puts ", force: :cascade do |t|"
188
193
 
189
194
  # then dump all non-primary key columns
190
- columns.each do |column|
195
+ columns.sort_by(&:name).each do |column|
191
196
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
192
197
  next if column.name == pk
193
198
 
@@ -202,12 +207,17 @@ module ActiveRecord
202
207
  end
203
208
 
204
209
  indexes_in_create(table, tbl)
205
- check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
210
+ remaining = check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
206
211
  exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
207
212
  unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
208
213
 
209
214
  tbl.puts " end"
210
215
 
216
+ if remaining
217
+ tbl.puts
218
+ tbl.print remaining.string
219
+ end
220
+
211
221
  stream.print tbl.string
212
222
  rescue => e
213
223
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
@@ -222,8 +232,8 @@ module ActiveRecord
222
232
  def indexes(table, stream)
223
233
  if (indexes = @connection.indexes(table)).any?
224
234
  add_index_statements = indexes.map do |index|
225
- table_name = remove_prefix_and_suffix(index.table).inspect
226
- " add_index #{([table_name] + index_parts(index)).join(', ')}"
235
+ table_name = remove_prefix_and_suffix(index.table)
236
+ " add_index #{([relation_name(table_name).inspect] + index_parts(index)).join(', ')}"
227
237
  end
228
238
 
229
239
  stream.puts add_index_statements.sort.join("\n")
@@ -267,35 +277,49 @@ module ActiveRecord
267
277
  index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
268
278
  index_parts << "type: #{index.type.inspect}" if index.type
269
279
  index_parts << "comment: #{index.comment.inspect}" if index.comment
280
+ index_parts << "enabled: #{index.enabled.inspect}" if @connection.supports_disabling_indexes? && index.disabled?
270
281
  index_parts
271
282
  end
272
283
 
273
284
  def check_constraints_in_create(table, stream)
274
285
  if (check_constraints = @connection.check_constraints(table)).any?
275
- add_check_constraint_statements = check_constraints.map do |check_constraint|
276
- parts = [
277
- "t.check_constraint #{check_constraint.expression.inspect}"
278
- ]
286
+ check_valid, check_invalid = check_constraints.partition { |chk| chk.validate? }
279
287
 
280
- if check_constraint.export_name_on_schema_dump?
281
- parts << "name: #{check_constraint.name.inspect}"
288
+ unless check_valid.empty?
289
+ check_constraint_statements = check_valid.map do |check|
290
+ " t.check_constraint #{check_parts(check).join(', ')}"
282
291
  end
283
292
 
284
- parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
285
-
286
- " #{parts.join(', ')}"
293
+ stream.puts check_constraint_statements.sort.join("\n")
287
294
  end
288
295
 
289
- stream.puts add_check_constraint_statements.sort.join("\n")
296
+ unless check_invalid.empty?
297
+ remaining = StringIO.new
298
+ table_name = remove_prefix_and_suffix(table).inspect
299
+
300
+ add_check_constraint_statements = check_invalid.map do |check|
301
+ " add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
302
+ end
303
+
304
+ remaining.puts add_check_constraint_statements.sort.join("\n")
305
+ remaining
306
+ end
290
307
  end
291
308
  end
292
309
 
310
+ def check_parts(check)
311
+ check_parts = [ check.expression.inspect ]
312
+ check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
313
+ check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
314
+ check_parts
315
+ end
316
+
293
317
  def foreign_keys(table, stream)
294
318
  if (foreign_keys = @connection.foreign_keys(table)).any?
295
319
  add_foreign_key_statements = foreign_keys.map do |foreign_key|
296
320
  parts = [
297
- "add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
298
- remove_prefix_and_suffix(foreign_key.to_table).inspect,
321
+ relation_name(remove_prefix_and_suffix(foreign_key.from_table)).inspect,
322
+ relation_name(remove_prefix_and_suffix(foreign_key.to_table)).inspect,
299
323
  ]
300
324
 
301
325
  if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
@@ -306,16 +330,13 @@ module ActiveRecord
306
330
  parts << "primary_key: #{foreign_key.primary_key.inspect}"
307
331
  end
308
332
 
309
- if foreign_key.export_name_on_schema_dump?
310
- parts << "name: #{foreign_key.name.inspect}"
311
- end
312
-
333
+ parts << "name: #{foreign_key.name.inspect}" if foreign_key.export_name_on_schema_dump?
313
334
  parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
314
335
  parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
315
336
  parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
316
337
  parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
317
338
 
318
- " #{parts.join(', ')}"
339
+ " add_foreign_key #{parts.join(', ')}"
319
340
  end
320
341
 
321
342
  stream.puts add_foreign_key_statements.sort.join("\n")
@@ -340,6 +361,10 @@ module ActiveRecord
340
361
  end
341
362
  end
342
363
 
364
+ def relation_name(name)
365
+ name
366
+ end
367
+
343
368
  def remove_prefix_and_suffix(table)
344
369
  # This method appears at the top when profiling active_record test cases run.
345
370
  # Avoid costly calculation when there are no prefix and suffix.
@@ -34,7 +34,8 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  def delete_all_versions
37
- @pool.with_connection do |connection|
37
+ # Eagerly check in connection to avoid checking in/out many times in the called method.
38
+ @pool.with_connection do
38
39
  versions.each do |version|
39
40
  delete_version(version)
40
41
  end
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  scope = current_scope
24
24
 
25
25
  if scope
26
- if self == scope.klass
26
+ if self == scope.model
27
27
  scope.clone
28
28
  else
29
29
  relation.merge!(scope)
@@ -191,7 +191,10 @@ module ActiveRecord
191
191
  private
192
192
  def singleton_method_added(name)
193
193
  super
194
- generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
194
+ # Most Kernel extends are both singleton and instance methods so
195
+ # respond_to is a fast check, but we don't want to define methods
196
+ # only on the module (ex. Module#name)
197
+ generate_relation_method(name) if Kernel.respond_to?(name) && (Kernel.method_defined?(name) || Kernel.private_method_defined?(name)) && !ActiveRecord::Relation.method_defined?(name)
195
198
  end
196
199
  end
197
200
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/module/delegation"
4
3
 
5
4
  module ActiveRecord
6
5
  module Scoping
@@ -30,13 +30,13 @@ module ActiveRecord
30
30
  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
31
31
  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
32
32
  #
33
- # === Options
33
+ # ==== Options
34
34
  #
35
- # [:length]
35
+ # [+:length+]
36
36
  # Length of the Secure Random, with a minimum of 24 characters. It will
37
37
  # default to 24.
38
38
  #
39
- # [:on]
39
+ # [+:on+]
40
40
  # The callback when the value is generated. When called with <tt>on:
41
41
  # :initialize</tt>, the value is generated in an
42
42
  # <tt>after_initialize</tt> callback, otherwise the value will be used