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
@@ -18,70 +18,62 @@ module ActiveRecord
18
18
  @cache_path = cache_path
19
19
  end
20
20
 
21
- def set_schema_cache(cache)
22
- @cache = cache
23
- end
24
-
25
21
  def clear!
26
22
  @cache = empty_cache
27
23
 
28
24
  nil
29
25
  end
30
26
 
31
- def load!(connection)
32
- cache(connection)
27
+ def load!(pool)
28
+ cache(pool)
33
29
 
34
30
  self
35
31
  end
36
32
 
37
- def primary_keys(connection, table_name)
38
- cache(connection).primary_keys(connection, table_name)
33
+ def primary_keys(pool, table_name)
34
+ cache(pool).primary_keys(pool, table_name)
39
35
  end
40
36
 
41
- def data_source_exists?(connection, name)
42
- cache(connection).data_source_exists?(connection, name)
37
+ def data_source_exists?(pool, name)
38
+ cache(pool).data_source_exists?(pool, name)
43
39
  end
44
40
 
45
- def add(connection, name)
46
- cache(connection).add(connection, name)
41
+ def add(pool, name)
42
+ cache(pool).add(pool, name)
47
43
  end
48
44
 
49
- def data_sources(connection, name)
50
- cache(connection).data_sources(connection, name)
45
+ def data_sources(pool, name)
46
+ cache(pool).data_source_exists?(pool, name)
51
47
  end
52
48
 
53
- def columns(connection, table_name)
54
- cache(connection).columns(connection, table_name)
49
+ def columns(pool, table_name)
50
+ cache(pool).columns(pool, table_name)
55
51
  end
56
52
 
57
- def columns_hash(connection, table_name)
58
- cache(connection).columns_hash(connection, table_name)
53
+ def columns_hash(pool, table_name)
54
+ cache(pool).columns_hash(pool, table_name)
59
55
  end
60
56
 
61
- def columns_hash?(connection, table_name)
62
- cache(connection).columns_hash?(connection, table_name)
57
+ def columns_hash?(pool, table_name)
58
+ cache(pool).columns_hash?(pool, table_name)
63
59
  end
64
60
 
65
- def indexes(connection, table_name)
66
- cache(connection).indexes(connection, table_name)
61
+ def indexes(pool, table_name)
62
+ cache(pool).indexes(pool, table_name)
67
63
  end
68
64
 
69
- def database_version(connection) # :nodoc:
70
- cache(connection).database_version(connection)
65
+ def version(pool)
66
+ cache(pool).version(pool)
71
67
  end
72
68
 
73
- def version(connection)
74
- cache(connection).version(connection)
69
+ def size(pool)
70
+ cache(pool).size
75
71
  end
76
72
 
77
- def size(connection)
78
- cache(connection).size
79
- end
80
-
81
- def clear_data_source_cache!(connection, name)
73
+ def clear_data_source_cache!(pool, name)
82
74
  return if @cache.nil? && !possible_cache_available?
83
75
 
84
- cache(connection).clear_data_source_cache!(connection, name)
76
+ cache(pool).clear_data_source_cache!(pool, name)
85
77
  end
86
78
 
87
79
  def cached?(table_name)
@@ -96,9 +88,9 @@ module ActiveRecord
96
88
  @cache&.cached?(table_name)
97
89
  end
98
90
 
99
- def dump_to(connection, filename)
91
+ def dump_to(pool, filename)
100
92
  fresh_cache = empty_cache
101
- fresh_cache.add_all(connection)
93
+ fresh_cache.add_all(pool)
102
94
  fresh_cache.dump_to(filename)
103
95
 
104
96
  @cache = fresh_cache
@@ -111,8 +103,8 @@ module ActiveRecord
111
103
  new_cache
112
104
  end
113
105
 
114
- def cache(connection)
115
- @cache ||= load_cache(connection) || empty_cache
106
+ def cache(pool)
107
+ @cache ||= load_cache(pool) || empty_cache
116
108
  end
