activerecord 6.1.4.6 → 7.0.2.3

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1188 -932
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +187 -55
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +119 -90
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +8 -23
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +38 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +35 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +47 -53
  94. data/lib/active_record/core.rb +122 -132
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +16 -32
  100. data/lib/active_record/delegated_type.rb +52 -11
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +61 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +49 -42
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +17 -20
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +3 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +9 -3
  147. data/lib/active_record/log_subscriber.rb +14 -3
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +8 -3
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +4 -4
  152. data/lib/active_record/migration/compatibility.rb +107 -3
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +109 -79
  155. data/lib/active_record/model_schema.rb +45 -58
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +219 -52
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +15 -5
  163. data/lib/active_record/railtie.rb +127 -17
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +66 -129
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +67 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +3 -3
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +249 -61
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +184 -84
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +11 -7
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +61 -12
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +1 -1
  191. data/lib/active_record/signed_id.rb +1 -1
  192. data/lib/active_record/suppressor.rb +11 -15
  193. data/lib/active_record/tasks/database_tasks.rb +120 -58
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  196. data/lib/active_record/test_databases.rb +1 -1
  197. data/lib/active_record/test_fixtures.rb +4 -4
  198. data/lib/active_record/timestamp.rb +3 -4
  199. data/lib/active_record/transactions.rb +9 -14
  200. data/lib/active_record/translation.rb +2 -2
  201. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  202. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  203. data/lib/active_record/type/internal/timezone.rb +2 -2
  204. data/lib/active_record/type/serialized.rb +1 -1
  205. data/lib/active_record/type/type_map.rb +17 -20
  206. data/lib/active_record/type.rb +1 -2
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/uniqueness.rb +1 -1
  209. data/lib/active_record.rb +204 -28
  210. data/lib/arel/attributes/attribute.rb +0 -8
  211. data/lib/arel/crud.rb +28 -22
  212. data/lib/arel/delete_manager.rb +18 -4
  213. data/lib/arel/filter_predications.rb +9 -0
  214. data/lib/arel/insert_manager.rb +2 -3
  215. data/lib/arel/nodes/casted.rb +1 -1
  216. data/lib/arel/nodes/delete_statement.rb +12 -13
  217. data/lib/arel/nodes/filter.rb +10 -0
  218. data/lib/arel/nodes/function.rb +1 -0
  219. data/lib/arel/nodes/insert_statement.rb +2 -2
  220. data/lib/arel/nodes/select_core.rb +2 -2
  221. data/lib/arel/nodes/select_statement.rb +2 -2
  222. data/lib/arel/nodes/update_statement.rb +8 -3
  223. data/lib/arel/nodes.rb +1 -0
  224. data/lib/arel/predications.rb +11 -3
  225. data/lib/arel/select_manager.rb +10 -4
  226. data/lib/arel/table.rb +0 -1
  227. data/lib/arel/tree_manager.rb +0 -12
  228. data/lib/arel/update_manager.rb +18 -4
  229. data/lib/arel/visitors/dot.rb +80 -90
  230. data/lib/arel/visitors/mysql.rb +8 -2
  231. data/lib/arel/visitors/postgresql.rb +0 -10
  232. data/lib/arel/visitors/to_sql.rb +58 -2
  233. data/lib/arel.rb +2 -1
  234. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  236. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  237. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  238. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  239. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  240. metadata +56 -11
@@ -11,6 +11,8 @@ module ActiveRecord
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)
14
16
  end
15
17
 
16
18
  def explain(arel, binds = [])
@@ -18,10 +20,9 @@ module ActiveRecord
18
20
  SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
19
21
  end
20
22
 
21
- def execute(sql, name = nil) #:nodoc:
22
- if preventing_writes? && write_query?(sql)
23
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
24
- end
23
+ def execute(sql, name = nil) # :nodoc:
24
+ sql = transform_query(sql)
25
+ check_if_write_query(sql)
25
26
 
26
27
  materialize_transactions
27
28
  mark_transaction_written_if_write(sql)
@@ -33,17 +34,16 @@ module ActiveRecord
33
34
  end
34
35
  end
35
36
 
36
- def exec_query(sql, name = nil, binds = [], prepare: false)
37
- if preventing_writes? && write_query?(sql)
38
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
39
- end
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
40
 
41
41
  materialize_transactions
42
42
  mark_transaction_written_if_write(sql)
43
43
 
44
44
  type_casted_binds = type_casted_binds(binds)
