activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -13,6 +13,7 @@ module ActiveRecord
13
13
  @columns_hash = {}
14
14
  @primary_keys = {}
15
15
  @data_sources = {}
16
+ @indexes = {}
16
17
  end
17
18
 
18
19
  def initialize_dup(other)
@@ -21,22 +22,27 @@ module ActiveRecord
21
22
  @columns_hash = @columns_hash.dup
22
23
  @primary_keys = @primary_keys.dup
23
24
  @data_sources = @data_sources.dup
25
+ @indexes = @indexes.dup
24
26
  end
25
27
 
26
28
  def encode_with(coder)
27
- coder["columns"] = @columns
28
- coder["columns_hash"] = @columns_hash
29
- coder["primary_keys"] = @primary_keys
30
- coder["data_sources"] = @data_sources
31
- coder["version"] = connection.migration_context.current_version
29
+ coder["columns"] = @columns
30
+ coder["columns_hash"] = @columns_hash
31
+ coder["primary_keys"] = @primary_keys
32
+ coder["data_sources"] = @data_sources
33
+ coder["indexes"] = @indexes
34
+ coder["version"] = connection.migration_context.current_version
35
+ coder["database_version"] = database_version
32
36
  end
33
37
 
34
38
  def init_with(coder)
35
- @columns = coder["columns"]
36
- @columns_hash = coder["columns_hash"]
37
- @primary_keys = coder["primary_keys"]
38
- @data_sources = coder["data_sources"]
39
- @version = coder["version"]
39
+ @columns = coder["columns"]
40
+ @columns_hash = coder["columns_hash"]
41
+ @primary_keys = coder["primary_keys"]
42
+ @data_sources = coder["data_sources"]
43
+ @indexes = coder["indexes"] || {}
44
+ @version = coder["version"]
45
+ @database_version = coder["database_version"]
40
46
  end
41
47
 
42
48
  def primary_keys(table_name)
@@ -57,6 +63,7 @@ module ActiveRecord
57
63
  primary_keys(table_name)
58
64
  columns(table_name)
59
65
  columns_hash(table_name)
66
+ indexes(table_name)
60
67
  end
61
68
  end
62
69
 
@@ -77,17 +84,32 @@ module ActiveRecord
77
84
  }]
78
85
  end
79
86
 
87
+ # Checks whether the columns hash is already cached for a table.
88
+ def columns_hash?(table_name)
89
+ @columns_hash.key?(table_name)
90
+ end
91
+
92
+ def indexes(table_name)
93
+ @indexes[table_name] ||= connection.indexes(table_name)
94
+ end
95
+
96
+ def database_version # :nodoc:
97
+ @database_version ||= connection.get_database_version
98
+ end
99
+
80
100
  # Clears out internal caches
81
101
  def clear!
82
102
  @columns.clear
83
103
  @columns_hash.clear
84
104
  @primary_keys.clear
85
105
  @data_sources.clear
106
+ @indexes.clear
86
107
  @version = nil
108
+ @database_version = nil
87
109
  end
88
110
 
89
111
  def size