117
109
 
118
110
  def possible_cache_available?
@@ -121,7 +113,7 @@ module ActiveRecord
121
113
  File.file?(@cache_path)
122
114
  end
123
115
 
124
- def load_cache(connection)
116
+ def load_cache(pool)
125
117
  # Can't load if schema dumps are disabled
126
118
  return unless possible_cache_available?
127
119
 
@@ -130,11 +122,13 @@ module ActiveRecord
130
122
 
131
123
  if self.class.check_schema_cache_dump_version
132
124
  begin
133
- current_version = connection.schema_version
125
+ pool.with_connection do |connection|
126
+ current_version = connection.schema_version
134
127
 
135
- if new_cache.version(connection) != current_version
136
- warn "Ignoring #{@cache_path} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{new_cache.schema_version}."
137
- return
128
+ if new_cache.version(connection) != current_version
129
+ warn "Ignoring #{@cache_path} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{new_cache.schema_version}."
130
+ return
131
+ end
138
132
  end
139
133
  rescue ActiveRecordError => error
140
134
  warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
@@ -147,9 +141,25 @@ module ActiveRecord
147
141
  end
148
142
 
149
143
  class BoundSchemaReflection
150
- def initialize(abstract_schema_reflection, connection)
144
+ class FakePool # :nodoc
145
+ def initialize(connection)
146
+ @connection = connection
147
+ end
148
+
149
+ def with_connection
150
+ yield @connection
151
+ end
152
+ end
153
+
154
+ class << self
155
+ def for_lone_connection(abstract_schema_reflection, connection) # :nodoc:
156
+ new(abstract_schema_reflection, FakePool.new(connection))
157
+ end
158
+ end
159
+
160
+ def initialize(abstract_schema_reflection, pool)
151
161
  @schema_reflection = abstract_schema_reflection
152
- @connection = connection
162
+ @pool = pool
153
163
  end
154
164
 
155
165
  def clear!
@@ -157,7 +167,7 @@ module ActiveRecord
157
167
  end
158
168
 
159
169
  def load!
160
- @schema_reflection.load!(@connection)
170
+ @schema_reflection.load!(@pool)
161
171
  end
162
172
 
163
173
  def cached?(table_name)
@@ -165,72 +175,56 @@ module ActiveRecord
165
175
  end
166
176
 
167
177
  def primary_keys(table_name)
168
- @schema_reflection.primary_keys(@connection, table_name)
178
+ @schema_reflection.primary_keys(@pool, table_name)
169
179
  end
170
180
 
171
181
  def data_source_exists?(name)
172
- @schema_reflection.data_source_exists?(@connection, name)
182
+ @schema_reflection.data_source_exists?(@pool, name)
173
183
  end
174
184
 
175
185
  def add(name)
176
- @schema_reflection.add(@connection, name)
186
+ @schema_reflection.add(@pool, name)
177
187
  end
178
188
 
179
189
  def data_sources(name)
180
- @schema_reflection.data_sources(@connection, name)
190
+ @schema_reflection.data_sources(@pool, name)
181
191
  end
182
192
 
183
193
  def columns(table_name)
184
- @schema_reflection.columns(@connection, table_name)
194
+ @schema_reflection.columns(@pool, table_name)
185
195
  end
186
196
 
187
197
  def columns_hash(table_name)
188
- @schema_reflection.columns_hash(@connection, table_name)
198
+ @schema_reflection.columns_hash(@pool, table_name)
189
199
  end
190
200
 
191
201
  def columns_hash?(table_name)
192
- @schema_reflection.columns_hash?(@connection, table_name)
202
+ @schema_reflection.columns_hash?(@pool, table_name)
193
203
  end
194
204
 
195
205
  def indexes(table_name)
196
- @schema_reflection.indexes(@connection, table_name)
197
- end
198
-
199
- def database_version # :nodoc:
200
- @schema_reflection.database_version(@connection)
206
+ @schema_reflection.indexes(@pool, table_name)
201
207
  end
