activerecord 6.0.1 → 6.1.7

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -633
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +26 -15
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -37
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  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 -3
  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 +38 -13
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +119 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +218 -87
  114. data/lib/active_record/core.rb +269 -68
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +2 -2
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +42 -9
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +117 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +45 -16
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +339 -188
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -83
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +25 -72
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +5 -9
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -29
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,8 +1,37 @@
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
+ if filename.include?(".dump")
13
+ Marshal.load(file)
14
+ else
15
+ if YAML.respond_to?(:unsafe_load)
16
+ YAML.unsafe_load(file)
17
+ else
18
+ YAML.load(file)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.read(filename, &block)
25
+ if File.extname(filename) == ".gz"
26
+ Zlib::GzipReader.open(filename) { |gz|
27
+ yield gz.read
28
+ }
29
+ else
30
+ yield File.read(filename)
31
+ end
32
+ end
33
+ private_class_method :read
34
+
6
35
  attr_reader :version
7
36
  attr_accessor :connection
8
37
 
@@ -26,27 +55,33 @@ module ActiveRecord
26
55
  end
27
56
 
28
57
  def encode_with(coder)
58
+ reset_version!
59
+
29
60
  coder["columns"] = @columns
30
- coder["columns_hash"] = @columns_hash
31
61
  coder["primary_keys"] = @primary_keys
32
62
  coder["data_sources"] = @data_sources
33
63
  coder["indexes"] = @indexes
34
- coder["version"] = connection.migration_context.current_version
64
+ coder["version"] = @version
35
65
  coder["database_version"] = database_version
36
66
  end
37
67
 
38
68
  def init_with(coder)
39
69
  @columns = coder["columns"]
40
- @columns_hash = coder["columns_hash"]
41
70
  @primary_keys = coder["primary_keys"]
42
71
  @data_sources = coder["data_sources"]
43
72
  @indexes = coder["indexes"] || {}
44
73
  @version = coder["version"]
45
74
  @database_version = coder["database_version"]
75
+
76
+ derive_columns_hash_and_deduplicate_values
46
77
  end
47
78
 
48
79
  def primary_keys(table_name)
49
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
80
+ @primary_keys.fetch(table_name) do
81
+ if data_source_exists?(table_name)
82
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
83
+ end
84
+ end
50
85
  end
51
86
 
52
87
  # A cached lookup for table existence.
@@ -54,7 +89,7 @@ module ActiveRecord
54
89
  prepare_data_sources if @data_sources.empty?
55
90
  return @data_sources[name] if @data_sources.key? name
56
91
 
57
- @data_sources[name] = connection.data_source_exists?(name)
92
+ @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
58
93
  end
59
94
 
60
95
  # Add internal cache for table with +table_name+.
@@ -73,15 +108,17 @@ module ActiveRecord
73
108
 
74
109
  # Get the columns for a table
75
110
  def columns(table_name)
76
- @columns[table_name] ||= connection.columns(table_name)
111
+ @columns.fetch(table_name) do
112
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
113
+ end
77
114
  end
78
115
 
79
116
  # Get the columns for a table as a hash, key is the column name
80
117
  # value is the column object.
81
118
  def columns_hash(table_name)
82
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
83
- [col.name, col]
84
- }]
119
+ @columns_hash.fetch(table_name) do
120
+ @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
121
+ end
85
122
  end
86
123
 
87
124
  # Checks whether the columns hash is already cached for a table.
@@ -90,7 +127,9 @@ module ActiveRecord
90
127
  end
91
128
 
92
129
  def indexes(table_name)
93
- @indexes[table_name] ||= connection.indexes(table_name)
130
+ @indexes.fetch(table_name) do
131
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
132
+ end
94
133
  end
95
134
 
96
135
  def database_version # :nodoc:
@@ -121,21 +160,97 @@ module ActiveRecord
121
160
  @indexes.delete name
122
161
  end
123
162
 
