activerecord 7.0.8 → 7.1.3.4

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 (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1554 -1452
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -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 +20 -4
  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 +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  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 +31 -7
  22. data/lib/active_record/associations/preloader.rb +13 -10
  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 +313 -217
  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 +52 -34
  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 +74 -51
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  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 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  76. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  78. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
  80. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  81. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  82. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  83. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  84. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  85. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  86. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  87. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  88. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  89. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  90. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  91. data/lib/active_record/connection_adapters.rb +3 -1
  92. data/lib/active_record/connection_handling.rb +72 -95
  93. data/lib/active_record/core.rb +175 -153
  94. data/lib/active_record/counter_cache.rb +46 -25
  95. data/lib/active_record/database_configurations/database_config.rb +9 -3
  96. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  97. data/lib/active_record/database_configurations/url_config.rb +17 -11
  98. data/lib/active_record/database_configurations.rb +86 -33
  99. data/lib/active_record/delegated_type.rb +9 -4
  100. data/lib/active_record/deprecator.rb +7 -0
  101. data/lib/active_record/destroy_association_async_job.rb +2 -0
  102. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  103. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  104. data/lib/active_record/encryption/config.rb +25 -1
  105. data/lib/active_record/encryption/configurable.rb +12 -19
  106. data/lib/active_record/encryption/context.rb +10 -3
  107. data/lib/active_record/encryption/contexts.rb +5 -1
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  109. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  113. data/lib/active_record/encryption/key_generator.rb +12 -1
  114. data/lib/active_record/encryption/message_serializer.rb +2 -0
  115. data/lib/active_record/encryption/properties.rb +3 -3
  116. data/lib/active_record/encryption/scheme.rb +19 -22
  117. data/lib/active_record/encryption.rb +1 -0
  118. data/lib/active_record/enum.rb +112 -28
  119. data/lib/active_record/errors.rb +112 -18
  120. data/lib/active_record/explain.rb +23 -3
  121. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  122. data/lib/active_record/fixture_set/render_context.rb +2 -0
  123. data/lib/active_record/fixture_set/table_row.rb +29 -8
  124. data/lib/active_record/fixtures.rb +135 -71
  125. data/lib/active_record/future_result.rb +31 -5
  126. data/lib/active_record/gem_version.rb +4 -4
  127. data/lib/active_record/inheritance.rb +30 -16
  128. data/lib/active_record/insert_all.rb +57 -10
  129. data/lib/active_record/integration.rb +8 -8
  130. data/lib/active_record/internal_metadata.rb +120 -30
  131. data/lib/active_record/locking/pessimistic.rb +5 -2
  132. data/lib/active_record/log_subscriber.rb +29 -12
  133. data/lib/active_record/marshalling.rb +56 -0
  134. data/lib/active_record/message_pack.rb +124 -0
  135. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  136. data/lib/active_record/middleware/database_selector.rb +6 -8
  137. data/lib/active_record/middleware/shard_selector.rb +3 -1
  138. data/lib/active_record/migration/command_recorder.rb +104 -5
  139. data/lib/active_record/migration/compatibility.rb +139 -5
  140. data/lib/active_record/migration/default_strategy.rb +23 -0
  141. data/lib/active_record/migration/execution_strategy.rb +19 -0
  142. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  143. data/lib/active_record/migration.rb +219 -111
  144. data/lib/active_record/model_schema.rb +64 -44
  145. data/lib/active_record/nested_attributes.rb +24 -6
  146. data/lib/active_record/normalization.rb +167 -0
  147. data/lib/active_record/persistence.rb +188 -37
  148. data/lib/active_record/promise.rb +84 -0
  149. data/lib/active_record/query_cache.rb +3 -21
  150. data/lib/active_record/query_logs.rb +77 -52
  151. data/lib/active_record/query_logs_formatter.rb +41 -0
  152. data/lib/active_record/querying.rb +15 -2
  153. data/lib/active_record/railtie.rb +109 -47
  154. data/lib/active_record/railties/controller_runtime.rb +12 -6
  155. data/lib/active_record/railties/databases.rake +142 -148
  156. data/lib/active_record/railties/job_runtime.rb +23 -0
  157. data/lib/active_record/readonly_attributes.rb +32 -5
  158. data/lib/active_record/reflection.rb +174 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  160. data/lib/active_record/relation/batches.rb +190 -61
  161. data/lib/active_record/relation/calculations.rb +187 -63
  162. data/lib/active_record/relation/delegation.rb +23 -9
  163. data/lib/active_record/relation/finder_methods.rb +77 -16
  164. data/lib/active_record/relation/merger.rb +2 -0
  165. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  167. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  168. data/lib/active_record/relation/predicate_builder.rb +26 -14
  169. data/lib/active_record/relation/query_attribute.rb +2 -1
  170. data/lib/active_record/relation/query_methods.rb +352 -63
  171. data/lib/active_record/relation/spawn_methods.rb +18 -1
  172. data/lib/active_record/relation.rb +91 -35
  173. data/lib/active_record/result.rb +19 -5
  174. data/lib/active_record/runtime_registry.rb +24 -1
  175. data/lib/active_record/sanitization.rb +51 -11
  176. data/lib/active_record/schema.rb +2 -3
  177. data/lib/active_record/schema_dumper.rb +46 -7
  178. data/lib/active_record/schema_migration.rb +68 -33
  179. data/lib/active_record/scoping/default.rb +15 -5
  180. data/lib/active_record/scoping/named.rb +2 -2
  181. data/lib/active_record/scoping.rb +2 -1
  182. data/lib/active_record/secure_password.rb +60 -0
  183. data/lib/active_record/secure_token.rb +21 -3
  184. data/lib/active_record/signed_id.rb +7 -5
  185. data/lib/active_record/store.rb +8 -8
  186. data/lib/active_record/suppressor.rb +3 -1
  187. data/lib/active_record/table_metadata.rb +10 -1
  188. data/lib/active_record/tasks/database_tasks.rb +127 -105
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  192. data/lib/active_record/test_fixtures.rb +113 -96
  193. data/lib/active_record/timestamp.rb +27 -15
  194. data/lib/active_record/token_for.rb +113 -0
  195. data/lib/active_record/touch_later.rb +11 -6
  196. data/lib/active_record/transactions.rb +36 -10
  197. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  198. data/lib/active_record/type/internal/timezone.rb +7 -2
  199. data/lib/active_record/type/time.rb +4 -0
  200. data/lib/active_record/validations/absence.rb +1 -1
  201. data/lib/active_record/validations/numericality.rb +5 -4
  202. data/lib/active_record/validations/presence.rb +5 -28
  203. data/lib/active_record/validations/uniqueness.rb +47 -2
  204. data/lib/active_record/validations.rb +8 -4
  205. data/lib/active_record/version.rb +1 -1
  206. data/lib/active_record.rb +121 -16
  207. data/lib/arel/errors.rb +10 -0
  208. data/lib/arel/factory_methods.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +48 -12
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. 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) # :nodoc:
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 # :nodoc:
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,12 +44,13 @@ 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)
65
51
  records = stmt.to_a