45
45
 
46
- log(sql, name, binds, type_casted_binds) do
46
+ log(sql, name, binds, type_casted_binds, async: async) do
47
47
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
48
48
  # Don't cache statements if they are not prepared
49
49
  unless prepare
@@ -70,49 +70,57 @@ module ActiveRecord
70
70
  end
71
71
  end
72
72
 
73
- def exec_delete(sql, name = "SQL", binds = [])
73
+ def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
74
74
  exec_query(sql, name, binds)
75
75
  @connection.changes
76
76
  end
77
77
  alias :exec_update :exec_delete
78
78
 
79
- def begin_isolated_db_transaction(isolation) #:nodoc
79
+ def begin_isolated_db_transaction(isolation) # :nodoc:
80
80
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
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
82
 
83
- Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
83
+ ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = @connection.get_first_value("PRAGMA read_uncommitted")
84
84
  @connection.read_uncommitted = true
85
85
  begin_db_transaction
86
86
  end
87
87
 
88
- def begin_db_transaction #:nodoc:
88
+ def begin_db_transaction # :nodoc:
89
89
  log("begin transaction", "TRANSACTION") { @connection.transaction }
90
90
  end
91
91
 
92
- def commit_db_transaction #:nodoc:
92
+ def commit_db_transaction # :nodoc:
93
93
  log("commit transaction", "TRANSACTION") { @connection.commit }
94
94
  reset_read_uncommitted
95
95
  end
96
96
 
97
- def exec_rollback_db_transaction #:nodoc:
97
+ def exec_rollback_db_transaction # :nodoc:
98
98
  log("rollback transaction", "TRANSACTION") { @connection.rollback }
99
99
  reset_read_uncommitted
100
100
  end
101
101
 
102
+ # https://stackoverflow.com/questions/17574784
103
+ # https://www.sqlite.org/lang_datefunc.html
104
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
105
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
106
+
107
+ def high_precision_current_timestamp
108
+ HIGH_PRECISION_CURRENT_TIMESTAMP
109
+ end
110
+
102
111
  private
103
112
  def reset_read_uncommitted
104
- read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
113
+ read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
105
114
  return unless read_uncommitted
106
115
 
107
116
  @connection.read_uncommitted = read_uncommitted
108
117
  end
109
118
 
110
119
  def execute_batch(statements, name = nil)
120
+ statements = statements.map { |sql| transform_query(sql) }
111
121
  sql = combine_multi_statements(statements)
112
122
 
113
- if preventing_writes? && write_query?(sql)
114
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
115
- end
123
+ check_if_write_query(sql)
116
124
 
117
125
  materialize_transactions
118
126
  mark_transaction_written_if_write(sql)
@@ -45,6 +45,34 @@ module ActiveRecord
45
45
  0
46
46
  end
47
47
 
48
+ def quote_default_expression(value, column) # :nodoc:
49
+ if value.is_a?(Proc)
50
+ value = value.call
51
+ if value.match?(/\A\w+\(.*\)\z/)
52
+ "(#{value})"
53
+ else
54
+ value
55
+ end
56
+ else
57
+ super
58
+ end
59
+ end
60
+
61
+ def type_cast(value) # :nodoc:
62
+ case value
63
+ when BigDecimal
64
+ value.to_f
65
+ when String
66
+ if value.encoding == Encoding::ASCII_8BIT
67
+ super(value.encode(Encoding::UTF_8))
68
+ else
69
+ super
70
+ end
71
+ else
72
+ super
73
+ end
74
+ end
75
+
48
76
  def column_name_matcher
49
77
  COLUMN_NAME
50
78
  end
@@ -80,22 +108,6 @@ module ActiveRecord
80
108
  /ix
81
109
 
82
110
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
83
-
84
- private
85
- def _type_cast(value)
86
- case value
87
- when BigDecimal
88
- value.to_f
89
- when String
90
- if value.encoding == Encoding::ASCII_8BIT
91
- super(value.encode(Encoding::UTF_8))
92
- else
93
- super
94
- end
95
- else
96
- super
97
- end
98
- end
99
111
  end
100
112
  end
101
113
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module SchemaStatements # :nodoc:
7
7
  # Returns an array of indexes for the given table.
8
8
  def indexes(table_name)
9
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
9
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").filter_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
12
  next if row["name"].start_with?("sqlite_")
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  where: where,
50
50
  orders: orders
51
51
  )
52
- end.compact
52
+ end
53
53
  end