163
+ def dump_to(filename)
164
+ clear!
165
+ connection.data_sources.each { |table| add(table) }
166
+ open(filename) { |f|
167
+ if filename.include?(".dump")
168
+ f.write(Marshal.dump(self))
169
+ else
170
+ f.write(YAML.dump(self))
171
+ end
172
+ }
173
+ end
174
+
124
175
  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]
176
+ reset_version!
177
+
178
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
128
179
  end
129
180
 
130
181
  def marshal_load(array)
131
- @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
- @indexes = @indexes || {}
182
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
183
+ @indexes ||= {}
184
+
185
+ derive_columns_hash_and_deduplicate_values
133
186
  end
134
187
 
135
188
  private
189
+ def reset_version!
190
+ @version = connection.migration_context.current_version
191
+ end
192
+
193
+ def derive_columns_hash_and_deduplicate_values
194
+ @columns = deep_deduplicate(@columns)
195
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
196
+ @primary_keys = deep_deduplicate(@primary_keys)
197
+ @data_sources = deep_deduplicate(@data_sources)
198
+ @indexes = deep_deduplicate(@indexes)
199
+ end
200
+
201
+ if RUBY_VERSION < "2.7"
202
+ def deep_deduplicate(value)
203
+ case value
204
+ when Hash
205
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
206
+ when Array
207
+ value.map { |i| deep_deduplicate(i) }
208
+ when String
209
+ if value.tainted?
210
+ # Ruby 2.6 and 2.7 have slightly different implementations of the String#-@ method.
211
+ # In Ruby 2.6, the receiver of the String#-@ method is modified under certain
212
+ # circumstances, and this was later identified as a bug
213
+ # (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
214
+ value = value.dup
215
+ end
216
+ -value
217
+ when Deduplicable
218
+ -value
219
+ else
220
+ value
221
+ end
222
+ end
223
+ else
224
+ def deep_deduplicate(value)
225
+ case value
226
+ when Hash
227
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
228
+ when Array
229
+ value.map { |i| deep_deduplicate(i) }
230
+ when String, Deduplicable
231
+ -value
232
+ else
233
+ value
234
+ end
235
+ end
236
+ end
237
+
136
238
  def prepare_data_sources
137
239
  connection.data_sources.each { |source| @data_sources[source] = true }
138
240
  end
241
+
242
+ def open(filename)
243
+ File.atomic_write(filename) do |file|
244
+ if File.extname(filename) == ".gz"
245
+ zipper = Zlib::GzipWriter.new file
246
+ yield zipper
247
+ zipper.flush
248
+ zipper.close
249
+ else
250
+ yield file
251
+ end
252
+ end
253
+ end
139
254
  end
140
255
  end
141
256
  end
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  # :stopdoc:
5
5
  module ConnectionAdapters
6
6
  class SqlTypeMetadata
7
+ include Deduplicable
8
+
7
9
  attr_reader :sql_type, :type, :limit, :precision, :scale
8
10
 
9
11
  def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -32,6 +34,12 @@ module ActiveRecord
32
34
  precision.hash >> 1 ^
33
35
  scale.hash >> 2
34
36
  end
37
+
38
+ private
39
+ def deduplicated
40
+ @sql_type = -sql_type
41
+ super
42
+ end
35
43
  end
36
44
  end
37
45
  end
@@ -5,12 +5,19 @@ module ActiveRecord
5
5
  module SQLite3
6
6
  module DatabaseStatements
7
7
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
8
- :begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback, :with
8
+ :pragma
9
9
  ) # :nodoc:
10
10
  private_constant :READ_QUERY
11
11
 
12
12
  def write_query?(sql) # :nodoc:
13
13
  !READ_QUERY.match?(sql)
14
+ rescue ArgumentError # Invalid encoding
15
+ !READ_QUERY.match?(sql.b)
16
+ end
17
+
18
+ def explain(arel, binds = [])
19
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
20
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
14
21
  end
15
22
 
16
23
  def execute(sql, name = nil) #:nodoc:
@@ -19,6 +26,7 @@ module ActiveRecord
19
26
  end
20
27
 
21
28
  materialize_transactions
29
+ mark_transaction_written_if_write(sql)
22
30
 
