activerecord 6.0.6 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +783 -910
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +43 -26
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +32 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +223 -66
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +27 -10
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +2 -2
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +34 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +16 -7
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -13
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +266 -95
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +318 -195
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +39 -51
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +28 -30
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,8 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/file/atomic"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  class SchemaCache
8
+ def self.load_from(filename)
9
+ return unless File.file?(filename)
10
+
11
+ read(filename) do |file|
12
+ filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
13
+ end
14
+ end
15
+
16
+ def self.read(filename, &block)
17
+ if File.extname(filename) == ".gz"
18
+ Zlib::GzipReader.open(filename) { |gz|
19
+ yield gz.read
20
+ }
21
+ else
22
+ yield File.read(filename)
23
+ end
24
+ end
25
+ private_class_method :read
26
+
6
27
  attr_reader :version
7
28
  attr_accessor :connection
8
29
 
@@ -26,27 +47,33 @@ module ActiveRecord
26
47
  end
27
48
 
28
49
  def encode_with(coder)
50
+ reset_version!
51
+
29
52
  coder["columns"] = @columns
30
- coder["columns_hash"] = @columns_hash
31
53
  coder["primary_keys"] = @primary_keys
32
54
  coder["data_sources"] = @data_sources
33
55
  coder["indexes"] = @indexes
34
- coder["version"] = connection.migration_context.current_version
56
+ coder["version"] = @version
35
57
  coder["database_version"] = database_version
36
58
  end
37
59
 
38
60
  def init_with(coder)
39
61
  @columns = coder["columns"]
40
- @columns_hash = coder["columns_hash"]
41
62
  @primary_keys = coder["primary_keys"]
42
63
  @data_sources = coder["data_sources"]
43
64
  @indexes = coder["indexes"] || {}
44
65
  @version = coder["version"]
45
66
  @database_version = coder["database_version"]
67
+
68
+ derive_columns_hash_and_deduplicate_values
46
69
  end
47
70
 
48
71
  def primary_keys(table_name)
49
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
72
+ @primary_keys.fetch(table_name) do
73
+ if data_source_exists?(table_name)
74
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
75
+ end
76
+ end
50
77
  end
51
78
 
52
79
  # A cached lookup for table existence.
@@ -54,7 +81,7 @@ module ActiveRecord
54
81
  prepare_data_sources if @data_sources.empty?
55
82
  return @data_sources[name] if @data_sources.key? name
56
83
 
57
- @data_sources[name] = connection.data_source_exists?(name)
84
+ @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
58
85
  end
59
86
 
60
87
  # Add internal cache for table with +table_name+.
@@ -73,15 +100,17 @@ module ActiveRecord
73
100
 
74
101
  # Get the columns for a table
75
102
  def columns(table_name)
76
- @columns[table_name] ||= connection.columns(table_name)
103
+ @columns.fetch(table_name) do
104
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
105
+ end
77
106
  end
78
107
 
79
108
  # Get the columns for a table as a hash, key is the column name
80
109
  # value is the column object.
81
110
  def columns_hash(table_name)
82
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
83
- [col.name, col]
84
- }]
111
+ @columns_hash.fetch(table_name) do
112
+ @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
113
+ end
85
114
  end
86
115
 
87
116
  # Checks whether the columns hash is already cached for a table.
@@ -90,7 +119,9 @@ module ActiveRecord
90
119
  end
91
120
 
92
121
  def indexes(table_name)
93
- @indexes[table_name] ||= connection.indexes(table_name)
122
+ @indexes.fetch(table_name) do
123
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
124
+ end
94
125
  end
95
126
 
96
127
  def database_version # :nodoc:
@@ -121,21 +152,73 @@ module ActiveRecord
121
152
  @indexes.delete name
122
153
  end
123
154
 
155
+ def dump_to(filename)
156
+ clear!
157
+ connection.data_sources.each { |table| add(table) }
158
+ open(filename) { |f|
159
+ if filename.include?(".dump")
160
+ f.write(Marshal.dump(self))
161
+ else
162
+ f.write(YAML.dump(self))
163
+ end
164
+ }
165
+ end
166
+
124
167
  def marshal_dump
