activerecord 6.1.7.6 → 7.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1570 -1016
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +20 -22
  17. data/lib/active_record/associations/collection_proxy.rb +15 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  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 +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
  93. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
  96. data/lib/active_record/connection_adapters.rb +6 -5
  97. data/lib/active_record/connection_handling.rb +49 -55
  98. data/lib/active_record/core.rb +123 -148
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  100. data/lib/active_record/database_configurations/database_config.rb +12 -9
  101. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  102. data/lib/active_record/database_configurations/url_config.rb +2 -2
  103. data/lib/active_record/database_configurations.rb +15 -32
  104. data/lib/active_record/delegated_type.rb +53 -12
  105. data/lib/active_record/destroy_association_async_job.rb +1 -1
  106. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  107. data/lib/active_record/dynamic_matchers.rb +1 -1
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  109. data/lib/active_record/encryption/cipher.rb +53 -0
  110. data/lib/active_record/encryption/config.rb +44 -0
  111. data/lib/active_record/encryption/configurable.rb +67 -0
  112. data/lib/active_record/encryption/context.rb +35 -0
  113. data/lib/active_record/encryption/contexts.rb +72 -0
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  116. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  118. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  119. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  120. data/lib/active_record/encryption/encryptor.rb +155 -0
  121. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  122. data/lib/active_record/encryption/errors.rb +15 -0
  123. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  124. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  125. data/lib/active_record/encryption/key.rb +28 -0
  126. data/lib/active_record/encryption/key_generator.rb +42 -0
  127. data/lib/active_record/encryption/key_provider.rb +46 -0
  128. data/lib/active_record/encryption/message.rb +33 -0
  129. data/lib/active_record/encryption/message_serializer.rb +90 -0
  130. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  131. data/lib/active_record/encryption/properties.rb +76 -0
  132. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  133. data/lib/active_record/encryption/scheme.rb +99 -0
  134. data/lib/active_record/encryption.rb +55 -0
  135. data/lib/active_record/enum.rb +50 -43
  136. data/lib/active_record/errors.rb +67 -4
  137. data/lib/active_record/explain_registry.rb +11 -6
  138. data/lib/active_record/explain_subscriber.rb +1 -1
  139. data/lib/active_record/fixture_set/file.rb +15 -1
  140. data/lib/active_record/fixture_set/table_row.rb +41 -6
  141. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  142. data/lib/active_record/fixtures.rb +20 -23
  143. data/lib/active_record/future_result.rb +139 -0
  144. data/lib/active_record/gem_version.rb +5 -5
  145. data/lib/active_record/inheritance.rb +55 -17
  146. data/lib/active_record/insert_all.rb +80 -14
  147. data/lib/active_record/integration.rb +4 -3
  148. data/lib/active_record/internal_metadata.rb +1 -5
  149. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  150. data/lib/active_record/locking/optimistic.rb +36 -21
  151. data/lib/active_record/locking/pessimistic.rb +10 -4
  152. data/lib/active_record/log_subscriber.rb +23 -7
  153. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  154. data/lib/active_record/middleware/database_selector.rb +18 -6
  155. data/lib/active_record/middleware/shard_selector.rb +60 -0
  156. data/lib/active_record/migration/command_recorder.rb +8 -9
  157. data/lib/active_record/migration/compatibility.rb +93 -46
  158. data/lib/active_record/migration/join_table.rb +1 -1
  159. data/lib/active_record/migration.rb +167 -87
  160. data/lib/active_record/model_schema.rb +58 -59
  161. data/lib/active_record/nested_attributes.rb +13 -12
  162. data/lib/active_record/no_touching.rb +3 -3
  163. data/lib/active_record/null_relation.rb +2 -6
  164. data/lib/active_record/persistence.rb +231 -61
  165. data/lib/active_record/query_cache.rb +2 -2
  166. data/lib/active_record/query_logs.rb +149 -0
  167. data/lib/active_record/querying.rb +16 -6
  168. data/lib/active_record/railtie.rb +136 -22
  169. data/lib/active_record/railties/controller_runtime.rb +4 -5
  170. data/lib/active_record/railties/databases.rake +78 -136
  171. data/lib/active_record/readonly_attributes.rb +11 -0
  172. data/lib/active_record/reflection.rb +80 -49
  173. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  174. data/lib/active_record/relation/batches.rb +6 -6
  175. data/lib/active_record/relation/calculations.rb +92 -60
  176. data/lib/active_record/relation/delegation.rb +7 -7
  177. data/lib/active_record/relation/finder_methods.rb +31 -35
  178. data/lib/active_record/relation/merger.rb +20 -13
  179. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  180. data/lib/active_record/relation/predicate_builder.rb +1 -6
  181. data/lib/active_record/relation/query_attribute.rb +28 -11
  182. data/lib/active_record/relation/query_methods.rb +304 -68
  183. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  184. data/lib/active_record/relation/spawn_methods.rb +2 -2
  185. data/lib/active_record/relation/where_clause.rb +10 -19
  186. data/lib/active_record/relation.rb +189 -88
  187. data/lib/active_record/result.rb +23 -11
  188. data/lib/active_record/runtime_registry.rb +9 -13
  189. data/lib/active_record/sanitization.rb +17 -12
  190. data/lib/active_record/schema.rb +38 -23
  191. data/lib/active_record/schema_dumper.rb +29 -19
  192. data/lib/active_record/schema_migration.rb +4 -4
  193. data/lib/active_record/scoping/default.rb +60 -13
  194. data/lib/active_record/scoping/named.rb +3 -11
  195. data/lib/active_record/scoping.rb +64 -34
  196. data/lib/active_record/serialization.rb +6 -1
  197. data/lib/active_record/signed_id.rb +3 -3
  198. data/lib/active_record/store.rb +2 -2
  199. data/lib/active_record/suppressor.rb +11 -15
  200. data/lib/active_record/table_metadata.rb +6 -2
  201. data/lib/active_record/tasks/database_tasks.rb +127 -60
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  204. data/lib/active_record/test_databases.rb +1 -1
  205. data/lib/active_record/test_fixtures.rb +9 -6
  206. data/lib/active_record/timestamp.rb +3 -4
  207. data/lib/active_record/transactions.rb +12 -17
  208. data/lib/active_record/translation.rb +3 -3
  209. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  210. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  211. data/lib/active_record/type/internal/timezone.rb +2 -2
  212. data/lib/active_record/type/serialized.rb +9 -5
  213. data/lib/active_record/type/type_map.rb +17 -20
  214. data/lib/active_record/type.rb +1 -2
  215. data/lib/active_record/validations/associated.rb +4 -4
  216. data/lib/active_record/validations/presence.rb +2 -2
  217. data/lib/active_record/validations/uniqueness.rb +4 -4
  218. data/lib/active_record/version.rb +1 -1
  219. data/lib/active_record.rb +225 -27
  220. data/lib/arel/attributes/attribute.rb +0 -8
  221. data/lib/arel/crud.rb +28 -22
  222. data/lib/arel/delete_manager.rb +18 -4
  223. data/lib/arel/filter_predications.rb +9 -0
  224. data/lib/arel/insert_manager.rb +2 -3
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/casted.rb +1 -1
  227. data/lib/arel/nodes/delete_statement.rb +12 -13
  228. data/lib/arel/nodes/filter.rb +10 -0
  229. data/lib/arel/nodes/function.rb +1 -0
  230. data/lib/arel/nodes/insert_statement.rb +2 -2
  231. data/lib/arel/nodes/select_core.rb +2 -2
  232. data/lib/arel/nodes/select_statement.rb +2 -2
  233. data/lib/arel/nodes/update_statement.rb +8 -3
  234. data/lib/arel/nodes.rb +1 -0
  235. data/lib/arel/predications.rb +11 -3
  236. data/lib/arel/select_manager.rb +10 -4
  237. data/lib/arel/table.rb +0 -1
  238. data/lib/arel/tree_manager.rb +0 -12
  239. data/lib/arel/update_manager.rb +18 -4
  240. data/lib/arel/visitors/dot.rb +80 -90
  241. data/lib/arel/visitors/mysql.rb +8 -2
  242. data/lib/arel/visitors/postgresql.rb +0 -10
  243. data/lib/arel/visitors/to_sql.rb +58 -2
  244. data/lib/arel.rb +2 -1
  245. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  247. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  248. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  249. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  250. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  251. metadata +55 -11