23
31
  log(sql, name) do
24
32
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -33,6 +41,7 @@ module ActiveRecord
33
41
  end
34
42
 
35
43
  materialize_transactions
44
+ mark_transaction_written_if_write(sql)
36
45
 
37
46
  type_casted_binds = type_casted_binds(binds)
38
47
 
@@ -58,7 +67,7 @@ module ActiveRecord
58
67
  records = stmt.to_a
59
68
  end
60
69
 
61
- ActiveRecord::Result.new(cols, records)
70
+ build_result(columns: cols, rows: records)
62
71
  end
63
72
  end
64
73
  end
@@ -69,26 +78,46 @@ module ActiveRecord
69
78
  end
70
79
  alias :exec_update :exec_delete
71
80
 
81
+ def begin_isolated_db_transaction(isolation) #:nodoc
82
+ raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
83
+ raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
84
+
85
+ Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
86
+ @connection.read_uncommitted = true
87
+ begin_db_transaction
88
+ end
89
+
72
90
  def begin_db_transaction #:nodoc:
73
- log("begin transaction", nil) { @connection.transaction }
91
+ log("begin transaction", "TRANSACTION") { @connection.transaction }
74
92
  end
75
93
 
76
94
  def commit_db_transaction #:nodoc:
77
- log("commit transaction", nil) { @connection.commit }
95
+ log("commit transaction", "TRANSACTION") { @connection.commit }
96
+ reset_read_uncommitted
78
97
  end
79
98
 
80
99
  def exec_rollback_db_transaction #:nodoc:
81
- log("rollback transaction", nil) { @connection.rollback }
100
+ log("rollback transaction", "TRANSACTION") { @connection.rollback }
101
+ reset_read_uncommitted
82
102
  end
83
103
 
84
-
85
104
  private
86
- def execute_batch(sql, name = nil)
105
+ def reset_read_uncommitted
106
+ read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
107
+ return unless read_uncommitted
108
+
109
+ @connection.read_uncommitted = read_uncommitted
110
+ end
111
+
112
+ def execute_batch(statements, name = nil)
113
+ sql = combine_multi_statements(statements)
114
+
87
115
  if preventing_writes? && write_query?(sql)
88
116
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
89
117
  end
90
118
 
91
119
  materialize_transactions
120
+ mark_transaction_written_if_write(sql)
92
121
 
93
122
  log(sql, name) do
94
123
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -108,11 +137,8 @@ module ActiveRecord
108
137
  end.compact
109
138
  end
110
139
 
111
- def build_truncate_statements(*table_names)
112
- truncate_tables = table_names.map do |table_name|
113
- "DELETE FROM #{quote_table_name(table_name)}"
114
- end
115
- combine_multi_statements(truncate_tables)
140
+ def build_truncate_statement(table_name)
141
+ "DELETE FROM #{quote_table_name(table_name)}"
116
142
  end
117
143
  end
118
144
  end
@@ -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
@@ -82,7 +82,6 @@ module ActiveRecord
82
82
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
83
83
 
84
84
  private
85
-
86
85
  def _type_cast(value)
87
86
  case value
88
87
  when BigDecimal
@@ -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
@@ -55,13 +55,13 @@ module ActiveRecord
55
55
  def add_foreign_key(from_table, to_table, **options)
56
56
  alter_table(from_table) do |definition|
57
57
  to_table = strip_table_name_prefix_and_suffix(to_table)
58
- definition.foreign_key(to_table, options)
58
+ definition.foreign_key(to_table, **options)
59
59
  end
60
60
  end
61
61
 
62
62
  def remove_foreign_key(from_table, to_table = nil, **options)
63
63
  to_table ||= options[:to_table]
64
- options = options.except(:name, :to_table)
64
+ options = options.except(:name, :to_table, :validate)
65
65
  foreign_keys = foreign_keys(from_table)
66
66
 
67
67
  fkey = foreign_keys.detect do |fk|
@@ -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)
91
- SQLite3::TableDefinition.new(self, *args)
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)