125
- # if we get current version during initialization, it happens stack over flow.
126
- @version = connection.migration_context.current_version
127
- [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
168
+ reset_version!
169
+
170
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
128
171
  end
129
172
 
130
173
  def marshal_load(array)
131
- @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
- @indexes = @indexes || {}
174
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
175
+ @indexes ||= {}
176
+
177
+ derive_columns_hash_and_deduplicate_values
133
178
  end
134
179
 
135
180
  private
181
+ def reset_version!
182
+ @version = connection.migration_context.current_version
183
+ end
184
+
185
+ def derive_columns_hash_and_deduplicate_values
186
+ @columns = deep_deduplicate(@columns)
187
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
188
+ @primary_keys = deep_deduplicate(@primary_keys)
189
+ @data_sources = deep_deduplicate(@data_sources)
190
+ @indexes = deep_deduplicate(@indexes)
191
+ end
192
+
193
+ def deep_deduplicate(value)
194
+ case value
195
+ when Hash
196
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
197
+ when Array
198
+ value.map { |i| deep_deduplicate(i) }
199
+ when String, Deduplicable
200
+ -value
201
+ else
202
+ value
203
+ end
204
+ end
205
+
136
206
  def prepare_data_sources
137
207
  connection.data_sources.each { |source| @data_sources[source] = true }
138
208
  end
209
+
210
+ def open(filename)
211
+ File.atomic_write(filename) do |file|
212
+ if File.extname(filename) == ".gz"
213
+ zipper = Zlib::GzipWriter.new file
214
+ yield zipper
215
+ zipper.flush
216
+ zipper.close
217
+ else
218
+ yield file
219
+ end
220
+ end
221
+ end
139
222
  end
140
223
  end
141
224
  end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/connection_adapters/deduplicable"
4
+
3
5
  module ActiveRecord
4
6
  # :stopdoc:
5
7
  module ConnectionAdapters
6
8
  class SqlTypeMetadata
9
+ include Deduplicable
10
+
7
11
  attr_reader :sql_type, :type, :limit, :precision, :scale
8
12
 
9
13
  def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -32,6 +36,12 @@ module ActiveRecord
32
36
  precision.hash >> 1 ^
33
37
  scale.hash >> 2
34
38
  end
39
+
40
+ private
41
+ def deduplicated
42
+ @sql_type = -sql_type
43
+ super
44
+ end
35
45
  end
36
46
  end
37
47
  end
@@ -13,12 +13,18 @@ module ActiveRecord
13
13
  !READ_QUERY.match?(sql)
14
14
  end
15
15
 
16
+ def explain(arel, binds = [])
17
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
18
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
19
+ end
20
+
16
21
  def execute(sql, name = nil) #:nodoc:
17
22
  if preventing_writes? && write_query?(sql)
18
23
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
19
24
  end
20
25
 
21
26
  materialize_transactions
27
+ mark_transaction_written_if_write(sql)
22
28
 
23
29
  log(sql, name) do
24
30
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -33,6 +39,7 @@ module ActiveRecord
33
39
  end
34
40
 
35
41
  materialize_transactions
42
+ mark_transaction_written_if_write(sql)
36
43
 
37
44
  type_casted_binds = type_casted_binds(binds)
38
45
 
@@ -58,7 +65,7 @@ module ActiveRecord
58
65
  records = stmt.to_a
59
66
  end
60
67
 
61
- ActiveRecord::Result.new(cols, records)
68
+ build_result(columns: cols, rows: records)
62
69
  end
63
70
  end
64
71
  end
@@ -69,20 +76,37 @@ module ActiveRecord
69
76
  end
70
77
  alias :exec_update :exec_delete
71
78
 
79
+ def begin_isolated_db_transaction(isolation) #:nodoc
80
+ raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
+ 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
+
83
+ Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
84
+ @connection.read_uncommitted = true
85
+ begin_db_transaction
86
+ end
87
+
72
88
  def begin_db_transaction #:nodoc:
73
- log("begin transaction", nil) { @connection.transaction }
89
+ log("begin transaction", "TRANSACTION") { @connection.transaction }
74
90
  end
75
91
 
76
92
  def commit_db_transaction #:nodoc:
77
- log("commit transaction", nil) { @connection.commit }
93
+ log("commit transaction", "TRANSACTION") { @connection.commit }
94
+ reset_read_uncommitted
78
95
  end
79
96
 
80
97
  def exec_rollback_db_transaction #:nodoc:
81
- log("rollback transaction", nil) { @connection.rollback }
98
+ log("rollback transaction", "TRANSACTION") { @connection.rollback }
99
+ reset_read_uncommitted
82
100
  end
83
101
 
84
-
85
102
  private
103
+ def reset_read_uncommitted
104
+ read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
105
+ return unless read_uncommitted
106
+
107
+ @connection.read_uncommitted = read_uncommitted
108
+ end
109
+
86
110
  def execute_batch(statements, name = nil)
87
111
  sql = combine_multi_statements(statements)
88
112
 
@@ -91,6 +115,7 @@ module ActiveRecord
91
115
  end
92
116
 
93
117
  materialize_transactions
118
+ mark_transaction_written_if_write(sql)
94
119
 
95
120
  log(sql, name) do
96
121
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
  # "table_name"."column_name" | function(one or no argument)
61
61
  ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
62
62
  )