@@ -20,10 +20,9 @@ module ActiveRecord
20
20
  SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
21
21
  end
22
22
 
23
- def execute(sql, name = nil) #:nodoc:
24
- if preventing_writes? && write_query?(sql)
25
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
26
- end
23
+ def execute(sql, name = nil) # :nodoc:
24
+ sql = transform_query(sql)
25
+ check_if_write_query(sql)
27
26
 
28
27
  materialize_transactions
29
28
  mark_transaction_written_if_write(sql)
@@ -35,17 +34,16 @@ module ActiveRecord
35
34
  end
36
35
  end
37
36
 
38
- def exec_query(sql, name = nil, binds = [], prepare: false)
39
- if preventing_writes? && write_query?(sql)
40
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
41
- 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)
42
40
 
43
41
  materialize_transactions
44
42
  mark_transaction_written_if_write(sql)
45
43
 
46
44
  type_casted_binds = type_casted_binds(binds)
47
45
 
48
- log(sql, name, binds, type_casted_binds) do
46
+ log(sql, name, binds, type_casted_binds, async: async) do
49
47
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
50
48
  # Don't cache statements if they are not prepared
51
49
  unless prepare
@@ -72,49 +70,57 @@ module ActiveRecord
72
70
  end
73
71
  end
74
72
 
