activerecord 7.0.8.7 → 7.1.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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +193 -97
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. data/lib/active_record/null_relation.rb +0 -63
@@ -4,8 +4,234 @@ require "active_support/core_ext/file/atomic"
4
4
 
5
5
  module ActiveRecord
6
6
  module ConnectionAdapters
7
+ class SchemaReflection
8
+ class << self
9
+ attr_accessor :use_schema_cache_dump
10
+ attr_accessor :check_schema_cache_dump_version
11
+ end
12
+
13
+ self.use_schema_cache_dump = true
14
+ self.check_schema_cache_dump_version = true
15
+
16
+ def initialize(cache_path, cache = nil)
17
+ @cache = cache
18
+ @cache_path = cache_path
19
+ end
20
+
21
+ def set_schema_cache(cache)
22
+ @cache = cache
23
+ end
24
+
25
+ def clear!
26
+ @cache = empty_cache
27
+
28
+ nil
29
+ end
30
+
31
+ def load!(connection)
32
+ cache(connection)
33
+
34
+ self
35
+ end
36
+
37
+ def primary_keys(connection, table_name)
38
+ cache(connection).primary_keys(connection, table_name)
39
+ end
40
+
41
+ def data_source_exists?(connection, name)
42
+ cache(connection).data_source_exists?(connection, name)
43
+ end
44
+
45
+ def add(connection, name)
46
+ cache(connection).add(connection, name)
47
+ end
48
+
49
+ def data_sources(connection, name)
50
+ cache(connection).data_sources(connection, name)
51
+ end
52
+
53
+ def columns(connection, table_name)
54
+ cache(connection).columns(connection, table_name)
55
+ end
56
+
57
+ def columns_hash(connection, table_name)
58
+ cache(connection).columns_hash(connection, table_name)
59
+ end
60
+
61
+ def columns_hash?(connection, table_name)
62
+ cache(connection).columns_hash?(connection, table_name)
63
+ end
64
+
65
+ def indexes(connection, table_name)
66
+ cache(connection).indexes(connection, table_name)
67
+ end
68
+
69
+ def database_version(connection)
70
+ cache(connection).database_version(connection)
71
+ end
72
+
73
+ def version(connection)
74
+ cache(connection).version(connection)
75
+ end
76
+
77
+ def size(connection)
78
+ cache(connection).size
79
+ end
80
+
81
+ def clear_data_source_cache!(connection, name)
82
+ return if @cache.nil? && !possible_cache_available?
83
+
84
+ cache(connection).clear_data_source_cache!(connection, name)
85
+ end
86
+
87
+ def cached?(table_name)
88
+ if @cache.nil?
89
+ # If `check_schema_cache_dump_version` is enabled we can't load
90
+ # the schema cache dump without connecting to the database.
91
+ unless self.class.check_schema_cache_dump_version
92
+ @cache = load_cache(nil)
93
+ end
94
+ end
95
+
96
+ @cache&.cached?(table_name)
97
+ end
98
+
99
+ def dump_to(connection, filename)
100
+ fresh_cache = empty_cache
101
+ fresh_cache.add_all(connection)
102
+ fresh_cache.dump_to(filename)
103
+
104
+ @cache = fresh_cache
105
+ end
106
+
107
+ private
108
+ def empty_cache
109
+ new_cache = SchemaCache.allocate
110
+ new_cache.send(:initialize)
111
+ new_cache
112
+ end
113
+
114
+ def cache(connection)
115
+ @cache ||= load_cache(connection) || empty_cache
116
+ end
117
+
118
+ def possible_cache_available?
119
+ self.class.use_schema_cache_dump &&
120
+ @cache_path &&
121
+ File.file?(@cache_path)
122
+ end
123
+
124
+ def load_cache(connection)
125
+ # Can't load if schema dumps are disabled
126
+ return unless possible_cache_available?
127
+
128
+ # Check we can find one
129
+ return unless new_cache = SchemaCache._load_from(@cache_path)
130
+
131
+ if self.class.check_schema_cache_dump_version
132
+ begin
133
+ current_version = connection.schema_version
134
+
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
138
+ end
139
+ rescue ActiveRecordError => error
140
+ warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
141
+ return
142
+ end
143
+ end
144
+
145
+ new_cache
146
+ end
147
+ end
148
+
149
+ class BoundSchemaReflection
150
+ def initialize(abstract_schema_reflection, connection)
151
+ @schema_reflection = abstract_schema_reflection
152
+ @connection = connection
153
+ end
154
+
155
+ def clear!
156
+ @schema_reflection.clear!
157
+ end
158
+
159
+ def load!
160
+ @schema_reflection.load!(@connection)
161
+ end
162
+
163
+ def cached?(table_name)
164
+ @schema_reflection.cached?(table_name)
165
+ end
166
+
167
+ def primary_keys(table_name)
168
+ @schema_reflection.primary_keys(@connection, table_name)
169
+ end
170
+
171
+ def data_source_exists?(name)
172
+ @schema_reflection.data_source_exists?(@connection, name)
173
+ end
174
+
175
+ def add(name)
176
+ @schema_reflection.add(@connection, name)
177
+ end
178
+
179
+ def data_sources(name)
180
+ @schema_reflection.data_sources(@connection, name)
181
+ end
182
+
183
+ def columns(table_name)
184
+ @schema_reflection.columns(@connection, table_name)
185
+ end
186
+
187
+ def columns_hash(table_name)
188
+ @schema_reflection.columns_hash(@connection, table_name)
189
+ end
190
+
191
+ def columns_hash?(table_name)
192
+ @schema_reflection.columns_hash?(@connection, table_name)
193
+ end
194
+
195
+ def indexes(table_name)
196
+ @schema_reflection.indexes(@connection, table_name)
197
+ end
198
+
199
+ def database_version
200
+ @schema_reflection.database_version(@connection)
201
+ end
202
+
203
+ def version
204
+ @schema_reflection.version(@connection)
205
+ end
206
+
207
+ def size
208
+ @schema_reflection.size(@connection)
209
+ end
210
+
211
+ def clear_data_source_cache!(name)
212
+ @schema_reflection.clear_data_source_cache!(@connection, name)
213
+ end
214
+
215
+ def dump_to(filename)
216
+ @schema_reflection.dump_to(@connection, filename)
217
+ end
218
+ end
219
+
220
+ # = Active Record Connection Adapters Schema Cache
7
221
  class SchemaCache