63
- (?:\s+AS\s+(?:\w+|"\w+"))?
63
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
64
64
  )
65
65
  (?:\s*,\s*\g<1>)*
66
66
  \z
@@ -3,8 +3,12 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
- class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
7
  private
8
+ def supports_index_using?
9
+ false
10
+ end
11
+
8
12
  def add_column_options!(sql, options)
9
13
  if options[:collation]
10
14
  sql << " COLLATE \"#{options[:collation]}\""
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
10
10
  # Indexes SQLite creates implicitly for internal use start with "sqlite_".
11
11
  # See https://www.sqlite.org/fileformat2.html#intschema
12
- next if row["name"].starts_with?("sqlite_")
12
+ next if row["name"].start_with?("sqlite_")
13
13
 
14
14
  index_sql = query_value(<<~SQL, "SCHEMA")
15
15
  SELECT sql
@@ -78,6 +78,35 @@ module ActiveRecord
78
78
  alter_table(from_table, foreign_keys)
79
79
  end
80
80
 
81
+ def check_constraints(table_name)
82
+ table_sql = query_value(<<-SQL, "SCHEMA")
83
+ SELECT sql
84
+ FROM sqlite_master
85
+ WHERE name = #{quote_table_name(table_name)} AND type = 'table'
86
+ UNION ALL
87
+ SELECT sql
88
+ FROM sqlite_temp_master
89
+ WHERE name = #{quote_table_name(table_name)} AND type = 'table'
90
+ SQL
91
+
92
+ table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
93
+ CheckConstraintDefinition.new(table_name, expression, name: name)
94
+ end
95
+ end
96
+
97
+ def add_check_constraint(table_name, expression, **options)
98
+ alter_table(table_name) do |definition|
99
+ definition.check_constraint(expression, **options)
100
+ end
101
+ end
102
+
103
+ def remove_check_constraint(table_name, expression = nil, **options)
104
+ check_constraints = check_constraints(table_name)
105
+ chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
106
+ check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
107
+ alter_table(table_name, foreign_keys(table_name), check_constraints)
108
+ end
109
+
81
110
  def create_schema_dumper(options)
82
111
  SQLite3::SchemaDumper.create(self, options)
83
112
  end
@@ -87,8 +116,12 @@ module ActiveRecord
87
116
  SQLite3::SchemaCreation.new(self)
88
117
  end
89
118
 
90
- def create_table_definition(*args, **options)
91
- SQLite3::TableDefinition.new(self, *args, **options)
119
+ def create_table_definition(name, **options)
120
+ SQLite3::TableDefinition.new(self, name, **options)
121
+ end
122
+
123
+ def validate_index_length!(table_name, new_name, internal = false)
124
+ super unless internal
92
125
  end
93
126
 
94
127
  def new_column_from_field(table_name, field)
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  # Allow database path relative to Rails.root, but only if the database
27
27
  # path is not the special path that tells sqlite to build a database only
28
28
  # in memory.
29
- if ":memory:" != config[:database]
29
+ if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:")
30
30
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
31
31
  dirname = File.dirname(config[:database])
32
32
  Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -48,8 +48,8 @@ module ActiveRecord
48
48
  end
49
49
 
50
50
  module ConnectionAdapters #:nodoc:
51
- # The SQLite3 adapter works SQLite 3.6.16 or newer
52
- # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
51
+ # The SQLite3 adapter works with the sqlite3-ruby drivers
52
+ # (available as gem from https://rubygems.org/gems/sqlite3).
53
53
  #
54
54
  # Options:
55
55
  #
@@ -76,16 +76,6 @@ module ActiveRecord
76
76
  json: { name: "json" },
77
77
  }
78
78
 