202
208
 
203
209
  def version
204
- @schema_reflection.version(@connection)
210
+ @schema_reflection.version(@pool)
205
211
  end
206
212
 
207
213
  def size
208
- @schema_reflection.size(@connection)
214
+ @schema_reflection.size(@pool)
209
215
  end
210
216
 
211
217
  def clear_data_source_cache!(name)
212
- @schema_reflection.clear_data_source_cache!(@connection, name)
218
+ @schema_reflection.clear_data_source_cache!(@pool, name)
213
219
  end
214
220
 
215
221
  def dump_to(filename)
216
- @schema_reflection.dump_to(@connection, filename)
222
+ @schema_reflection.dump_to(@pool, filename)
217
223
  end
218
224
  end
219
225
 
220
226
  # = Active Record Connection Adapters Schema Cache
221
227
  class SchemaCache
222
- class << self
223
- def new(connection)
224
- BoundSchemaReflection.new(SchemaReflection.new(nil), connection)
225
- end
226
- deprecate new: "use ActiveRecord::ConnectionAdapters::SchemaReflection instead", deprecator: ActiveRecord.deprecator
227
-
228
- def load_from(filename) # :nodoc:
229
- BoundSchemaReflection.new(SchemaReflection.new(filename), nil)
230
- end
231
- deprecate load_from: "use ActiveRecord::ConnectionAdapters::SchemaReflection instead", deprecator: ActiveRecord.deprecator
232
- end
233
-
234
228
  def self._load_from(filename) # :nodoc:
235
229
  return unless File.file?(filename)
236
230
 
@@ -258,13 +252,12 @@ module ActiveRecord
258
252
  end
259
253
  private_class_method :read
260
254
 
261
- def initialize
255
+ def initialize # :nodoc:
262
256
  @columns = {}
263
257
  @columns_hash = {}
264
258
  @primary_keys = {}
265
259
  @data_sources = {}
266
260
  @indexes = {}
267
- @database_version = nil
268
261
  @version = nil
269
262
  end
270
263
 
@@ -283,17 +276,15 @@ module ActiveRecord
283
276
  coder["data_sources"] = @data_sources.sort.to_h
284
277
  coder["indexes"] = @indexes.sort.to_h
285
278
  coder["version"] = @version
286
- coder["database_version"] = @database_version
287
279
  end
288
280
 
289
- def init_with(coder)
281
+ def init_with(coder) # :nodoc:
290
282
  @columns = coder["columns"]
291
283
  @columns_hash = coder["columns_hash"]
292
284
  @primary_keys = coder["primary_keys"]
293
285
  @data_sources = coder["data_sources"]
294
286
  @indexes = coder["indexes"] || {}
295
287
  @version = coder["version"]
296
- @database_version = coder["database_version"]
297
288
 
298
289
  unless coder["deduplicated"]
299
290
  derive_columns_hash_and_deduplicate_values
@@ -304,78 +295,85 @@ module ActiveRecord
304
295
  @columns.key?(table_name)
305
296
  end
306
297
 
307
- def primary_keys(connection, table_name)
298
+ def primary_keys(pool, table_name)
308
299
  @primary_keys.fetch(table_name) do
309
- if data_source_exists?(connection, table_name)
310
- @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
300
+ pool.with_connection do |connection|
301
+ if data_source_exists?(pool, table_name)
302
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
303
+ end
311
304
  end
312
305
  end
313
306
  end
314
307
 
315
308
  # A cached lookup for table existence.
316
- def data_source_exists?(connection, name)
309
+ def data_source_exists?(pool, name)
317
310
  return if ignored_table?(name)
318
- prepare_data_sources(connection) if @data_sources.empty?
311
+
312
+ if @data_sources.empty?
313
+ tables_to_cache(pool).each do |source|
314
+ @data_sources[source] = true
315
+ end
316
+ end
317
+
319
318
  return @data_sources[name] if @data_sources.key? name