8
- def self.load_from(filename)
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
+ def self._load_from(filename) # :nodoc:
9
235
  return unless File.file?(filename)
10
236
 
11
237
  read(filename) do |file|
@@ -32,20 +258,17 @@ module ActiveRecord
32
258
  end
33
259
  private_class_method :read
34
260
 
35
- attr_reader :version
36
- attr_accessor :connection
37
-
38
- def initialize(conn)
39
- @connection = conn
40
-
261
+ def initialize
41
262
  @columns = {}
42
263
  @columns_hash = {}
43
264
  @primary_keys = {}
44
265
  @data_sources = {}
45
266
  @indexes = {}
267
+ @database_version = nil
268
+ @version = nil
46
269
  end
47
270
 
48
- def initialize_dup(other)
271
+ def initialize_dup(other) # :nodoc:
49
272
  super
50
273
  @columns = @columns.dup
51
274
  @columns_hash = @columns_hash.dup
@@ -54,61 +277,67 @@ module ActiveRecord
54
277
  @indexes = @indexes.dup
55
278
  end
56
279
 
57
- def encode_with(coder)
58
- reset_version!
59
-
60
- coder["columns"] = @columns
61
- coder["primary_keys"] = @primary_keys
62
- coder["data_sources"] = @data_sources
63
- coder["indexes"] = @indexes
280
+ def encode_with(coder) # :nodoc:
281
+ coder["columns"] = @columns.sort.to_h
282
+ coder["primary_keys"] = @primary_keys.sort.to_h
283
+ coder["data_sources"] = @data_sources.sort.to_h
284
+ coder["indexes"] = @indexes.sort.to_h
64
285
  coder["version"] = @version
65
- coder["database_version"] = database_version
286
+ coder["database_version"] = @database_version
66
287
  end
67
288
 
68
289
  def init_with(coder)
69
290
  @columns = coder["columns"]
291
+ @columns_hash = coder["columns_hash"]
70
292
  @primary_keys = coder["primary_keys"]