75
- def exec_delete(sql, name = "SQL", binds = [])
73
+ def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
76
74
  exec_query(sql, name, binds)
77
75
  @connection.changes
78
76
  end
79
77
  alias :exec_update :exec_delete
80
78
 
81
- def begin_isolated_db_transaction(isolation) #:nodoc
79
+ def begin_isolated_db_transaction(isolation) # :nodoc:
82
80
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
83
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?
84
82
 
85
- 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")
86
84
  @connection.read_uncommitted = true
87
85
  begin_db_transaction
88
86
  end
89
87
 
90
- def begin_db_transaction #:nodoc:
88
+ def begin_db_transaction # :nodoc:
91
89
  log("begin transaction", "TRANSACTION") { @connection.transaction }
92
90
  end
93
91
 
94
- def commit_db_transaction #:nodoc:
92
+ def commit_db_transaction # :nodoc:
95
93
  log("commit transaction", "TRANSACTION") { @connection.commit }
96
94
  reset_read_uncommitted
97
95
  end
98
96
 
99
- def exec_rollback_db_transaction #:nodoc:
97
+ def exec_rollback_db_transaction # :nodoc:
100
98
  log("rollback transaction", "TRANSACTION") { @connection.rollback }
101
99
  reset_read_uncommitted
102
100
  end
103
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
+
104
111
  private
105
112
  def reset_read_uncommitted
106
- read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
113
+ read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
107
114
  return unless read_uncommitted
108
115
 
109
116
  @connection.read_uncommitted = read_uncommitted
110
117
  end
111
118
 
112
119
  def execute_batch(statements, name = nil)
120
+ statements = statements.map { |sql| transform_query(sql) }
113
121
  sql = combine_multi_statements(statements)
114
122
 
115
- if preventing_writes? && write_query?(sql)
116
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
117
- end
123
+ check_if_write_query(sql)
118
124
 
119
125
  materialize_transactions