79
- def self.represent_boolean_as_integer=(value) # :nodoc:
80
- if value == false
81
- raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
82
- end
83
-
84
- ActiveSupport::Deprecation.warn(
85
- "`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
86
- )
87
- end
88
-
89
79
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
90
80
  private
91
81
  def dealloc(stmt)
@@ -116,6 +106,10 @@ module ActiveRecord
116
106
  true
117
107
  end
118
108
 
109
+ def supports_transaction_isolation?
110
+ true
111
+ end
112
+
119
113
  def supports_partial_index?
120
114
  true
121
115
  end
@@ -132,6 +126,10 @@ module ActiveRecord
132
126
  true
133
127
  end
134
128
 
129
+ def supports_check_constraints?
130
+ true
131
+ end
132
+
135
133
  def supports_views?
136
134
  true
137
135
  end
@@ -175,13 +173,6 @@ module ActiveRecord
175
173
  true
176
174
  end
177
175
 
178
- # Returns 62. SQLite supports index names up to 64
179
- # characters. The rest is used by Rails internally to perform
180
- # temporary rename operations
181
- def allowed_index_name_length
182
- index_name_length - 2
183
- end
184
-
185
176
  def native_database_types #:nodoc:
186
177
  NATIVE_DATABASE_TYPES
187
178
  end
@@ -215,14 +206,6 @@ module ActiveRecord
215
206
  end
216
207
  end
217
208
 
218
- #--
219
- # DATABASE STATEMENTS ======================================
220
- #++
221
- def explain(arel, binds = [])
222
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
223
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
224
- end
225
-
226
209
  # SCHEMA STATEMENTS ========================================
227
210
 
228
211
  def primary_keys(table_name) # :nodoc:
@@ -230,8 +213,11 @@ module ActiveRecord
230
213
  pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
231
214
  end
232
215
 
233
- def remove_index(table_name, options = {}) #:nodoc:
234
- index_name = index_name_for_remove(table_name, options)
216
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
217
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
218
+
219
+ index_name = index_name_for_remove(table_name, column_name, options)
220
+
235
221
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
236
222
  end
237
223
 
@@ -240,6 +226,8 @@ module ActiveRecord
240
226
  # Example:
241
227
  # rename_table('octopuses', 'octopi')
242
228
  def rename_table(table_name, new_name)
229
+ schema_cache.clear_data_source_cache!(table_name.to_s)
230
+ schema_cache.clear_data_source_cache!(new_name.to_s)
243
231
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
244
232
  rename_table_indexes(table_name, new_name)
245
233
  end
@@ -280,16 +268,10 @@ module ActiveRecord
280
268
  end
281
269
  end
282
270
 
283
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
271
+ def change_column(table_name, column_name, type, **options) #:nodoc:
284
272
  alter_table(table_name) do |definition|
285
273
  definition[column_name].instance_eval do
286
- self.type = aliased_types(type.to_s, type)
287
- self.limit = options[:limit] if options.include?(:limit)
288
- self.default = options[:default] if options.include?(:default)
289
- self.null = options[:null] if options.include?(:null)
290
- self.precision = options[:precision] if options.include?(:precision)
291
- self.scale = options[:scale] if options.include?(:scale)
292
- self.collation = options[:collation] if options.include?(:collation)
274
+ self.type = type
293
275
  self.options.merge!(options)
294
276
  end
295
277
  end
@@ -326,12 +308,17 @@ module ActiveRecord
326
308
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
327
309
  elsif insert.update_duplicates?
328
310
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
311
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
329
312
  sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
330
313
  end
331
314
 
332
315
  sql
333
316
  end
334
317
 
318
+ def shared_cache? # :nodoc:
319
+ @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
320
+ end
321
+
335
322
  def get_database_version # :nodoc:
336
323
  SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
337
324
  end
@@ -368,7 +355,12 @@ module ActiveRecord
368
355
  options[:null] == false && options[:default].nil?
369
356
  end
370
357
 
371
- def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
358
+ def alter_table(
359
+ table_name,
360
+ foreign_keys = foreign_keys(table_name),
361
+ check_constraints = check_constraints(table_name),
362
+ **options
363
+ )
372
364
  altered_table_name = "a#{table_name}"
373
365
 
374
366
  caller = lambda do |definition|
@@ -381,6 +373,10 @@ module ActiveRecord
381
373
  definition.foreign_key(to_table, **fk.options)
382
374
  end
383
375
 