320
319
 
321
- @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
320
+ @data_sources[deep_deduplicate(name)] = pool.with_connection do |connection|
321
+ connection.data_source_exists?(name)
322
+ end
322
323
  end
323
324
 
324
325
  # Add internal cache for table with +table_name+.
325
- def add(connection, table_name)
326
- if data_source_exists?(connection, table_name)
327
- primary_keys(connection, table_name)
328
- columns(connection, table_name)
329
- columns_hash(connection, table_name)
330
- indexes(connection, table_name)
326
+ def add(pool, table_name)
327
+ pool.with_connection do
328
+ if data_source_exists?(pool, table_name)
329
+ primary_keys(pool, table_name)
330
+ columns(pool, table_name)
331
+ columns_hash(pool, table_name)
332
+ indexes(pool, table_name)
333
+ end
331
334
  end
332
335
  end
333
336
 
334
- def data_sources(_connection, name) # :nodoc:
335
- @data_sources[name]
336
- end
337
- deprecate data_sources: :data_source_exists?, deprecator: ActiveRecord.deprecator
338
-
339
337
  # Get the columns for a table
340
- def columns(connection, table_name)
338
+ def columns(pool, table_name)
341
339
  if ignored_table?(table_name)
342
- raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
340
+ raise ActiveRecord::StatementInvalid.new("Table '#{table_name}' doesn't exist", connection_pool: pool)
343
341
  end
344
342
 
345
343
  @columns.fetch(table_name) do
346
- @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
344
+ pool.with_connection do |connection|
345
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
346
+ end
347
347
  end
348
348
  end
349
349
 
350
350
  # Get the columns for a table as a hash, key is the column name
351
351
  # value is the column object.
352
- def columns_hash(connection, table_name)
352
+ def columns_hash(pool, table_name)
353
353
  @columns_hash.fetch(table_name) do
354
- @columns_hash[deep_deduplicate(table_name)] = columns(connection, table_name).index_by(&:name).freeze
354
+ @columns_hash[deep_deduplicate(table_name)] = columns(pool, table_name).index_by(&:name).freeze
355
355
  end
356
356
  end
357
357
 
358
358
  # Checks whether the columns hash is already cached for a table.
359
- def columns_hash?(connection, table_name)
359
+ def columns_hash?(_pool, table_name)
360
360
  @columns_hash.key?(table_name)
361
361
  end
362
362
 
363
- def indexes(connection, table_name)
363
+ def indexes(pool, table_name)
364
364
  @indexes.fetch(table_name) do
365
- if data_source_exists?(connection, table_name)
366
- @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
367
- else
368
- []
365
+ pool.with_connection do |connection|
366
+ if data_source_exists?(pool, table_name)
367
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
368
+ else
369
+ []
370
+ end
369
371
  end
370
372
  end
371
373
  end
372
374
 
373
- def database_version(connection) # :nodoc:
374
- @database_version ||= connection.get_database_version
375
- end
376
-
377
- def version(connection)
378
- @version ||= connection.schema_version
375
+ def version(pool)
376
+ @version ||= pool.with_connection(&:schema_version)
379
377
  end
380
378
 
381
379
  def schema_version
@@ -395,13 +393,14 @@ module ActiveRecord
395
393
  @indexes.delete name
396
394
  end
397
395
 
398
- def add_all(connection) # :nodoc:
399
- tables_to_cache(connection).each do |table|
400
- add(connection, table)
401
- end
396
+ def add_all(pool) # :nodoc:
397
+ pool.with_connection do
398
+ tables_to_cache(pool).each do |table|
399
+ add(pool, table)
400
+ end
402
401
 
403
- version(connection)
404
- database_version(connection)
402
+ version(pool)
403
+ end
405
404
  end
406
405
 
407
406
  def dump_to(filename)
