activerecord 6.1.7 → 7.1.5

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 (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -15,41 +15,25 @@ module ActiveRecord
15
15
  !READ_QUERY.match?(sql.b)
16
16
  end
17
17
 
18
- def explain(arel, binds = [])
19
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
20
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
18
+ def explain(arel, binds = [], _options = [])
19
+ sql = "EXPLAIN QUERY PLAN " + to_sql(arel, binds)
20
+ result = internal_exec_query(sql, "EXPLAIN", [])
21
+ SQLite3::ExplainPrettyPrinter.new.pp(result)
21
22
  end
22
23
 
23
- def execute(sql, name = nil) #:nodoc:
24
- if preventing_writes? && write_query?(sql)
25
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
26
- end
24
+ def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
25
+ sql = transform_query(sql)
26
+ check_if_write_query(sql)
27
27
 
28
- materialize_transactions
29
- mark_transaction_written_if_write(sql)
30
-
31
- log(sql, name) do
32
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
33
- @connection.execute(sql)
34
- end
35
- end
36
- end
37
-
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
42
-
43
- materialize_transactions
44
28
  mark_transaction_written_if_write(sql)
45
29
 
46
30
  type_casted_binds = type_casted_binds(binds)
47
31
 
48
- log(sql, name, binds, type_casted_binds) do
49
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
32
+ log(sql, name, binds, type_casted_binds, async: async) do
33
+ with_raw_connection do |conn|
50
34
  # Don't cache statements if they are not prepared
51
35
  unless prepare
52
- stmt = @connection.prepare(sql)
36
+ stmt = conn.prepare(sql)
53
37
  begin
54
38
  cols = stmt.columns
55
39
  unless without_prepared_statement?(binds)
@@ -60,76 +44,107 @@ module ActiveRecord
60
44
  stmt.close
61
45
  end
62
46
  else
63
- stmt = @statements[sql] ||= @connection.prepare(sql)
47
+ stmt = @statements[sql] ||= conn.prepare(sql)
64
48
  cols = stmt.columns
65
49
  stmt.reset!
66
50
  stmt.bind_params(type_casted_binds)
67
51
  records = stmt.to_a
68
52
  end
53
+ verified!
69
54
 
70
55
  build_result(columns: cols, rows: records)
71
56
  end
72
57
  end
73
58
  end
74
59
 
75
- def exec_delete(sql, name = "SQL", binds = [])
76
- exec_query(sql, name, binds)
77
- @connection.changes
60
+ def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
61
+ internal_exec_query(sql, name, binds)
62
+ @raw_connection.changes
78
63
  end
79
64
  alias :exec_update :exec_delete
80
65
 
81
- def begin_isolated_db_transaction(isolation) #:nodoc
66
+ def begin_isolated_db_transaction(isolation) # :nodoc:
82
67
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
83
68
  raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
84
69
 
85
- Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
86
- @connection.read_uncommitted = true
87
- begin_db_transaction
70
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
71
+ ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
72
+ conn.read_uncommitted = true
73
+ begin_db_transaction
74
+ end
88
75
  end
89
76
 
90
- def begin_db_transaction #:nodoc:
91
- log("begin transaction", "TRANSACTION") { @connection.transaction }
77
+ def begin_db_transaction # :nodoc:
78
+ log("begin transaction", "TRANSACTION") do
79
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
80
+ result = conn.transaction
81
+ verified!
82
+ result
83
+ end
84
+ end
92
85
  end
93
86
 
94
- def commit_db_transaction #:nodoc:
95
- log("commit transaction", "TRANSACTION") { @connection.commit }
87
+ def commit_db_transaction # :nodoc:
88
+ log("commit transaction", "TRANSACTION") do
89
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
90
+ conn.commit
91
+ end
92
+ end
96
93
  reset_read_uncommitted
97
94
  end
98
95
 
99
- def exec_rollback_db_transaction #:nodoc:
100
- log("rollback transaction", "TRANSACTION") { @connection.rollback }
96
+ def exec_rollback_db_transaction # :nodoc:
97
+ log("rollback transaction", "TRANSACTION") do
98
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
99
+ conn.rollback
100
+ end
101
+ end
101
102
  reset_read_uncommitted
102
103
  end
103
104
 
105
+ # https://stackoverflow.com/questions/17574784
106
+ # https://www.sqlite.org/lang_datefunc.html
107
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
108
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
109
+
110
+ def high_precision_current_timestamp
111
+ HIGH_PRECISION_CURRENT_TIMESTAMP
112
+ end
113
+
104
114
  private
115
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
116
+ log(sql, name, async: async) do
117
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
118
+ result = conn.execute(sql)
119
+ verified!
120
+ result
121
+ end
122
+ end
123
+ end
124
+
105
125
  def reset_read_uncommitted
106
- read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
126
+ read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
107
127
  return unless read_uncommitted
108
128
 
109
- @connection.read_uncommitted = read_uncommitted
129
+ @raw_connection&.read_uncommitted = read_uncommitted
110
130
  end
111
131
 
112
132
  def execute_batch(statements, name = nil)
133
+ statements = statements.map { |sql| transform_query(sql) }
113
134
  sql = combine_multi_statements(statements)
114
135
 
115
- if preventing_writes? && write_query?(sql)
116
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
117
- end
118
-
119
- materialize_transactions
136
+ check_if_write_query(sql)
120
137
  mark_transaction_written_if_write(sql)
121
138
 
122
139
  log(sql, name) do
123
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
124
- @connection.execute_batch2(sql)
140
+ with_raw_connection do |conn|
141
+ result = conn.execute_batch2(sql)
142
+ verified!
143
+ result
125
144
  end
126
145
  end
127
146
  end
128
147
 
129
- def last_inserted_id(result)
130
- @connection.last_insert_row_id
131
- end
132
-
133
148
  def build_fixture_statements(fixture_set)
134
149
  fixture_set.flat_map do |table_name, fixtures|
135
150
  next if fixtures.empty?
@@ -140,6 +155,10 @@ module ActiveRecord
140
155
  def build_truncate_statement(table_name)
141
156
  "DELETE FROM #{quote_table_name(table_name)}"
142
157
  end
158
+
159
+ def returning_column_values(result)
160
+ result.rows.first
161
+ end
143
162
  end
144
163
  end
145
164
  end
@@ -4,8 +4,11 @@ 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
- @connection.class.quote(s)
11
+ ::SQLite3::Database.quote(s)
9
12
  end
10
13
 
11
14
  def quote_table_name_for_assignment(table, attr)
@@ -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
@@ -58,7 +89,7 @@ module ActiveRecord
58
89
  (
59
90
  (?:
60
91
  # "table_name"."column_name" | function(one or no argument)
61
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
92
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
62
93
  )
63
94
  (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
64
95
  )
@@ -71,8 +102,9 @@ module ActiveRecord
71
102
  (
72
103
  (?:
73
104
  # "table_name"."column_name" | function(one or no argument)
74
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
105
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
75
106
  )
107
+ (?:\s+COLLATE\s+(?:\w+|"\w+"))?
76
108
  (?:\s+ASC|\s+DESC)?
77
109
  )
78
110
  (?:\s*,\s*\g<1>)*
@@ -80,22 +112,6 @@ module ActiveRecord
80
112
  /ix
81
113
 
82
114
  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
115
  end
100
116
  end
101
117
  end
@@ -3,7 +3,14 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
+ # = Active Record SQLite3 Adapter \Table Definition
6
7
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
8
+ def change_column(column_name, type, **options)
9
+ name = column_name.to_s
10
+ @columns_hash[name] = nil
11
+ column(name, type, **options)
12
+ end
13
+
7
14
  def references(*args, **options)
8
15
  super(*args, type: :integer, **options)
9
16
  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
+ internal_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,9 +21,9 @@ 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
- columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
26
+ columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
27
  col["name"]
28
28
  end
29
29
 
@@ -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|
@@ -100,7 +102,9 @@ module ActiveRecord
100
102
  end
101
103
  end
102
104
 
103
- def remove_check_constraint(table_name, expression = nil, **options)
105
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
106
+ return if if_exists && !check_constraint_exists?(table_name, **options)
107
+
104
108
  check_constraints = check_constraints(table_name)
105
109
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
106
110
  check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
@@ -111,9 +115,13 @@ module ActiveRecord
111
115
  SQLite3::SchemaDumper.create(self, options)
112
116
  end
113
117
 
118
+ def schema_creation # :nodoc
119
+ SQLite3::SchemaCreation.new(self)
120
+ end
121
+
114
122
  private
115
- def schema_creation
116
- SQLite3::SchemaCreation.new(self)
123
+ def valid_table_definition_options
124
+ super + [:rename]
117
125
  end
118
126
 
119
127
  def create_table_definition(name, **options)
@@ -124,21 +132,34 @@ module ActiveRecord
124
132
  super unless internal
125
133
  end
126
134
 
127
- 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
135
+ def new_column_from_field(table_name, field, definitions)
136
+ default = field["dflt_value"]
139
137
 
140
138
  type_metadata = fetch_type_metadata(field["type"])
141
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
139
+ default_value = extract_value_from_default(default)
140
+ default_function = extract_default_function(default_value, default)
141
+ rowid = is_column_the_rowid?(field, definitions)
142
+
143
+ Column.new(
144
+ field["name"],
145
+ default_value,
146
+ type_metadata,
147
+ field["notnull"].to_i == 0,
148
+ default_function,
149
+ collation: field["collation"],
150
+ auto_increment: field["auto_increment"],
151
+ rowid: rowid
152
+ )
153
+ end
154
+
155
+ INTEGER_REGEX = /integer/i
156
+ # if a rowid table has a primary key that consists of a single column
157
+ # and the declared type of that column is "INTEGER" in any mixture of upper and lower case,
158
+ # then the column becomes an alias for the rowid.
159
+ def is_column_the_rowid?(field, column_definitions)
160
+ return false unless INTEGER_REGEX.match?(field["type"]) && field["pk"] == 1
161
+ # is the primary key a single column?
162
+ column_definitions.one? { |c| c["pk"] > 0 }
142
163
  end
143
164
 
144
165
  def data_source_sql(name = nil, type: nil)