376
+ check_constraints.each do |chk|
377
+ definition.check_constraint(chk.expression, **chk.options)
378
+ end
379
+
384
380
  yield definition if block_given?
385
381
  end
386
382
 
@@ -405,6 +401,7 @@ module ActiveRecord
405
401
  if from_primary_key.is_a?(Array)
406
402
  @definition.primary_keys from_primary_key
407
403
  end
404
+
408
405
  columns(from).each do |column|
409
406
  column_name = options[:rename] ?
410
407
  (options[:rename][column.name] ||
@@ -446,10 +443,10 @@ module ActiveRecord
446
443
 
447
444
  unless columns.empty?
448
445
  # index name can't be the same
449
- opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
450
- opts[:unique] = true if index.unique
451
- opts[:where] = index.where if index.where
452
- add_index(to, columns, opts)
446
+ options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
447
+ options[:unique] = true if index.unique
448
+ options[:where] = index.where if index.where
449
+ add_index(to, columns, **options)
453
450
  end
454
451
  end
455
452
  end
@@ -468,17 +465,18 @@ module ActiveRecord
468
465
  end
469
466
 
470
467
  def translate_exception(exception, message:, sql:, binds:)
471
- case exception.message
472
468
  # SQLite 3.8.2 returns a newly formatted error message:
473
469
  # UNIQUE constraint failed: *table_name*.*column_name*
474
470
  # Older versions of SQLite return:
475
471
  # column *column_name* is not unique
476
- when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
472
+ if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
477
473
  RecordNotUnique.new(message, sql: sql, binds: binds)
478
- when /.* may not be NULL/, /NOT NULL constraint failed: .*/
474
+ elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
479
475
  NotNullViolation.new(message, sql: sql, binds: binds)
480
- when /FOREIGN KEY constraint failed/i
476
+ elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
481
477
  InvalidForeignKey.new(message, sql: sql, binds: binds)
478
+ elsif exception.message.match?(/called on a closed database/i)
479
+ ConnectionNotEstablished.new(exception)
482
480
  else
483
481
  super
484
482
  end
@@ -498,12 +496,12 @@ module ActiveRecord
498
496
  # Result will have following sample string
499
497
  # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
500
498
  # "password_digest" varchar COLLATE "NOCASE");
501
- result = exec_query(sql, "SCHEMA").first
499
+ result = query_value(sql, "SCHEMA")
502
500
 
503
501
  if result
504
502
  # Splitting with left parentheses and discarding the first part will return all
505
503
  # columns separated with comma(,).
506
- columns_string = result["sql"].split("(", 2).last
504
+ columns_string = result.split("(", 2).last
507
505
 
508
506
  columns_string.split(",").each do |column_string|
509
507
  # This regex will match the column name and collation type and will save
@@ -511,7 +509,7 @@ module ActiveRecord
511
509
  collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
512
510
  end
513
511
 
514
- basic_structure.map! do |column|
512
+ basic_structure.map do |column|
515
513
  column_name = column["name"]
516
514
 
517
515
  if collation_hash.has_key? column_name
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :AbstractAdapter
9
+ end
10
+
11
+ autoload :Column
12
+ autoload :PoolConfig
13
+ autoload :PoolManager
14
+ autoload :LegacyPoolManager
15
+
16
+ autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
17
+ autoload :IndexDefinition
18
+ autoload :ColumnDefinition
19
+ autoload :ChangeColumnDefinition
20
+ autoload :ForeignKeyDefinition
21
+ autoload :CheckConstraintDefinition
22
+ autoload :TableDefinition
23
+ autoload :Table
24
+ autoload :AlterTable
25
+ autoload :ReferenceDefinition
26
+ end
27
+
28
+ autoload_at "active_record/connection_adapters/abstract/connection_pool" do
29
+ autoload :ConnectionHandler
30
+ end
31
+
32
+ autoload_under "abstract" do
33
+ autoload :SchemaStatements
34
+ autoload :DatabaseStatements
35
+ autoload :DatabaseLimits
36
+ autoload :Quoting
37
+ autoload :ConnectionPool
38
+ autoload :QueryCache
39
+ autoload :Savepoints
40
+ end
41
+
42
+ autoload_at "active_record/connection_adapters/abstract/transaction" do
43
+ autoload :TransactionManager
44
+ autoload :NullTransaction
45
+ autoload :RealTransaction
46
+ autoload :SavepointTransaction
47
+ autoload :TransactionState
48
+ end
49
+ end
50
+ end