71
293
  @data_sources = coder["data_sources"]
72
294
  @indexes = coder["indexes"] || {}
73
295
  @version = coder["version"]
74
296
  @database_version = coder["database_version"]
75
297
 
76
- derive_columns_hash_and_deduplicate_values
298
+ unless coder["deduplicated"]
299
+ derive_columns_hash_and_deduplicate_values
300
+ end
77
301
  end
78
302
 
79
- def primary_keys(table_name)
303
+ def cached?(table_name)
304
+ @columns.key?(table_name)
305
+ end
306
+
307
+ def primary_keys(connection, table_name)
80
308
  @primary_keys.fetch(table_name) do
81
- if data_source_exists?(table_name)
309
+ if data_source_exists?(connection, table_name)
82
310
  @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
83
311
  end
84
312
  end
85
313
  end
86
314
 
87
315
  # A cached lookup for table existence.
88
- def data_source_exists?(name)
316
+ def data_source_exists?(connection, name)
89
317
  return if ignored_table?(name)
90
- prepare_data_sources if @data_sources.empty?
318
+ prepare_data_sources(connection) if @data_sources.empty?
91
319
  return @data_sources[name] if @data_sources.key? name
92
320
 
93
321
  @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
94
322
  end
95
323
 
96
324
  # Add internal cache for table with +table_name+.
97
- def add(table_name)
98
- if data_source_exists?(table_name)
99
- primary_keys(table_name)
100
- columns(table_name)
101
- columns_hash(table_name)
102
- indexes(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)
103
331
  end
104
332
  end
105
333
 
106
- def data_sources(name)
334
+ def data_sources(_connection, name) # :nodoc:
107
335
  @data_sources[name]
108
336
  end
337
+ deprecate data_sources: :data_source_exists?, deprecator: ActiveRecord.deprecator
109
338
 
110
339
  # Get the columns for a table
111
- def columns(table_name)
340
+ def columns(connection, table_name)
112
341
  if ignored_table?(table_name)
113
342
  raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
114
343
  end
@@ -120,20 +349,20 @@ module ActiveRecord
120
349
 
121
350
  # Get the columns for a table as a hash, key is the column name
122
351
  # value is the column object.
123
- def columns_hash(table_name)
352
+ def columns_hash(connection, table_name)
124
353
  @columns_hash.fetch(table_name) do
125
- @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
354
+ @columns_hash[deep_deduplicate(table_name)] = columns(connection, table_name).index_by(&:name).freeze
126
355
  end
127
356
  end
128
357
 
129
358
  # Checks whether the columns hash is already cached for a table.
130
- def columns_hash?(table_name)
359
+ def columns_hash?(connection, table_name)
131
360
  @columns_hash.key?(table_name)
132
361
  end
133
362
 
134
- def indexes(table_name)
363
+ def indexes(connection, table_name)
135
364
  @indexes.fetch(table_name) do
136
- if data_source_exists?(table_name)
365
+ if data_source_exists?(connection, table_name)
137
366
  @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
138
367
  else
139
368
  []
@@ -141,19 +370,16 @@ module ActiveRecord
141
370
  end
142
371
  end
143
372
 
144
- def database_version # :nodoc:
373
+ def database_version(connection) # :nodoc:
145
374
  @database_version ||= connection.get_database_version
146
375
  end
147
376
 
148
- # Clears out internal caches
149
- def clear!
150
- @columns.clear
151
- @columns_hash.clear
152
- @primary_keys.clear
153
- @data_sources.clear
154
- @indexes.clear
155
- @version = nil
156
- @database_version = nil
377
+ def version(connection)
378
+ @version ||= connection.schema_version
379
+ end
380
+
381
+ def schema_version
382
+ @version
157
383
  end
158
384
 
159
385
  def size
@@ -161,7 +387,7 @@ module ActiveRecord
161
387
  end
162
388
 
163
389
  # Clear out internal caches for the data source +name+.
164
- def clear_data_source_cache!(name)
390
+ def clear_data_source_cache!(_connection, name)
165
391
  @columns.delete name
166
392
  @columns_hash.delete name
167
393
  @primary_keys.delete name
@@ -169,9 +395,16 @@ module ActiveRecord
169
395
  @indexes.delete name
