activerecord 7.1.5 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2440
  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