90
- [@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
112
+ [@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
91
113
  end
92
114
 
93
115
  # Clear out internal caches for the data source +name+.
@@ -96,20 +118,21 @@ module ActiveRecord
96
118
  @columns_hash.delete name
97
119
  @primary_keys.delete name
98
120
  @data_sources.delete name
121
+ @indexes.delete name
99
122
  end
100
123
 
101
124
  def marshal_dump
102
125
  # if we get current version during initialization, it happens stack over flow.
103
126
  @version = connection.migration_context.current_version
104
- [@version, @columns, @columns_hash, @primary_keys, @data_sources]
127
+ [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
105
128
  end
106
129
 
107
130
  def marshal_load(array)
108
- @version, @columns, @columns_hash, @primary_keys, @data_sources = array
131
+ @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
+ @indexes = @indexes || {}
109
133
  end
110
134
 
111
135
  private
112
-
113
136
  def prepare_data_sources
114
137
  connection.data_sources.each { |source| @data_sources[source] = true }
115
138
  end
@@ -16,19 +16,22 @@ module ActiveRecord
16
16
 
17
17
  def ==(other)
18
18
  other.is_a?(SqlTypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
19
+ sql_type == other.sql_type &&
20
+ type == other.type &&
21
+ limit == other.limit &&
22
+ precision == other.precision &&
23
+ scale == other.scale
20
24
  end
21
25
  alias eql? ==
22
26
 
23
27
  def hash
24
- attributes_for_hash.hash
28
+ SqlTypeMetadata.hash ^
29
+ sql_type.hash ^
30
+ type.hash ^
31
+ limit.hash ^
32
+ precision.hash >> 1 ^
33
+ scale.hash >> 2
25
34
  end
26
-
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, sql_type, type, limit, precision, scale]
31
- end
32
35
  end
33
36
  end
34
37
  end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module DatabaseStatements
7
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
8
+ :begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback, :with
9
+ ) # :nodoc:
10
+ private_constant :READ_QUERY
11
+
12
+ def write_query?(sql) # :nodoc:
13
+ !READ_QUERY.match?(sql)
14
+ end
15
+
16
+ def execute(sql, name = nil) #:nodoc:
17
+ if preventing_writes? && write_query?(sql)
18
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
19
+ end
20
+
21
+ materialize_transactions
22
+
23
+ log(sql, name) do
24
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
25
+ @connection.execute(sql)
26
+ end
27
+ end
28
+ end
29
+
30
+ def exec_query(sql, name = nil, binds = [], prepare: false)
31
+ if preventing_writes? && write_query?(sql)
32
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
33
+ end
34
+
35
+ materialize_transactions
36
+
37
+ type_casted_binds = type_casted_binds(binds)
38
+
39
+ log(sql, name, binds, type_casted_binds) do
40
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
41
+ # Don't cache statements if they are not prepared
42
+ unless prepare
43
+ stmt = @connection.prepare(sql)
44
+ begin
45
+ cols = stmt.columns
46
+ unless without_prepared_statement?(binds)
47
+ stmt.bind_params(type_casted_binds)
48
+ end
49
+ records = stmt.to_a
50
+ ensure
51
+ stmt.close
52
+ end
53
+ else
54
+ stmt = @statements[sql] ||= @connection.prepare(sql)
55
+ cols = stmt.columns
56
+ stmt.reset!
57
+ stmt.bind_params(type_casted_binds)
58
+ records = stmt.to_a
59
+ end
60
+
61
+ ActiveRecord::Result.new(cols, records)
62
+ end
63
+ end
64
+ end
65
+
66
+ def exec_delete(sql, name = "SQL", binds = [])
67
+ exec_query(sql, name, binds)
68
+ @connection.changes
69
+ end
70
+ alias :exec_update :exec_delete
71
+
72
+ def begin_db_transaction #:nodoc:
73
+ log("begin transaction", nil) { @connection.transaction }
74
+ end
75
+
76
+ def commit_db_transaction #:nodoc:
77
+ log("commit transaction", nil) { @connection.commit }
78
+ end
79
+
80
+ def exec_rollback_db_transaction #:nodoc:
81
+ log("rollback transaction", nil) { @connection.rollback }
82
+ end
83
+
84
+
85
+ private
86
+ def execute_batch(statements, name = nil)
87
+ sql = combine_multi_statements(statements)
88
+
89
+ if preventing_writes? && write_query?(sql)
90
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
91
+ end
92
+
93
+ materialize_transactions
94
+
95
+ log(sql, name) do
96
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
97
+ @connection.execute_batch2(sql)
98
+ end
99
+ end
100
+ end
101
+
102
+ def last_inserted_id(result)
103
+ @connection.last_insert_row_id
104
+ end
105
+
106
+ def build_fixture_statements(fixture_set)
107
+ fixture_set.flat_map do |table_name, fixtures|
108
+ next if fixtures.empty?
109
+ fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
110
+ end.compact
111
+ end
112
+
113
+ def build_truncate_statement(table_name)
114
+ "DELETE FROM #{quote_table_name(table_name)}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -13,11 +13,11 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def quote_table_name(name)
16
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
16
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
17
  end
18
18
 
19
19
  def quote_column_name(name)
20
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
20
+ self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
21
21
  end
22
22
 
23
23
  def quoted_time(value)
@@ -30,23 +30,58 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def quoted_true
33
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
33
+ "1"
34
34
  end
35
35
 
36
36
  def unquoted_true
37
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
37
+ 1
38
38
  end
39
39
 
40
40
  def quoted_false
41
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
41
+ "0"
42
42
  end
43
43
 
44
44
  def unquoted_false
45
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
45
+ 0
46
46
  end
47
47
 
48
- private
48
+ def column_name_matcher
49
+ COLUMN_NAME
50
+ end
51
+
52
+ def column_name_with_order_matcher
53
+ COLUMN_NAME_WITH_ORDER
54
+ end
49
55
 
56
+ COLUMN_NAME = /
57
+ \A
58
+ (
59
+ (?:
60
+ # "table_name"."column_name" | function(one or no argument)
61
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
62
+ )
63
+ (?:\s+AS\s+(?:\w+|"\w+"))?
64
+ )
65
+ (?:\s*,\s*\g<1>)*
66
+ \z
67
+ /ix
68
+
69
+ COLUMN_NAME_WITH_ORDER = /
70
+ \A
71
+ (
72
+ (?:
73
+ # "table_name"."column_name" | function(one or no argument)
74
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
75
+ )
76
+ (?:\s+ASC|\s+DESC)?
77
+ )
78
+ (?:\s*,\s*\g<1>)*
79
+ \z
80
+ /ix
81
+
82
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
83
+
84
+ private
50
85
  def _type_cast(value)
51
86
  case value
52
87
  when BigDecimal
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # See https://www.sqlite.org/fileformat2.html#intschema
12
12
  next if row["name"].starts_with?("sqlite_")
13
13
 
14
- index_sql = query_value(<<-SQL, "SCHEMA")
14
+ index_sql = query_value(<<~SQL, "SCHEMA")
15
15
  SELECT sql
16
16
  FROM sqlite_master
17
17
  WHERE name = #{quote(row['name'])} AND type = 'index'
@@ -21,19 +21,24 @@ module ActiveRecord
21
21
  WHERE name = #{quote(row['name'])} AND type = 'index'
22
22
  SQL
23
23
 
24
- /\sWHERE\s+(?<where>.+)$/i =~ index_sql
24
+ /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\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"]
28
28
  end
29
29
 
30
- # Add info on sort order for columns (only desc order is explicitly specified, asc is
31
- # the default)
32
30
  orders = {}
33
- if index_sql # index_sql can be null in case of primary key indexes
34
- index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
35
- orders[order_column] = :desc
36
- }
31
+
32
+ if columns.any?(&:nil?) # index created with an expression
33
+ columns = expressions
34
+ else
35
+ # Add info on sort order for columns (only desc order is explicitly specified,
36
+ # asc is the default)
37
+ if index_sql # index_sql can be null in case of primary key indexes
38
+ index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
39
+ orders[order_column] = :desc
40
+ }
41
+ end
37
42
  end