@@ -415,20 +414,22 @@ module ActiveRecord
415
414
  end
416
415
 
417
416
  def marshal_dump # :nodoc:
418
- [@version, @columns, {}, @primary_keys, @data_sources, @indexes, @database_version]
417
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes]
419
418
  end
420
419
 
421
420
  def marshal_load(array) # :nodoc:
422
- @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
421
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, _database_version = array
423
422
  @indexes ||= {}
424
423
 
425
424
  derive_columns_hash_and_deduplicate_values
426
425
  end
427
426
 
428
427
  private
429
- def tables_to_cache(connection)
430
- connection.data_sources.reject do |table|
431
- ignored_table?(table)
428
+ def tables_to_cache(pool)
429
+ pool.with_connection do |connection|
430
+ connection.data_sources.reject do |table|
431
+ ignored_table?(table)
432
+ end
432
433
  end
433
434
  end
434
435
 
@@ -459,12 +460,6 @@ module ActiveRecord
459
460
  end
460
461
  end
461
462
 
462
- def prepare_data_sources(connection)
463
- tables_to_cache(connection).each do |source|
464
- @data_sources[source] = true
465
- end
466
- end
467
-
468
463
  def open(filename)
469
464
  FileUtils.mkdir_p(File.dirname(filename))
470
465
 
@@ -6,10 +6,11 @@ module ActiveRecord
6
6
  class Column < ConnectionAdapters::Column # :nodoc:
7
7
  attr_reader :rowid
8
8
 
9
- def initialize(*, auto_increment: nil, rowid: false, **)
9
+ def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
10
10
  super
11
11
  @auto_increment = auto_increment
12
12
  @rowid = rowid
13
+ @generated_type = generated_type
13
14
  end
14
15
 
15
16
  def auto_increment?
@@ -20,6 +21,18 @@ module ActiveRecord
20
21
  auto_increment? || rowid
21
22
  end
22
23
 
24
+ def virtual?
25
+ !@generated_type.nil?
26
+ end
27
+
28
+ def virtual_stored?
29
+ virtual? && @generated_type == :stored
30
+ end
31
+
32
+ def has_default?
33
+ super && !virtual?
34
+ end
35
+
23
36
  def init_with(coder)
24
37
  @auto_increment = coder["auto_increment"]
25
38
  super
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  SQLite3::ExplainPrettyPrinter.new.pp(result)
22
22
  end
23
23
 
24
- def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
24
+ def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
25
25
  sql = transform_query(sql)
26
26
  check_if_write_query(sql)
27
27
 
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
 
30
30
  type_casted_binds = type_casted_binds(binds)
31
31
 
32
- log(sql, name, binds, type_casted_binds, async: async) do
32
+ log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
33
33
  with_raw_connection do |conn|
34
34
  # Don't cache statements if they are not prepared
35
35
  unless prepare
@@ -52,7 +52,9 @@ module ActiveRecord
52
52
  end
53
53
  verified!
54
54
 
55
- build_result(columns: cols, rows: records)
55
+ result = build_result(columns: cols, rows: records)
56
+ notification_payload[:row_count] = result.length
57
+ result
56
58
  end
57
59
  end
58
60
  end
@@ -104,7 +106,7 @@ module ActiveRecord
104
106
 
105
107
  # https://stackoverflow.com/questions/17574784
106
108
  # https://www.sqlite.org/lang_datefunc.html
107
- HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
109
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')", retryable: true).freeze # :nodoc:
108
110
  private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
109
111
 
110
112
  def high_precision_current_timestamp
@@ -113,10 +115,11 @@ module ActiveRecord
113
115
 
114
116
  private
115
117
  def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
116
- log(sql, name, async: async) do
118
+ log(sql, name, async: async) do |notification_payload|
117
119
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
118
120
  result = conn.execute(sql)
119
121
  verified!
122
+ notification_payload[:row_count] = result.length
120
123
  result
121
124
  end
122
125
  end