120
126
  mark_transaction_written_if_write(sql)
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  module Quoting # :nodoc:
7
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
9
+
7
10
  def quote_string(s)
8
11
  @connection.class.quote(s)
9
12
  end
@@ -13,11 +16,11 @@ module ActiveRecord
13
16
  end
14
17
 
15
18
  def quote_table_name(name)
16
- self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
19
+ QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "\".\"").freeze
17
20
  end
18
21
 
19
22
  def quote_column_name(name)
20
- self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
23
+ QUOTED_COLUMN_NAMES[name] ||= %Q("#{super.gsub('"', '""')}")
21
24
  end
22
25
 
23
26
  def quoted_time(value)
@@ -45,6 +48,34 @@ module ActiveRecord
45
48
  0
46
49
  end
47
50
 
51
+ def quote_default_expression(value, column) # :nodoc:
52
+ if value.is_a?(Proc)
53
+ value = value.call
54
+ if value.match?(/\A\w+\(.*\)\z/)
55
+ "(#{value})"
56
+ else
57
+ value
58
+ end
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ def type_cast(value) # :nodoc:
65
+ case value
66
+ when BigDecimal
67
+ value.to_f
68
+ when String
69
+ if value.encoding == Encoding::ASCII_8BIT
70
+ super(value.encode(Encoding::UTF_8))
71
+ else
72
+ super
73
+ end
74
+ else
75
+ super
76
+ end
77
+ end
78
+
48
79
  def column_name_matcher
49
80
  COLUMN_NAME
50
81
  end
@@ -80,22 +111,6 @@ module ActiveRecord
80
111
  /ix
81
112
 
82
113
  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
114
  end
100
115
  end
101
116
  end
@@ -4,6 +4,12 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
7
+ def change_column(column_name, type, **options)
8
+ name = column_name.to_s
9
+ @columns_hash[name] = nil
10
+ column(name, type, **options)
11
+ end
12
+
7
13
  def references(*args, **options)
8
14
  super(*args, type: :integer, **options)
9
15
  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_")
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  WHERE name = #{quote(row['name'])} AND type = 'index'
22
22
  SQL
23
23
 
24
- /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
24
+ /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?(?:\s*\/\*.*\*\/)?\z/i =~ index_sql
25
25
 
26
26
  columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
27
  col["name"]
@@ -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.delete(: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)
@@ -82,11 +84,11 @@ module ActiveRecord
82
84
  table_sql = query_value(<<-SQL, "SCHEMA")
83
85
  SELECT sql
84
86
  FROM sqlite_master
85
- WHERE name = #{quote_table_name(table_name)} AND type = 'table'
87
+ WHERE name = #{quote(table_name)} AND type = 'table'
86
88
  UNION ALL
87
89
  SELECT sql
88
90
  FROM sqlite_temp_master
89
- WHERE name = #{quote_table_name(table_name)} AND type = 'table'
91
+ WHERE name = #{quote(table_name)} AND type = 'table'
90
92
  SQL
91
93
 
92
94
  table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
@@ -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,16 +285,13 @@ 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
- definition[column_name].instance_eval do
274
- self.type = aliased_types(type.to_s, type)
275
- self.options.merge!(options)
276
- end
290
+ definition.change_column(column_name, type, **options)
277
291
  end
278
292
  end
279
293
 
280
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
294
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
281
295
  column = column_for(table_name, column_name)
282
296
  alter_table(table_name, rename: { column.name => new_column_name.to_s })
283
297
  rename_column_indexes(table_name, column.name, new_column_name)
@@ -308,8 +322,12 @@ module ActiveRecord
308
322
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
309
323
  elsif insert.update_duplicates?
310
324
  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(",")
325
+ if insert.raw_update_sql?
326
+ sql << insert.raw_update_sql
327
+ else
328
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
329
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
330
+ end
313
331
  end
314
332
 
315
333
  sql
@@ -320,7 +338,7 @@ module ActiveRecord
320
338
  end
321
339
 
322
340
  def get_database_version # :nodoc:
323
- SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
341
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
324
342
  end
325
343
 
326
344
  def check_version # :nodoc:
@@ -329,18 +347,38 @@ module ActiveRecord
329
347
  end
330
348
  end
331
349
 
350
+ class SQLite3Integer < Type::Integer # :nodoc:
351
+ private
352
+ def _limit
353
+ # INTEGER storage class can be stored 8 bytes value.
354
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
355
+ limit || 8
356
+ end
357
+ end
358
+
359
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
360
+
361
+ class << self
362
+ private
363
+ def initialize_type_map(m)
364
+ super
365
+ register_class_with_limit m, %r(int)i, SQLite3Integer
366
+ end
367
+ end
368
+
369
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
370
+
332
371
  private
372
+ def type_map
373
+ TYPE_MAP
374
+ end
375
+
333
376
  # See https://www.sqlite.org/limits.html,
334
377
  # the default value is 999 when not configured.
335
378
  def bind_params_length
336
379
  999
337
380
  end
338
381
 
339
- def initialize_type_map(m = type_map)
340
- super
341
- register_class_with_limit m, %r(int)i, SQLite3Integer
342
- end
343
-
344
382
  def table_structure(table_name)
345
383
  structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
346
384
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
@@ -348,6 +386,34 @@ module ActiveRecord
348
386
  end
349
387
  alias column_definitions table_structure
350
388
 
389
+ def extract_value_from_default(default)
390
+ case default
391
+ when /^null$/i
392
+ nil
393
+ # Quoted types
394
+ when /^'(.*)'$/m
395
+ $1.gsub("''", "'")
396
+ # Quoted types
397
+ when /^"(.*)"$/m
398
+ $1.gsub('""', '"')
399
+ # Numeric types
400
+ when /\A-?\d+(\.\d*)?\z/
401
+ $&
402
+ else
403
+ # Anything else is blank or some function
404
+ # and we can't know the value of that, so return nil.
405
+ nil
406
+ end
407
+ end
408
+
409
+ def extract_default_function(default_value, default)
410
+ default if has_default_function?(default_value, default)
411
+ end
412
+
413
+ def has_default_function?(default_value, default)
414
+ !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
415
+ end
416
+
351
417
  # See: https://www.sqlite.org/lang_altertable.html
352
418
  # SQLite has an additional restriction on the ALTER TABLE statement
353
419
  def invalid_alter_table_type?(type, options)
@@ -408,8 +474,14 @@ module ActiveRecord
408
474
  options[:rename][column.name.to_sym] ||
409
475
  column.name) : column.name
410
476
 
477
+ if column.has_default?
478
+ type = lookup_cast_type_from_column(column)
479
+ default = type.deserialize(column.default)
480
+ default = -> { column.default_function } if default.nil?
481
+ end
482
+
411
483
  @definition.column(column_name, column.type,
412
- limit: column.limit, default: column.default,
484
+ limit: column.limit, default: default,
413
485
  precision: column.precision, scale: column.scale,
414
486
  null: column.null, collation: column.collation,
415
487
  primary_key: column_name == from_primary_key
@@ -446,6 +518,7 @@ module ActiveRecord
446
518
  options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
447
519
  options[:unique] = true if index.unique
448
520
  options[:where] = index.where if index.where
521
+ options[:order] = index.orders if index.orders
449
522
  add_index(to, columns, **options)
450
523
  end
451
524
  end
@@ -482,7 +555,7 @@ module ActiveRecord
482
555
  end
483
556
  end
484
557
 
485
- COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
558
+ COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i.freeze
486
559
 
487
560
  def table_structure_with_collation(table_name, basic_structure)
488
561
  collation_hash = {}
@@ -544,17 +617,6 @@ module ActiveRecord
544
617
 
545
618
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
546
619
  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
620
  end
559
621
  ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
560
622
  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