38
43
 
39
44
  IndexDefinition.new(
@@ -47,6 +52,32 @@ module ActiveRecord
47
52
  end.compact
48
53
  end
49
54
 
55
+ def add_foreign_key(from_table, to_table, **options)
56
+ alter_table(from_table) do |definition|
57
+ to_table = strip_table_name_prefix_and_suffix(to_table)
58
+ definition.foreign_key(to_table, **options)
59
+ end
60
+ end
61
+
62
+ def remove_foreign_key(from_table, to_table = nil, **options)
63
+ to_table ||= options[:to_table]
64
+ options = options.except(:name, :to_table)
65
+ foreign_keys = foreign_keys(from_table)
66
+
67
+ fkey = foreign_keys.detect do |fk|
68
+ table = to_table || begin
69
+ table = options[:column].to_s.delete_suffix("_id")
70
+ Base.pluralize_table_names ? table.pluralize : table
71
+ end
72
+ table = strip_table_name_prefix_and_suffix(table)
73
+ fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
74
+ fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
75
+ end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
76
+
77
+ foreign_keys.delete(fkey)
78
+ alter_table(from_table, foreign_keys)
79
+ end
80
+
50
81
  def create_schema_dumper(options)
51
82
  SQLite3::SchemaDumper.create(self, options)
52
83
  end
@@ -56,8 +87,8 @@ module ActiveRecord
56
87
  SQLite3::SchemaCreation.new(self)
57
88
  end
58
89
 
59
- def create_table_definition(*args)
60
- SQLite3::TableDefinition.new(*args)
90
+ def create_table_definition(*args, **options)
91
+ SQLite3::TableDefinition.new(self, *args, **options)
61
92
  end
62
93
 
63
94
  def new_column_from_field(table_name, field)
@@ -74,14 +105,14 @@ module ActiveRecord
74
105
  end
75
106
 
76
107
  type_metadata = fetch_type_metadata(field["type"])
77
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
78
109
  end
79
110
 
80
111
  def data_source_sql(name = nil, type: nil)
81
112
  scope = quoted_scope(name, type: type)
82
113
  scope[:type] ||= "'table','view'"
83
114
 
84
- sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'".dup
115
+ sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
85
116
  sql << " AND name = #{scope[:name]}" if scope[:name]
86
117
  sql << " AND type IN (#{scope[:type]})"
87
118
  sql
@@ -4,17 +4,20 @@ require "active_record/connection_adapters/abstract_adapter"
4
4
  require "active_record/connection_adapters/statement_pool"
5
5
  require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
6
6
  require "active_record/connection_adapters/sqlite3/quoting"
7
+ require "active_record/connection_adapters/sqlite3/database_statements"
7
8
  require "active_record/connection_adapters/sqlite3/schema_creation"
8
9
  require "active_record/connection_adapters/sqlite3/schema_definitions"
9
10
  require "active_record/connection_adapters/sqlite3/schema_dumper"
10
11
  require "active_record/connection_adapters/sqlite3/schema_statements"
11
12
 
12
- gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
+ gem "sqlite3", "~> 1.4"
13
14
  require "sqlite3"
14
15
 
15
16
  module ActiveRecord
16
17
  module ConnectionHandling # :nodoc:
17
18
  def sqlite3_connection(config)
19
+ config = config.symbolize_keys
20
+
18
21
  # Require database.
19
22
  unless config[:database]
20
23
  raise ArgumentError, "No database file specified. Missing argument: database"
@@ -31,11 +34,9 @@ module ActiveRecord
31
34
 
32
35
  db = SQLite3::Database.new(
33
36
  config[:database].to_s,
34
- results_as_hash: true
37
+ config.merge(results_as_hash: true)
35
38
  )
36
39
 
37
- db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
38
-
39
40
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
40
41
  rescue Errno::ENOENT => error
41
42
  if error.message.include?("No such file or directory")
@@ -54,10 +55,11 @@ module ActiveRecord
54
55
  #
55
56
  # * <tt>:database</tt> - Path to the database file.
56
57
  class SQLite3Adapter < AbstractAdapter
57
- ADAPTER_NAME = "SQLite".freeze
58
+ ADAPTER_NAME = "SQLite"
58
59
 
59
60
  include SQLite3::Quoting
60
61
  include SQLite3::SchemaStatements
62
+ include SQLite3::DatabaseStatements
61
63
 
62
64
  NATIVE_DATABASE_TYPES = {
63
65
  primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
@@ -74,39 +76,38 @@ module ActiveRecord
74
76
  json: { name: "json" },
75
77
  }
76
78
 
77
- ##
78
- # :singleton-method:
79
- # Indicates whether boolean values are stored in sqlite3 databases as 1
80
- # and 0 or 't' and 'f'. Leaving <tt>ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer</tt>
81
- # set to false is deprecated. SQLite databases have used 't' and 'f' to
82
- # serialize boolean values and must have old data converted to 1 and 0
83
- # (its native boolean serialization) before setting this flag to true.
84
- # Conversion can be accomplished by setting up a rake task which runs
85
- #
86
- # ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
87
- # ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0)
88
- # for all models and all boolean columns, after which the flag must be set
89
- # to true by adding the following to your <tt>application.rb</tt> file:
90
- #
91
- # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
92
- class_attribute :represent_boolean_as_integer, default: false
79
+ def self.represent_boolean_as_integer=(value) # :nodoc:
80
+ if value == false
81
+ raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
82
+ end
83
+
84
+ ActiveSupport::Deprecation.warn(
85
+ "`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
86
+ )
87
+ end
93
88
 
94
89
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
95
90
  private
96
91
  def dealloc(stmt)
97
- stmt[:stmt].close unless stmt[:stmt].closed?
92
+ stmt.close unless stmt.closed?
98
93
  end
99
94
  end
100
95
 
101
96
  def initialize(connection, logger, connection_options, config)
102
97
  super(connection, logger, config)
103
-
104
- @active = true
105
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
106
-
107
98
  configure_connection
108
99
  end
109
100
 
101
+ def self.database_exists?(config)
102
+ config = config.symbolize_keys
103
+ if config[:database] == ":memory:"
104
+ true
105
+ else
106
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
107
+ File.exist?(database_file)
108
+ end
109
+ end
110
+
110
111
  def supports_ddl_transactions?
111
112
  true
112
113
  end
@@ -116,15 +117,19 @@ module ActiveRecord
116
117
  end
117
118
 
118
119
  def supports_partial_index?
119
- sqlite_version >= "3.8.0"
120
+ true
121
+ end
122
+
123
+ def supports_expression_index?
124
+ database_version >= "3.9.0"
120
125
  end
121
126
 
122
127
  def requires_reloading?
123
128
  true
124
129
  end
125
130
 
126
- def supports_foreign_keys_in_create?
127
- sqlite_version >= "3.6.19"
131
+ def supports_foreign_keys?
132
+ true
128
133
  end
129
134
 
130
135
  def supports_views?
@@ -139,27 +144,33 @@ module ActiveRecord
139
144
  true
140
145
  end
141
146
 
142
- def supports_multi_insert?
143
- sqlite_version >= "3.7.11"
147
+ def supports_common_table_expressions?
148
+ database_version >= "3.8.3"
149
+ end
150
+
151
+ def supports_insert_on_conflict?
152
+ database_version >= "3.24.0"
144
153
  end
154
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
155
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
156
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
145
157
 
146
158
  def active?
147
- @active
159
+ !@connection.closed?
160
+ end
161
+
162
+ def reconnect!
163
+ super
164
+ connect if @connection.closed?
148
165
  end
149
166
 
150
167
  # Disconnects from the database if already connected. Otherwise, this
151
168
  # method does nothing.
152
169
  def disconnect!
153
170
  super
154
- @active = false
155
171
  @connection.close rescue nil
156
172
  end
157
173
 
158
- # Clears the prepared statements cache.
159
- def clear_cache!
160
- @statements.clear
161
- end
162
-
163
174
  def supports_index_sort_order?
164
175
  true
165
176
  end
@@ -184,91 +195,34 @@ module ActiveRecord
184
195
  true
185
196
  end
186
197
 
198
+ def supports_lazy_transactions?
199
+ true
200
+ end
201
+
187
202
  # REFERENTIAL INTEGRITY ====================================
188
203
 
189
204
  def disable_referential_integrity # :nodoc:
190
- old = query_value("PRAGMA foreign_keys")
205
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
206
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
191
207
 
192
208
  begin
209
+ execute("PRAGMA defer_foreign_keys = ON")
193
210
  execute("PRAGMA foreign_keys = OFF")
194
211
  yield
195
212
  ensure
196
- execute("PRAGMA foreign_keys = #{old}")
213
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
214
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
197
215
  end
198
216
  end
199
217
 
200
218
  #--
201
219
  # DATABASE STATEMENTS ======================================
202
220
  #++
203
-
204
221
  def explain(arel, binds = [])
205
222
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
206
223
  SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
207
224
  end
208
225
 
209
- def exec_query(sql, name = nil, binds = [], prepare: false)
210
- type_casted_binds = type_casted_binds(binds)
211
-
212
- log(sql, name, binds, type_casted_binds) do
213
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
214
- # Don't cache statements if they are not prepared
215
- unless prepare
216
- stmt = @connection.prepare(sql)
217
- begin
218
- cols = stmt.columns
219
- unless without_prepared_statement?(binds)
220
- stmt.bind_params(type_casted_binds)
221
- end
222
- records = stmt.to_a
223
- ensure
224
- stmt.close
225
- end
226
- else
227
- cache = @statements[sql] ||= {
228
- stmt: @connection.prepare(sql)
229
- }
230
- stmt = cache[:stmt]
231
- cols = cache[:cols] ||= stmt.columns
232
- stmt.reset!
233
- stmt.bind_params(type_casted_binds)
234
- records = stmt.to_a
235
- end
236
-
237
- ActiveRecord::Result.new(cols, records)
238
- end
239
- end
240
- end
241
-
242
- def exec_delete(sql, name = "SQL", binds = [])
243
- exec_query(sql, name, binds)
244
- @connection.changes
245
- end
246
- alias :exec_update :exec_delete
247
-
248
- def last_inserted_id(result)
249
- @connection.last_insert_row_id
250
- end
251
-
252
- def execute(sql, name = nil) #:nodoc:
253
- log(sql, name) do
254
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
255
- @connection.execute(sql)
256
- end
257
- end
258
- end
259
-
260
- def begin_db_transaction #:nodoc:
261
- log("begin transaction", nil) { @connection.transaction }
262
- end
263
-
264
- def commit_db_transaction #:nodoc:
265
- log("commit transaction", nil) { @connection.commit }
266
- end
267
-
268
- def exec_rollback_db_transaction #:nodoc:
269
- log("rollback transaction", nil) { @connection.rollback }
270
- end
271
-
272
226
  # SCHEMA STATEMENTS ========================================
273
227
 
274
228
  def primary_keys(table_name) # :nodoc:
@@ -290,24 +244,22 @@ module ActiveRecord
290
244
  rename_table_indexes(table_name, new_name)
291
245
  end
292
246
 
293
- def valid_alter_table_type?(type, options = {})
294
- !invalid_alter_table_type?(type, options)
295
- end
296
- deprecate :valid_alter_table_type?
297
-
298
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
247
+ def add_column(table_name, column_name, type, **options) #:nodoc:
299
248
  if invalid_alter_table_type?(type, options)
300
249
  alter_table(table_name) do |definition|
301
- definition.column(column_name, type, options)
250
+ definition.column(column_name, type, **options)
302
251
  end
303
252
  else
304
253
  super
305
254
  end
306
255
  end
307
256
 
308
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
257
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
309
258
  alter_table(table_name) do |definition|
310
259
  definition.remove_column column_name
260
+ definition.foreign_keys.delete_if do |_, fk_options|
261
+ fk_options[:column] == column_name.to_s
262
+ end
311
263
  end
312
264
  end
313
265
 
@@ -366,27 +318,36 @@ module ActiveRecord
366
318
  end
367
319
  end
368
320
 
369
- def insert_fixtures(rows, table_name)
370
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
371
- `insert_fixtures` is deprecated and will be removed in the next version of Rails.
372
- Consider using `insert_fixtures_set` for performance improvement.
373
- MSG
374
- insert_fixtures_set(table_name => rows)
321
+ def build_insert_sql(insert) # :nodoc:
322
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
323
+
324
+ if insert.skip_duplicates?
325
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
326
+ elsif insert.update_duplicates?
327
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
328
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
329
+ end
330
+
331
+ sql
375
332
  end
376
333
 
377
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
378
- disable_referential_integrity do
379
- transaction(requires_new: true) do
380
- tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
334
+ def get_database_version # :nodoc:
335
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
336
+ end
381
337
 
382
- fixture_set.each do |table_name, rows|
383
- rows.each { |row| insert_fixture(row, table_name) }
384
- end
385
- end
338
+ def check_version # :nodoc:
339
+ if database_version < "3.8.0"
340
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
386
341
  end
387
342
  end
388
343
 
389
344
  private
345
+ # See https://www.sqlite.org/limits.html,
346
+ # the default value is 999 when not configured.
347
+ def bind_params_length
348
+ 999
349
+ end
350
+
390
351
  def initialize_type_map(m = type_map)
391
352
  super
392
353
  register_class_with_limit m, %r(int)i, SQLite3Integer
@@ -402,17 +363,31 @@ module ActiveRecord
402
363
  # See: https://www.sqlite.org/lang_altertable.html
403
364
  # SQLite has an additional restriction on the ALTER TABLE statement
404
365
  def invalid_alter_table_type?(type, options)
405
- type.to_sym == :primary_key || options[:primary_key]
366
+ type.to_sym == :primary_key || options[:primary_key] ||
367
+ options[:null] == false && options[:default].nil?
406
368
  end
407
369
 
408
- def alter_table(table_name, options = {})
370
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
409
371
  altered_table_name = "a#{table_name}"
410
- caller = lambda { |definition| yield definition if block_given? }
372
+
373
+ caller = lambda do |definition|
374
+ rename = options[:rename] || {}
375
+ foreign_keys.each do |fk|
376
+ if column = rename[fk.options[:column]]
377
+ fk.options[:column] = column
378
+ end
379
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
380
+ definition.foreign_key(to_table, **fk.options)
381
+ end
382
+
383
+ yield definition if block_given?
384
+ end
411
385
 
412
386
  transaction do
413
- move_table(table_name, altered_table_name,
414
- options.merge(temporary: true))
415
- move_table(altered_table_name, table_name, &caller)
387
+ disable_referential_integrity do
388
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
389
+ move_table(altered_table_name, table_name, &caller)
390
+ end
416
391
  end
417
392
  end
418
393
 
@@ -424,7 +399,7 @@ module ActiveRecord
424
399
  def copy_table(from, to, options = {})
425
400
  from_primary_key = primary_key(from)
426
401
  options[:id] = false
427
- create_table(to, options) do |definition|
402
+ create_table(to, **options) do |definition|
428
403
  @definition = definition
429
404
  if from_primary_key.is_a?(Array)
430
405
  @definition.primary_keys from_primary_key
@@ -442,6 +417,7 @@ module ActiveRecord
442
417
  primary_key: column_name == from_primary_key
443
418
  )