54
54
 
55
55
  def add_foreign_key(from_table, to_table, **options)
@@ -60,6 +60,8 @@ module ActiveRecord
60
60
  end
61
61
 
62
62
  def remove_foreign_key(from_table, to_table = nil, **options)
63
+ return if options[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
64
+
63
65
  to_table ||= options[:to_table]
64
66
  options = options.except(:name, :to_table, :validate)
65
67
  foreign_keys = foreign_keys(from_table)
@@ -125,20 +127,20 @@ module ActiveRecord
125
127
  end
126
128
 
127
129
  def new_column_from_field(table_name, field)
128
- default = \
129
- case field["dflt_value"]
130
- when /^null$/i
131
- nil
132
- when /^'(.*)'$/m
133
- $1.gsub("''", "'")
134
- when /^"(.*)"$/m
135
- $1.gsub('""', '"')
136
- else
137
- field["dflt_value"]
138
- end
130
+ default = field["dflt_value"]
139
131
 
140
132
  type_metadata = fetch_type_metadata(field["type"])
141
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
133
+ default_value = extract_value_from_default(default)
134
+ default_function = extract_default_function(default_value, default)
135
+
136
+ Column.new(
137
+ field["name"],
138
+ default_value,
139
+ type_metadata,
140
+ field["notnull"].to_i == 0,
141
+ default_function,
142
+ collation: field["collation"]
143
+ )
142
144
  end
143
145
 
144
146
  def data_source_sql(name = nil, type: nil)
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  end
48
48
  end
49
49
 
50
- module ConnectionAdapters #:nodoc:
50
+ module ConnectionAdapters # :nodoc:
51
51
  # The SQLite3 adapter works with the sqlite3-ruby drivers
52
52
  # (available as gem from https://rubygems.org/gems/sqlite3).
53
53
  #
@@ -84,6 +84,7 @@ module ActiveRecord
84
84
  end
85
85
 
86
86
  def initialize(connection, logger, connection_options, config)
87
+ @memory_database = config[:database] == ":memory:"
87
88
  super(connection, logger, config)
88
89
  configure_connection
89
90
  end
@@ -153,6 +154,10 @@ module ActiveRecord
153
154
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
154
155
  alias supports_insert_conflict_target? supports_insert_on_conflict?
155
156
 
157
+ def supports_concurrent_connections?
158
+ !@memory_database
159
+ end
160
+
156
161
  def active?
157
162
  !@connection.closed?
158
163
  end
@@ -173,11 +178,11 @@ module ActiveRecord
173
178
  true
174
179
  end
175
180
 
176
- def native_database_types #:nodoc:
181
+ def native_database_types # :nodoc:
177
182
  NATIVE_DATABASE_TYPES
178
183
  end
179
184
 
180
- # Returns the current database encoding format as a string, eg: 'UTF-8'
185
+ # Returns the current database encoding format as a string, e.g. 'UTF-8'
181
186
  def encoding
182
187
  @connection.encoding.to_s
183
188
  end
@@ -206,6 +211,10 @@ module ActiveRecord
206
211
  end
207
212
  end
208
213
 
214
+ def all_foreign_keys_valid? # :nodoc:
215
+ execute("PRAGMA foreign_key_check").blank?
216
+ end
217
+
209
218
  # SCHEMA STATEMENTS ========================================
210
219
 
211
220
  def primary_keys(table_name) # :nodoc:
@@ -232,7 +241,7 @@ module ActiveRecord
232
241
  rename_table_indexes(table_name, new_name)
233
242
  end
234
243
 
235
- def add_column(table_name, column_name, type, **options) #:nodoc:
244
+ def add_column(table_name, column_name, type, **options) # :nodoc:
236
245
  if invalid_alter_table_type?(type, options)
237
246
  alter_table(table_name) do |definition|
238
247
  definition.column(column_name, type, **options)
@@ -242,16 +251,24 @@ module ActiveRecord
242
251
  end
243
252
  end
244
253
 
245
- def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
254
+ def remove_column(table_name, column_name, type = nil, **options) # :nodoc:
246
255
  alter_table(table_name) do |definition|
247
256
  definition.remove_column column_name
248
- definition.foreign_keys.delete_if do |_, fk_options|
249
- fk_options[:column] == column_name.to_s
257
+ definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
258
+ end
259
+ end
260
+
261
+ def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
262
+ alter_table(table_name) do |definition|
263
+ column_names.each do |column_name|
264
+ definition.remove_column column_name
250
265
  end
266
+ column_names = column_names.map(&:to_s)
267
+ definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
251
268
  end
252
269
  end
253
270
 
254
- def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
271
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
255
272
  default = extract_new_default_value(default_or_changes)
256
273
 
257
274
  alter_table(table_name) do |definition|
@@ -259,7 +276,7 @@ module ActiveRecord
259
276
  end
260
277
  end
261
278
 
262
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
279
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
263
280
  unless null || default.nil?
264
281
  exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
265
282
  end
@@ -268,7 +285,7 @@ module ActiveRecord
268
285
  end
269
286
  end
270
287
 
271
- def change_column(table_name, column_name, type, **options) #:nodoc:
288
+ def change_column(table_name, column_name, type, **options) # :nodoc:
272
289
  alter_table(table_name) do |definition|
273
290
  definition[column_name].instance_eval do
274
291
  self.type = aliased_types(type.to_s, type)
@@ -277,7 +294,7 @@ module ActiveRecord
277
294
  end
278
295
  end
279
296
 
280
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
297
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
281
298
  column = column_for(table_name, column_name)
282
299
  alter_table(table_name, rename: { column.name => new_column_name.to_s })
283
300
  rename_column_indexes(table_name, column.name, new_column_name)
@@ -308,8 +325,12 @@ module ActiveRecord
308
325
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
309
326
  elsif insert.update_duplicates?
310
327
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
311
- sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
312
- sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
328
+ if insert.raw_update_sql?
329
+ sql << insert.raw_update_sql
330
+ else
331
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
332
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
333
+ end
313
334
  end
314
335
 
315
336
  sql
@@ -329,18 +350,38 @@ module ActiveRecord
329
350
  end
330
351
  end
331
352
 
353
+ class SQLite3Integer < Type::Integer # :nodoc:
354
+ private
355
+ def _limit
356
+ # INTEGER storage class can be stored 8 bytes value.
357
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
358
+ limit || 8
359
+ end
360
+ end
361
+
362
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
363
+
364
+ class << self
365
+ private
366
+ def initialize_type_map(m)
367
+ super
368
+ register_class_with_limit m, %r(int)i, SQLite3Integer
369
+ end
370
+ end
371
+
372
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
373
+
332
374
  private
375
+ def type_map
376
+ TYPE_MAP
377
+ end
378
+
333
379
  # See https://www.sqlite.org/limits.html,
334
380
  # the default value is 999 when not configured.
335
381
  def bind_params_length
336
382
  999
337
383
  end
338
384
 
339
- def initialize_type_map(m = type_map)
340
- super
341
- register_class_with_limit m, %r(int)i, SQLite3Integer
342
- end
343
-
344
385
  def table_structure(table_name)
345
386
  structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
346
387
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
@@ -348,6 +389,34 @@ module ActiveRecord
348
389
  end
349
390
  alias column_definitions table_structure
350
391
 
392
+ def extract_value_from_default(default)
393
+ case default
394
+ when /^null$/i
395
+ nil
396
+ # Quoted types
397
+ when /^'(.*)'$/m
398
+ $1.gsub("''", "'")
399
+ # Quoted types
400
+ when /^"(.*)"$/m
401
+ $1.gsub('""', '"')
402
+ # Numeric types
403
+ when /\A-?\d+(\.\d*)?\z/
404
+ $&
405
+ else
406
+ # Anything else is blank or some function
407
+ # and we can't know the value of that, so return nil.
408
+ nil
409
+ end
410
+ end
411
+
412
+ def extract_default_function(default_value, default)
413
+ default if has_default_function?(default_value, default)
414
+ end
415
+
416
+ def has_default_function?(default_value, default)
417
+ !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
418
+ end
419
+
351
420
  # See: https://www.sqlite.org/lang_altertable.html
352
421
  # SQLite has an additional restriction on the ALTER TABLE statement
353
422
  def invalid_alter_table_type?(type, options)
@@ -446,6 +515,7 @@ module ActiveRecord
446
515
  options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
447
516
  options[:unique] = true if index.unique
448
517
  options[:where] = index.where if index.where
518
+ options[:order] = index.orders if index.orders
449
519
  add_index(to, columns, **options)
450
520
  end
451
521
  end
@@ -482,7 +552,7 @@ module ActiveRecord
482
552
  end
483
553
  end
484
554
 
485
- COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
555
+ COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i.freeze
486
556
 
487
557
  def table_structure_with_collation(table_name, basic_structure)
488
558
  collation_hash = {}
@@ -544,17 +614,6 @@ module ActiveRecord
544
614
 
545
615
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
546
616
  end
547
-
548
- class SQLite3Integer < Type::Integer # :nodoc:
549
- private
550
- def _limit
551
- # INTEGER storage class can be stored 8 bytes value.
552
- # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
553
- limit || 8
554
- end
555
- end
556
-
557
- ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
558
617
  end
559
618
  ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
560
619
  end
@@ -27,20 +27,21 @@ module ActiveRecord
27
27
  autoload :ReferenceDefinition
28
28
  end
29
29
 
30
- autoload_at "active_record/connection_adapters/abstract/connection_pool" do
31
- autoload :ConnectionHandler
32
- end
33
-
34
30
  autoload_under "abstract" do
35
31
  autoload :SchemaStatements
36
32
  autoload :DatabaseStatements
37
33
  autoload :DatabaseLimits
38
34
  autoload :Quoting
39
- autoload :ConnectionPool
35
+ autoload :ConnectionHandler
40
36
  autoload :QueryCache
41
37
  autoload :Savepoints
42
38
  end
43
39
 
40
+ autoload_at "active_record/connection_adapters/abstract/connection_pool" do
41
+ autoload :ConnectionPool
42
+ autoload :NullPool
43
+ end
44
+
44
45
  autoload_at "active_record/connection_adapters/abstract/transaction" do
45
46
  autoload :TransactionManager
46
47
  autoload :NullTransaction
@@ -134,10 +134,8 @@ module ActiveRecord
134
134
  # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one_replica) do
135
135
  # Dog.first # finds first Dog record stored on the shard one replica
136
136
  # end
137
- #
138
- # The database kwarg is deprecated and will be removed in 6.2.0 without replacement.
139
- def connected_to(database: nil, role: nil, shard: nil, prevent_writes: false, &blk)
140
- if legacy_connection_handling
137
+ def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
138
+ if ActiveRecord.legacy_connection_handling
141
139
  if self != Base
142
140
  raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
143
141
  end
@@ -151,31 +149,11 @@ module ActiveRecord
151
149
  end
152
150
  end
153
151
 
154
- if database && (role || shard)
155
- raise ArgumentError, "`connected_to` cannot accept a `database` argument with any other arguments."
156
- elsif database
157
- ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 6.2.0 without replacement.")
158
-
159
- if database.is_a?(Hash)
160
- role, database = database.first
161
- role = role.to_sym
162
- end
163
-
164
- db_config, owner_name = resolve_config_for_connection(database)
165
- handler = lookup_connection_handler(role)
166
-
167
- handler.establish_connection(db_config, owner_name: owner_name, role: role)
168
-
169
- with_handler(role, &blk)
170
- elsif role || shard
171
- unless role
172
- raise ArgumentError, "`connected_to` cannot accept a `shard` argument without a `role`."
173
- end
174
-
175
- with_role_and_shard(role, shard, prevent_writes, &blk)
176
- else
152
+ unless role || shard
177
153
  raise ArgumentError, "must provide a `shard` and/or `role`."
178
154
  end
155
+
156
+ with_role_and_shard(role, shard, prevent_writes, &blk)
179
157
  end
180
158
 
181
159
  # Connects a role and/or shard to the provided connection names. Optionally +prevent_writes+
@@ -194,7 +172,7 @@ module ActiveRecord
194
172
  def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
195
173
  classes = classes.flatten
196
174
 
197
- if legacy_connection_handling
175
+ if ActiveRecord.legacy_connection_handling
198
176
  raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
199
177
  end
200
178
 
@@ -202,9 +180,9 @@ module ActiveRecord
202
180
  raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
203
181
  end
204
182
 
205
- prevent_writes = true if role == reading_role
183
+ prevent_writes = true if role == ActiveRecord.reading_role
206
184
 
207
- connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
185
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
208
186
  yield
209
187
  ensure
210
188
  connected_to_stack.pop
@@ -218,13 +196,32 @@ module ActiveRecord
218
196
  # It is not recommended to use this method in a request since it
219
197
  # does not yield to a block like +connected_to+.
220
198
  def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
221
- if legacy_connection_handling
199
+ if ActiveRecord.legacy_connection_handling
222
200
  raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
223
201
  end
224
202
 
225
- prevent_writes = true if role == reading_role
203
+ prevent_writes = true if role == ActiveRecord.reading_role
226
204
 
227
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
205
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
206
+ end
207
+
208
+ # Prohibit swapping shards while inside of the passed block.
209
+ #
210
+ # In some cases you may want to be able to swap shards but not allow a
211
+ # nested call to connected_to or connected_to_many to swap again. This
212
+ # is useful in cases you're using sharding to provide per-request
213
+ # database isolation.
214
+ def prohibit_shard_swapping(enabled = true)
215
+ prev_value = ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
216
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = enabled
217
+ yield
218
+ ensure
219
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = prev_value
220
+ end
221
+
222
+ # Determine whether or not shard swapping is currently prohibited
223
+ def shard_swapping_prohibited?
224
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
228
225
  end
229
226
 
230
227
  # Prevent writing to the database regardless of role.
@@ -239,7 +236,7 @@ module ActiveRecord
239
236
  # See +READ_QUERY+ for the queries that are blocked by this
240
237
  # method.
241
238
  def while_preventing_writes(enabled = true, &block)
242
- if legacy_connection_handling
239
+ if ActiveRecord.legacy_connection_handling
243
240
  connection_handler.while_preventing_writes(enabled, &block)
244
241
  else
245
242
  connected_to(role: current_role, prevent_writes: enabled, &block)
@@ -257,8 +254,8 @@ module ActiveRecord
257
254
  end
258
255
 
259
256
  def lookup_connection_handler(handler_key) # :nodoc:
260
- if ActiveRecord::Base.legacy_connection_handling
261
- handler_key ||= ActiveRecord::Base.writing_role
257
+ if ActiveRecord.legacy_connection_handling
258
+ handler_key ||= ActiveRecord.writing_role
262
259
  connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
263
260
  else
264
261
  ActiveRecord::Base.connection_handler
@@ -267,7 +264,7 @@ module ActiveRecord
267
264
 
268
265
  # Clears the query cache for all connections associated with the current thread.
269
266
  def clear_query_caches_for_current_thread
270
- if ActiveRecord::Base.legacy_connection_handling
267
+ if ActiveRecord.legacy_connection_handling
271
268
  ActiveRecord::Base.connection_handlers.each_value do |handler|
272
269
  clear_on_handler(handler)
273
270
  end
@@ -294,19 +291,8 @@ module ActiveRecord
294
291
  end
295
292
 
296
293
  def primary_class? # :nodoc:
297
- self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
298
- end
299
-
300
- # Returns the configuration of the associated connection as a hash:
301
- #
302
- # ActiveRecord::Base.connection_config
303
- # # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
304
- #
305
- # Please use only for reading.
306
- def connection_config
307
- connection_pool.db_config.configuration_hash
294
+ self == Base || application_record_class?
308
295
  end
309
- deprecate connection_config: "Use connection_db_config instead"
310
296
 
311
297
  # Returns the db_config object from the associated connection:
312
298
  #
@@ -374,17 +360,17 @@ module ActiveRecord
374
360
  end
375
361
 
376
362
  def with_role_and_shard(role, shard, prevent_writes)
377
- prevent_writes = true if role == reading_role
363
+ prevent_writes = true if role == ActiveRecord.reading_role
378
364
 
379
- if ActiveRecord::Base.legacy_connection_handling
365
+ if ActiveRecord.legacy_connection_handling
380
366
  with_handler(role.to_sym) do
381
367
  connection_handler.while_preventing_writes(prevent_writes) do
382
- self.connected_to_stack << { shard: shard, klasses: [self] }
368
+ append_to_connected_to_stack(shard: shard, klasses: [self])
383
369
  yield
384
370
  end
385
371
  end
386
372
  else
387
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
373
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
388
374
  return_value = yield
389
375
  return_value.load if return_value.is_a? ActiveRecord::Relation
390
376
  return_value
@@ -393,6 +379,14 @@ module ActiveRecord
393
379
  self.connected_to_stack.pop
394
380
  end
395
381
 
382
+ def append_to_connected_to_stack(entry)
383
+ if shard_swapping_prohibited? && entry[:shard].present?
384
+ raise ArgumentError, "cannot swap `shard` while shard swapping is prohibited."
385
+ end
386
+
387
+ connected_to_stack << entry
388
+ end
389
+
396
390
  def swap_connection_handler(handler, &blk) # :nodoc:
397
391
  old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
398
392
  return_value = yield