170
396
  end
171
397
 
398
+ def add_all(connection) # :nodoc:
399
+ tables_to_cache(connection).each do |table|
400
+ add(connection, table)
401
+ end
402
+
403
+ version(connection)
404
+ database_version(connection)
405
+ end
406
+
172
407
  def dump_to(filename)
173
- clear!
174
- tables_to_cache.each { |table| add(table) }
175
408
  open(filename) { |f|
176
409
  if filename.include?(".dump")
177
410
  f.write(Marshal.dump(self))
@@ -181,13 +414,11 @@ module ActiveRecord
181
414
  }
182
415
  end
183
416
 
184
- def marshal_dump
185
- reset_version!
186
-
187
- [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
417
+ def marshal_dump # :nodoc:
418
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, @database_version]
188
419
  end
189
420
 
190
- def marshal_load(array)
421
+ def marshal_load(array) # :nodoc:
191
422
  @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
192
423
  @indexes ||= {}
193
424
 
@@ -195,7 +426,7 @@ module ActiveRecord
195
426
  end
196
427
 
197
428
  private
198
- def tables_to_cache
429
+ def tables_to_cache(connection)
199
430
  connection.data_sources.reject do |table|
200
431
  ignored_table?(table)
201
432
  end
@@ -207,10 +438,6 @@ module ActiveRecord
207
438
  end
208
439
  end
209
440
 
210
- def reset_version!
211
- @version = connection.schema_version
212
- end
213
-
214
441
  def derive_columns_hash_and_deduplicate_values
215
442
  @columns = deep_deduplicate(@columns)
216
443
  @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
@@ -232,8 +459,8 @@ module ActiveRecord
232
459
  end
233
460
  end
234
461
 
235
- def prepare_data_sources
236
- tables_to_cache.each do |source|
462
+ def prepare_data_sources(connection)
463
+ tables_to_cache(connection).each do |source|
237
464
  @data_sources[source] = true
238
465
  end
239
466
  end
@@ -244,6 +471,7 @@ module ActiveRecord
244
471
  File.atomic_write(filename) do |file|
245
472
  if File.extname(filename) == ".gz"
246
473
  zipper = Zlib::GzipWriter.new file
474
+ zipper.mtime = 0
247
475
  yield zipper
248
476
  zipper.flush
249
477
  zipper.close
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ attr_reader :rowid
8
+
9
+ def initialize(*, auto_increment: nil, rowid: false, **)
10
+ super
11
+ @auto_increment = auto_increment
12
+ @rowid = rowid
13
+ end
14
+
15
+ def auto_increment?
16
+ @auto_increment
17
+ end
18
+
19
+ def auto_incremented_by_db?
20
+ auto_increment? || rowid
21
+ end
22
+
23
+ def init_with(coder)
24
+ @auto_increment = coder["auto_increment"]
25
+ super
26
+ end
27
+
28
+ def encode_with(coder)
29
+ coder["auto_increment"] = @auto_increment
30
+ super
31
+ end
32
+
33
+ def ==(other)
34
+ other.is_a?(Column) &&
35
+ super &&
36
+ auto_increment? == other.auto_increment?
37
+ end
38
+ alias :eql? :==
39
+
40
+ def hash
41
+ Column.hash ^
42
+ super.hash ^
43
+ auto_increment?.hash ^
44
+ rowid.hash
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -15,39 +15,25 @@ module ActiveRecord
15
15
  !READ_QUERY.match?(sql.b)
16
16
  end
17
17
 
18
- def explain(arel, binds = [])
19
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
20
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
18
+ def explain(arel, binds = [], _options = [])
19
+ sql = "EXPLAIN QUERY PLAN " + to_sql(arel, binds)
20
+ result = internal_exec_query(sql, "EXPLAIN", [])
21
+ SQLite3::ExplainPrettyPrinter.new.pp(result)
21
22
  end
22
23
 
23
- def execute(sql, name = nil) # :nodoc:
24
+ def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
24
25
  sql = transform_query(sql)
25
26
  check_if_write_query(sql)
26
27
 