66
52
  end
53
+ verified!
67
54
 
68
55
  build_result(columns: cols, rows: records)
69
56
  end
@@ -71,8 +58,8 @@ module ActiveRecord
71
58
  end
72
59
 
73
60
  def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
74
- exec_query(sql, name, binds)
75
- @connection.changes
61
+ internal_exec_query(sql, name, binds)
62
+ @raw_connection.changes
76
63
  end
77
64
  alias :exec_update :exec_delete
78
65
 
@@ -80,22 +67,38 @@ module ActiveRecord
80
67
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
68
  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
69
 
83
- ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = @connection.get_first_value("PRAGMA read_uncommitted")
84
- @connection.read_uncommitted = true
85
- begin_db_transaction
70
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
71
+ ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
72
+ conn.read_uncommitted = true
73
+ begin_db_transaction
74
+ end
86
75
  end
87
76
 
88
77
  def begin_db_transaction # :nodoc:
89
- log("begin transaction", "TRANSACTION") { @connection.transaction }
78
+ log("begin transaction", "TRANSACTION") do
79
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
80
+ result = conn.transaction
81
+ verified!
82
+ result
83
+ end
84
+ end
90
85
  end
91
86
 
92
87
  def commit_db_transaction # :nodoc:
93
- log("commit transaction", "TRANSACTION") { @connection.commit }
88
+ log("commit transaction", "TRANSACTION") do
89
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
90
+ conn.commit
91
+ end
92
+ end
94
93
  reset_read_uncommitted
95
94
  end
96
95
 
97
96
  def exec_rollback_db_transaction # :nodoc:
98
- log("rollback transaction", "TRANSACTION") { @connection.rollback }
97
+ log("rollback transaction", "TRANSACTION") do
98
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
99
+ conn.rollback
100
+ end
101
+ end
99
102
  reset_read_uncommitted
100
103
  end
101
104
 
@@ -109,11 +112,21 @@ module ActiveRecord
109
112
  end
110
113
 
111
114
  private
115
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
116
+ log(sql, name, async: async) do
117
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
118
+ result = conn.execute(sql)
119
+ verified!
120
+ result
121
+ end
122
+ end
123
+ end
124
+
112
125
  def reset_read_uncommitted
113
126
  read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
114
127
  return unless read_uncommitted
115
128
 
116
- @connection.read_uncommitted = read_uncommitted
129
+ @raw_connection&.read_uncommitted = read_uncommitted
117
130
  end
118
131
 
119
132
  def execute_batch(statements, name = nil)
@@ -121,21 +134,17 @@ module ActiveRecord
121
134
  sql = combine_multi_statements(statements)
122
135
 
123
136
  check_if_write_query(sql)
124
-
125
- materialize_transactions
126
137
  mark_transaction_written_if_write(sql)
127
138
 
128
139
  log(sql, name) do
129
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
130
- @connection.execute_batch2(sql)
140
+ with_raw_connection do |conn|
141
+ result = conn.execute_batch2(sql)
142
+ verified!
143
+ result
131
144
  end
132
145
  end
133
146
  end
134
147
 
135
- def last_inserted_id(result)
136
- @connection.last_insert_row_id
137
- end
138
-
139
148
  def build_fixture_statements(fixture_set)
140
149
  fixture_set.flat_map do |table_name, fixtures|
141
150
  next if fixtures.empty?
@@ -146,6 +155,10 @@ module ActiveRecord
146
155
  def build_truncate_statement(table_name)
147
156
  "DELETE FROM #{quote_table_name(table_name)}"
148
157
  end
158
+
159
+ def returning_column_values(result)
160
+ result.rows.first
161
+ end
149
162
  end
150
163
  end
151
164
  end