444
419
  end
420
+
445
421
  yield @definition if block_given?
446
422
  end
447
423
  copy_table_indexes(from, to, options[:rename] || {})
@@ -459,9 +435,12 @@ module ActiveRecord
459
435
  name = name[1..-1]
460
436
  end
461
437
 
462
- to_column_names = columns(to).map(&:name)
463
- columns = index.columns.map { |c| rename[c] || c }.select do |column|
464
- to_column_names.include?(column)
438
+ columns = index.columns
439
+ if columns.is_a?(Array)
440
+ to_column_names = columns(to).map(&:name)
441
+ columns = columns.map { |c| rename[c] || c }.select do |column|
442
+ to_column_names.include?(column)
443
+ end
465
444
  end
466
445
 
467
446
  unless columns.empty?
@@ -487,22 +466,18 @@ module ActiveRecord
487
466
  SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
488
467
  end
489
468
 
490
- def sqlite_version
491
- @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
492
- end
493
-
494
- def translate_exception(exception, message)
469
+ def translate_exception(exception, message:, sql:, binds:)
495
470
  case exception.message
496
471
  # SQLite 3.8.2 returns a newly formatted error message:
497
472
  # UNIQUE constraint failed: *table_name*.*column_name*
498
473
  # Older versions of SQLite return:
499
474
  # column *column_name* is not unique
500
475
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
501
- RecordNotUnique.new(message)
476
+ RecordNotUnique.new(message, sql: sql, binds: binds)
502
477
  when /.* may not be NULL/, /NOT NULL constraint failed: .*/
503
- NotNullViolation.new(message)
478
+ NotNullViolation.new(message, sql: sql, binds: binds)
504
479
  when /FOREIGN KEY constraint failed/i
505
- InvalidForeignKey.new(message)
480
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
506
481
  else
507
482
  super
508
483
  end
@@ -512,7 +487,7 @@ module ActiveRecord
512
487
 
513
488
  def table_structure_with_collation(table_name, basic_structure)
514
489
  collation_hash = {}
515
- sql = <<-SQL
490
+ sql = <<~SQL
516
491
  SELECT sql FROM
517
492
  (SELECT * FROM sqlite_master UNION ALL
518
493
  SELECT * FROM sqlite_temp_master)
@@ -545,7 +520,7 @@ module ActiveRecord
545
520
  column
546
521
  end
547
522
  else
548
- basic_structure.to_hash
523
+ basic_structure.to_a
549
524
  end
550
525
  end
551
526
 
@@ -553,7 +528,21 @@ module ActiveRecord
553
528
  Arel::Visitors::SQLite.new(self)
554
529
  end
555
530
 
531
+ def build_statement_pool
532
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
533
+ end
534
+
535
+ def connect
536
+ @connection = ::SQLite3::Database.new(
537
+ @config[:database].to_s,
538
+ @config.merge(results_as_hash: true)
539
+ )
540
+ configure_connection
541
+ end
542
+
556
543
  def configure_connection
544
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
545
+
557
546
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
558
547
  end
559
548