27
- materialize_transactions
28
- mark_transaction_written_if_write(sql)
29
-
30
- log(sql, name) do
31
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
32
- @connection.execute(sql)
33
- end
34
- end
35
- end
36
-
37
- def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
38
- sql = transform_query(sql)
39
- check_if_write_query(sql)
40
-
41
- materialize_transactions
42
28
  mark_transaction_written_if_write(sql)
43
29
 
44
30
  type_casted_binds = type_casted_binds(binds)
45
31
 
46
32
  log(sql, name, binds, type_casted_binds, async: async) do
47
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
33
+ with_raw_connection do |conn|
48
34
  # Don't cache statements if they are not prepared
49
35
  unless prepare
50
- stmt = @connection.prepare(sql)
36
+ stmt = conn.prepare(sql)
51
37
  begin
52
38
  cols = stmt.columns
53
39
  unless without_prepared_statement?(binds)
@@ -58,7 +44,7 @@ module ActiveRecord
58
44
  stmt.close
59
45
  end
60
46
  else
61
- stmt = @statements[sql] ||= @connection.prepare(sql)
47
+ stmt = @statements[sql] ||= conn.prepare(sql)
62
48
  cols = stmt.columns
63
49
  stmt.reset!
64
50
  stmt.bind_params(type_casted_binds)
@@ -71,8 +57,8 @@ module ActiveRecord
71
57
  end
72
58
 
73
59
  def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
74
- exec_query(sql, name, binds)
75
- @connection.changes
60
+ internal_exec_query(sql, name, binds)
61
+ @raw_connection.changes
76
62
  end
77
63
  alias :exec_update :exec_delete
78
64
 
@@ -80,22 +66,36 @@ module ActiveRecord
80
66
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
67
  raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
82
68
 
83
- ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = @connection.get_first_value("PRAGMA read_uncommitted")
84
- @connection.read_uncommitted = true
85
- begin_db_transaction
69
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
70
+ ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
71
+ conn.read_uncommitted = true
72
+ begin_db_transaction
73
+ end
86
74
  end
87
75
 
88
76
  def begin_db_transaction # :nodoc:
89
- log("begin transaction", "TRANSACTION") { @connection.transaction }
77
+ log("begin transaction", "TRANSACTION") do
78
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
79
+ conn.transaction
80
+ end
81
+ end
90
82
  end
91
83
 
92
84
  def commit_db_transaction # :nodoc:
93
- log("commit transaction", "TRANSACTION") { @connection.commit }
85
+ log("commit transaction", "TRANSACTION") do
86
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
87
+ conn.commit
88
+ end
89
+ end
94
90
  reset_read_uncommitted
95
91
  end
96
92
 
97
93
  def exec_rollback_db_transaction # :nodoc:
98
- log("rollback transaction", "TRANSACTION") { @connection.rollback }
94
+ log("rollback transaction", "TRANSACTION") do
95
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
96
+ conn.rollback
97
+ end
98
+ end
99
99
  reset_read_uncommitted
100
100
  end
101
101
 
@@ -109,11 +109,19 @@ module ActiveRecord
109
109
  end
110
110
 
111
111
  private
112
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
113
+ log(sql, name, async: async) do
114
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
115
+ conn.execute(sql)
116
+ end
117
+ end
118
+ end
119
+
112
120
  def reset_read_uncommitted
113
121
  read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
114
122
  return unless read_uncommitted
115
123
 
116
- @connection.read_uncommitted = read_uncommitted
124
+ @raw_connection&.read_uncommitted = read_uncommitted
117
125
  end
118
126
 
119
127
  def execute_batch(statements, name = nil)
@@ -121,19 +129,17 @@ module ActiveRecord
121
129
  sql = combine_multi_statements(statements)
122
130
 
123
131
  check_if_write_query(sql)
124
-
125
- materialize_transactions
126
132
  mark_transaction_written_if_write(sql)
127
133
 
128
134
  log(sql, name) do
129
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
130
- @connection.execute_batch2(sql)
135
+ with_raw_connection do |conn|
136
+ conn.execute_batch2(sql)
131
137
  end
132
138
  end
133
139
  end
134
140
 
135
141
  def last_inserted_id(result)
136
- @connection.last_insert_row_id
142
+ @raw_connection.last_insert_row_id
137
143
  end
138
144
 
139
145
  def build_fixture_statements(fixture_set)