@@ -136,10 +139,11 @@ module ActiveRecord
136
139
  check_if_write_query(sql)
137
140
  mark_transaction_written_if_write(sql)
138
141
 
139
- log(sql, name) do
142
+ log(sql, name) do |notification_payload|
140
143
  with_raw_connection do |conn|
141
144
  result = conn.execute_batch2(sql)
142
145
  verified!
146
+ notification_payload[:row_count] = result.length
143
147
  result
144
148
  end
145
149
  end
@@ -4,9 +4,52 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  module Quoting # :nodoc:
7
+ extend ActiveSupport::Concern
8
+
7
9
  QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
10
  QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
9
11
 
12
+ module ClassMethods # :nodoc:
13
+ def column_name_matcher
14
+ /
15
+ \A
16
+ (
17
+ (?:
18
+ # "table_name"."column_name" | function(one or no argument)
19
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
20
+ )
21
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
22
+ )
23
+ (?:\s*,\s*\g<1>)*
24
+ \z
25
+ /ix
26
+ end
27
+
28
+ def column_name_with_order_matcher
29
+ /
30
+ \A
31
+ (
32
+ (?:
33
+ # "table_name"."column_name" | function(one or no argument)
34
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
35
+ )
36
+ (?:\s+COLLATE\s+(?:\w+|"\w+"))?
37
+ (?:\s+ASC|\s+DESC)?
38
+ )
39
+ (?:\s*,\s*\g<1>)*
40
+ \z
41
+ /ix
42
+ end
43
+
44
+ def quote_column_name(name)
45
+ QUOTED_COLUMN_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""')}").freeze
46
+ end
47
+
48
+ def quote_table_name(name)
49
+ QUOTED_TABLE_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""').gsub(".", "\".\"")}").freeze
50
+ end
51
+ end
52
+
10
53
  def quote_string(s)
11
54
  ::SQLite3::Database.quote(s)
12
55
  end
@@ -15,14 +58,6 @@ module ActiveRecord
15
58
  quote_column_name(attr)
16
59
  end
17
60
 
18
- def quote_table_name(name)
19
- QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "\".\"").freeze
20
- end
21
-
22
- def quote_column_name(name)
23
- QUOTED_COLUMN_NAMES[name] ||= %Q("#{super.gsub('"', '""')}")
24
- end
25
-
26
61
  def quoted_time(value)
27
62
  value = value.change(year: 2000, month: 1, day: 1)
28
63
  quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
@@ -63,7 +98,7 @@ module ActiveRecord
63
98
 
64
99
  def type_cast(value) # :nodoc:
65
100
  case value
66
- when BigDecimal
101
+ when BigDecimal, Rational
67
102
  value.to_f
68
103
  when String
69
104
  if value.encoding == Encoding::ASCII_8BIT
@@ -75,43 +110,6 @@ module ActiveRecord
75
110
  super
76
111
  end
77
112
  end
78
-
79
- def column_name_matcher
80
- COLUMN_NAME
81
- end
82
-
83
- def column_name_with_order_matcher
84
- COLUMN_NAME_WITH_ORDER
85
- end
86
-
87
- COLUMN_NAME = /
88
- \A
89
- (
90
- (?:
91
- # "table_name"."column_name" | function(one or no argument)
92
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
93
- )
94
- (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
95
- )
96
- (?:\s*,\s*\g<1>)*
97
- \z
98
- /ix
99
-
100
- COLUMN_NAME_WITH_ORDER = /
101
- \A
102
- (
103
- (?:
104
- # "table_name"."column_name" | function(one or no argument)
105
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
106
- )
107
- (?:\s+COLLATE\s+(?:\w+|"\w+"))?
108
- (?:\s+ASC|\s+DESC)?
109
- )
110
- (?:\s*,\s*\g<1>)*
111
- \z
112
- /ix
113
-
114
- private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
115
113
  end
116
114
  end
117
115
  end