activerecord 5.0.7.2 → 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 (359) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +708 -2040
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +9 -7
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -22
  8. data/lib/active_record/advisory_lock_base.rb +18 -0
  9. data/lib/active_record/aggregations.rb +249 -247
  10. data/lib/active_record/association_relation.rb +18 -14
  11. data/lib/active_record/associations.rb +1603 -1592
  12. data/lib/active_record/associations/alias_tracker.rb +24 -34
  13. data/lib/active_record/associations/association.rb +114 -55
  14. data/lib/active_record/associations/association_scope.rb +94 -94
  15. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  16. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  17. data/lib/active_record/associations/builder/association.rb +18 -25
  18. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  19. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  20. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
  21. data/lib/active_record/associations/builder/has_many.rb +4 -0
  22. data/lib/active_record/associations/builder/has_one.rb +37 -1
  23. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  24. data/lib/active_record/associations/collection_association.rb +86 -254
  25. data/lib/active_record/associations/collection_proxy.rb +158 -122
  26. data/lib/active_record/associations/foreign_association.rb +9 -0
  27. data/lib/active_record/associations/has_many_association.rb +23 -30
  28. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  29. data/lib/active_record/associations/has_one_association.rb +59 -54
  30. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  31. data/lib/active_record/associations/join_dependency.rb +143 -176
  32. data/lib/active_record/associations/join_dependency/join_association.rb +38 -87
  33. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  34. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  35. data/lib/active_record/associations/preloader.rb +90 -103
  36. data/lib/active_record/associations/preloader/association.rb +86 -100
  37. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  38. data/lib/active_record/associations/singular_association.rb +12 -45
  39. data/lib/active_record/associations/through_association.rb +26 -14
  40. data/lib/active_record/attribute_assignment.rb +54 -61
  41. data/lib/active_record/attribute_decorators.rb +38 -17
  42. data/lib/active_record/attribute_methods.rb +66 -106
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
  44. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  45. data/lib/active_record/attribute_methods/primary_key.rb +85 -92
  46. data/lib/active_record/attribute_methods/query.rb +4 -3
  47. data/lib/active_record/attribute_methods/read.rb +20 -49
  48. data/lib/active_record/attribute_methods/serialization.rb +29 -7
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
  50. data/lib/active_record/attribute_methods/write.rb +34 -33
  51. data/lib/active_record/attributes.rb +38 -25
  52. data/lib/active_record/autosave_association.rb +54 -35
  53. data/lib/active_record/base.rb +27 -24
  54. data/lib/active_record/callbacks.rb +64 -35
  55. data/lib/active_record/coders/json.rb +2 -0
  56. data/lib/active_record/coders/yaml_column.rb +11 -12
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +552 -323
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +228 -147
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -202
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +396 -562
  70. data/lib/active_record/connection_adapters/column.rb +41 -13
  71. data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  73. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  101. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -31
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
  118. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  120. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  121. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  122. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  123. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  124. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  127. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +259 -266
  128. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  129. data/lib/active_record/connection_handling.rb +143 -40
  130. data/lib/active_record/core.rb +201 -163
  131. data/lib/active_record/counter_cache.rb +60 -28
  132. data/lib/active_record/database_configurations.rb +233 -0
  133. data/lib/active_record/database_configurations/database_config.rb +37 -0
  134. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  135. data/lib/active_record/database_configurations/url_config.rb +78 -0
  136. data/lib/active_record/define_callbacks.rb +22 -0
  137. data/lib/active_record/dynamic_matchers.rb +87 -87
  138. data/lib/active_record/enum.rb +60 -23
  139. data/lib/active_record/errors.rb +114 -18
  140. data/lib/active_record/explain.rb +4 -4
  141. data/lib/active_record/explain_registry.rb +3 -1
  142. data/lib/active_record/explain_subscriber.rb +9 -4
  143. data/lib/active_record/fixture_set/file.rb +13 -8
  144. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  145. data/lib/active_record/fixture_set/render_context.rb +17 -0
  146. data/lib/active_record/fixture_set/table_row.rb +152 -0
  147. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  148. data/lib/active_record/fixtures.rb +194 -504
  149. data/lib/active_record/gem_version.rb +5 -3
  150. data/lib/active_record/inheritance.rb +150 -99
  151. data/lib/active_record/insert_all.rb +179 -0
  152. data/lib/active_record/integration.rb +116 -25
  153. data/lib/active_record/internal_metadata.rb +16 -19
  154. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  155. data/lib/active_record/locking/optimistic.rb +77 -87
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +48 -29
  158. data/lib/active_record/middleware/database_selector.rb +74 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +369 -302
  162. data/lib/active_record/migration/command_recorder.rb +134 -100
  163. data/lib/active_record/migration/compatibility.rb +174 -56
  164. data/lib/active_record/migration/join_table.rb +8 -7
  165. data/lib/active_record/model_schema.rb +131 -127
  166. data/lib/active_record/nested_attributes.rb +213 -202
  167. data/lib/active_record/no_touching.rb +12 -3
  168. data/lib/active_record/null_relation.rb +12 -34
  169. data/lib/active_record/persistence.rb +446 -77
  170. data/lib/active_record/query_cache.rb +13 -12
  171. data/lib/active_record/querying.rb +37 -24
  172. data/lib/active_record/railtie.rb +128 -36
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +312 -177
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +214 -252
  179. data/lib/active_record/relation.rb +440 -318
  180. data/lib/active_record/relation/batches.rb +98 -52
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  182. data/lib/active_record/relation/calculations.rb +212 -173
  183. data/lib/active_record/relation/delegation.rb +72 -69
  184. data/lib/active_record/relation/finder_methods.rb +207 -247
  185. data/lib/active_record/relation/from_clause.rb +6 -8
  186. data/lib/active_record/relation/merger.rb +78 -62
  187. data/lib/active_record/relation/predicate_builder.rb +83 -105
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  195. data/lib/active_record/relation/query_attribute.rb +33 -2
  196. data/lib/active_record/relation/query_methods.rb +476 -334
  197. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  198. data/lib/active_record/relation/spawn_methods.rb +8 -8
  199. data/lib/active_record/relation/where_clause.rb +111 -96
  200. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  201. data/lib/active_record/result.rb +69 -40
  202. data/lib/active_record/runtime_registry.rb +5 -3
  203. data/lib/active_record/sanitization.rb +83 -99
  204. data/lib/active_record/schema.rb +7 -14
  205. data/lib/active_record/schema_dumper.rb +71 -69
  206. data/lib/active_record/schema_migration.rb +16 -6
  207. data/lib/active_record/scoping.rb +20 -20
  208. data/lib/active_record/scoping/default.rb +92 -95
  209. data/lib/active_record/scoping/named.rb +47 -27
  210. data/lib/active_record/secure_token.rb +4 -2
  211. data/lib/active_record/serialization.rb +2 -0
  212. data/lib/active_record/statement_cache.rb +63 -28
  213. data/lib/active_record/store.rb +121 -41
  214. data/lib/active_record/suppressor.rb +6 -3
  215. data/lib/active_record/table_metadata.rb +39 -18
  216. data/lib/active_record/tasks/database_tasks.rb +271 -81
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +70 -36
  223. data/lib/active_record/touch_later.rb +8 -6
  224. data/lib/active_record/transactions.rb +141 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +23 -18
  227. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  228. data/lib/active_record/type/date.rb +2 -0
  229. data/lib/active_record/type/date_time.rb +2 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  232. data/lib/active_record/type/internal/timezone.rb +2 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +16 -9
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +2 -1
  237. data/lib/active_record/type/type_map.rb +14 -17
  238. data/lib/active_record/type/unsigned_integer.rb +16 -0
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/type_caster/connection.rb +17 -12
  241. data/lib/active_record/type_caster/map.rb +5 -4
  242. data/lib/active_record/validations.rb +7 -5
  243. data/lib/active_record/validations/absence.rb +2 -0
  244. data/lib/active_record/validations/associated.rb +4 -3
  245. data/lib/active_record/validations/length.rb +2 -0
  246. data/lib/active_record/validations/presence.rb +4 -2
  247. data/lib/active_record/validations/uniqueness.rb +29 -42
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +62 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +203 -0
  317. data/lib/arel/visitors/dot.rb +296 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +156 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +158 -0
  323. data/lib/arel/visitors/oracle12.rb +65 -0
  324. data/lib/arel/visitors/postgresql.rb +109 -0
  325. data/lib/arel/visitors/sqlite.rb +38 -0
  326. data/lib/arel/visitors/to_sql.rb +888 -0
  327. data/lib/arel/visitors/visitor.rb +45 -0
  328. data/lib/arel/visitors/where_sql.rb +22 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  333. data/lib/rails/generators/active_record/migration.rb +17 -3
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  335. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
  338. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +137 -52
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  355. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  356. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  357. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  358. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  359. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+ def default_primary_key?(column)
9
+ schema_type(column) == :integer
10
+ end
11
+
12
+ def explicit_primary_key_default?(column)
13
+ column.bigint?
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module SchemaStatements # :nodoc:
7
+ # Returns an array of indexes for the given table.
8
+ def indexes(table_name)
9
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
10
+ # Indexes SQLite creates implicitly for internal use start with "sqlite_".
11
+ # See https://www.sqlite.org/fileformat2.html#intschema
12
+ next if row["name"].starts_with?("sqlite_")
13
+
14
+ index_sql = query_value(<<~SQL, "SCHEMA")
15
+ SELECT sql
16
+ FROM sqlite_master
17
+ WHERE name = #{quote(row['name'])} AND type = 'index'
18
+ UNION ALL
19
+ SELECT sql
20
+ FROM sqlite_temp_master
21
+ WHERE name = #{quote(row['name'])} AND type = 'index'
22
+ SQL
23
+
24
+ /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
25
+
26
+ columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
+ col["name"]
28
+ end
29
+
30
+ orders = {}
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
42
+ end
43
+
44
+ IndexDefinition.new(
45
+ table_name,
46
+ row["name"],
47
+ row["unique"] != 0,
48
+ columns,
49
+ where: where,
50
+ orders: orders
51
+ )
52
+ end.compact
53
+ end
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
+
81
+ def create_schema_dumper(options)
82
+ SQLite3::SchemaDumper.create(self, options)
83
+ end
84
+
85
+ private
86
+ def schema_creation
87
+ SQLite3::SchemaCreation.new(self)
88
+ end
89
+
90
+ def create_table_definition(*args, **options)
91
+ SQLite3::TableDefinition.new(self, *args, **options)
92
+ end
93
+
94
+ def new_column_from_field(table_name, field)
95
+ default = \
96
+ case field["dflt_value"]
97
+ when /^null$/i
98
+ nil
99
+ when /^'(.*)'$/m
100
+ $1.gsub("''", "'")
101
+ when /^"(.*)"$/m
102
+ $1.gsub('""', '"')
103
+ else
104
+ field["dflt_value"]
105
+ end
106
+
107
+ type_metadata = fetch_type_metadata(field["type"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
109
+ end
110
+
111
+ def data_source_sql(name = nil, type: nil)
112
+ scope = quoted_scope(name, type: type)
113
+ scope[:type] ||= "'table','view'"
114
+
115
+ sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
116
+ sql << " AND name = #{scope[:name]}" if scope[:name]
117
+ sql << " AND type IN (#{scope[:type]})"
118
+ sql
119
+ end
120
+
121
+ def quoted_scope(name = nil, type: nil)
122
+ type = \
123
+ case type
124
+ when "BASE TABLE"
125
+ "'table'"
126
+ when "VIEW"
127
+ "'view'"
128
+ end
129
+ scope = {}
130
+ scope[:name] = quote(name) if name
131
+ scope[:type] = type if type
132
+ scope
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -1,15 +1,23 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
4
- require 'active_record/connection_adapters/sqlite3/quoting'
5
- require 'active_record/connection_adapters/sqlite3/schema_creation'
1
+ # frozen_string_literal: true
6
2
 
7
- gem 'sqlite3', '~> 1.3.6'
8
- require 'sqlite3'
3
+ require "active_record/connection_adapters/abstract_adapter"
4
+ require "active_record/connection_adapters/statement_pool"
5
+ require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
6
+ require "active_record/connection_adapters/sqlite3/quoting"
7
+ require "active_record/connection_adapters/sqlite3/database_statements"
8
+ require "active_record/connection_adapters/sqlite3/schema_creation"
9
+ require "active_record/connection_adapters/sqlite3/schema_definitions"
10
+ require "active_record/connection_adapters/sqlite3/schema_dumper"
11
+ require "active_record/connection_adapters/sqlite3/schema_statements"
12
+
13
+ gem "sqlite3", "~> 1.4"
14
+ require "sqlite3"
9
15
 
10
16
  module ActiveRecord
11
17
  module ConnectionHandling # :nodoc:
12
18
  def sqlite3_connection(config)
19
+ config = config.symbolize_keys
20
+
13
21
  # Require database.
14
22
  unless config[:database]
15
23
  raise ArgumentError, "No database file specified. Missing argument: database"
@@ -18,7 +26,7 @@ module ActiveRecord
18
26
  # Allow database path relative to Rails.root, but only if the database
19
27
  # path is not the special path that tells sqlite to build a database only
20
28
  # in memory.
21
- if ':memory:' != config[:database]
29
+ if ":memory:" != config[:database]
22
30
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
23
31
  dirname = File.dirname(config[:database])
24
32
  Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -26,11 +34,9 @@ module ActiveRecord
26
34
 
27
35
  db = SQLite3::Database.new(
28
36
  config[:database].to_s,
29
- :results_as_hash => true
37
+ config.merge(results_as_hash: true)
30
38
  )
31
39
 
32
- db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
33
-
34
40
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
35
41
  rescue Errno::ENOENT => error
36
42
  if error.message.include?("No such file or directory")
@@ -49,12 +55,14 @@ module ActiveRecord
49
55
  #
50
56
  # * <tt>:database</tt> - Path to the database file.
51
57
  class SQLite3Adapter < AbstractAdapter
52
- ADAPTER_NAME = 'SQLite'.freeze
58
+ ADAPTER_NAME = "SQLite"
53
59
 
54
60
  include SQLite3::Quoting
61
+ include SQLite3::SchemaStatements
62
+ include SQLite3::DatabaseStatements
55
63
 
56
64
  NATIVE_DATABASE_TYPES = {
57
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
65
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
58
66
  string: { name: "varchar" },
59
67
  text: { name: "text" },
60
68
  integer: { name: "integer" },
@@ -64,30 +72,40 @@ module ActiveRecord
64
72
  time: { name: "time" },
65
73
  date: { name: "date" },
66
74
  binary: { name: "blob" },
67
- boolean: { name: "boolean" }
75
+ boolean: { name: "boolean" },
76
+ json: { name: "json" },
68
77
  }
69
78
 
70
- class StatementPool < ConnectionAdapters::StatementPool
71
- private
72
-
73
- def dealloc(stmt)
74
- stmt[:stmt].close unless stmt[:stmt].closed?
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."
75
82
  end
76
- end
77
83
 
78
- def schema_creation # :nodoc:
79
- SQLite3::SchemaCreation.new self
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
+ )
80
87
  end
81
88
 
82
- def arel_visitor # :nodoc:
83
- Arel::Visitors::SQLite.new(self)
89
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
90
+ private
91
+ def dealloc(stmt)
92
+ stmt.close unless stmt.closed?
93
+ end
84
94
  end
85
95
 
86
96
  def initialize(connection, logger, connection_options, config)
87
97
  super(connection, logger, config)
98
+ configure_connection
99
+ end
88
100
 
89
- @active = nil
90
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
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
91
109
  end
92
110
 
93
111
  def supports_ddl_transactions?
@@ -99,25 +117,18 @@ module ActiveRecord
99
117
  end
100
118
 
101
119
  def supports_partial_index?
102
- sqlite_version >= '3.8.0'
103
- end
104
-
105
- # Returns true, since this connection adapter supports prepared statement
106
- # caching.
107
- def supports_statement_cache?
108
120
  true
109
121
  end
110
122
 
111
- # Returns true, since this connection adapter supports migrations.
112
- def supports_migrations? #:nodoc:
113
- true
123
+ def supports_expression_index?
124
+ database_version >= "3.9.0"
114
125
  end
115
126
 
116
- def supports_primary_key? #:nodoc:
127
+ def requires_reloading?
117
128
  true
118
129
  end
119
130
 
120
- def requires_reloading?
131
+ def supports_foreign_keys?
121
132
  true
122
133
  end
123
134
 
@@ -129,37 +140,43 @@ module ActiveRecord
129
140
  true
130
141
  end
131
142
 
132
- def supports_multi_insert?
133
- sqlite_version >= '3.7.11'
143
+ def supports_json?
144
+ true
145
+ end
146
+
147
+ def supports_common_table_expressions?
148
+ database_version >= "3.8.3"
134
149
  end
135
150
 
151
+ def supports_insert_on_conflict?
152
+ database_version >= "3.24.0"
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?
157
+
136
158
  def active?
137
- @active != false
159
+ !@connection.closed?
160
+ end
161
+
162
+ def reconnect!
163
+ super
164
+ connect if @connection.closed?
138
165
  end
139
166
 
140
167
  # Disconnects from the database if already connected. Otherwise, this
141
168
  # method does nothing.
142
169
  def disconnect!
143
170
  super
144
- @active = false
145
171
  @connection.close rescue nil
146
172
  end
147
173
 
148
- # Clears the prepared statements cache.
149
- def clear_cache!
150
- @statements.clear
151
- end
152
-
153
174
  def supports_index_sort_order?
154
175
  true
155
176
  end
156
177
 
157
- def valid_type?(type)
158
- true
159
- end
160
-
161
178
  # Returns 62. SQLite supports index names up to 64
162
- # characters. The rest is used by rails internally to perform
179
+ # characters. The rest is used by Rails internally to perform
163
180
  # temporary rename operations
164
181
  def allowed_index_name_length
165
182
  index_name_length - 2
@@ -178,174 +195,39 @@ module ActiveRecord
178
195
  true
179
196
  end
180
197
 
181
- #--
182
- # DATABASE STATEMENTS ======================================
183
- #++
184
-
185
- def explain(arel, binds = [])
186
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
187
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
198
+ def supports_lazy_transactions?
199
+ true
188
200
  end
189
201
 
190
- def exec_query(sql, name = nil, binds = [], prepare: false)
191
- type_casted_binds = type_casted_binds(binds)
202
+ # REFERENTIAL INTEGRITY ====================================
192
203
 
193
- log(sql, name, binds, type_casted_binds) do
194
- # Don't cache statements if they are not prepared
195
- unless prepare
196
- stmt = @connection.prepare(sql)
197
- begin
198
- cols = stmt.columns
199
- unless without_prepared_statement?(binds)
200
- stmt.bind_params(type_casted_binds)
201
- end
202
- records = stmt.to_a
203
- ensure
204
- stmt.close
205
- end
206
- else
207
- cache = @statements[sql] ||= {
208
- :stmt => @connection.prepare(sql)
209
- }
210
- stmt = cache[:stmt]
211
- cols = cache[:cols] ||= stmt.columns
212
- stmt.reset!
213
- stmt.bind_params(type_casted_binds)
214
- records = stmt.to_a
215
- end
204
+ def disable_referential_integrity # :nodoc:
205
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
206
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
216
207
 
217
- ActiveRecord::Result.new(cols, records)
208
+ begin
209
+ execute("PRAGMA defer_foreign_keys = ON")
210
+ execute("PRAGMA foreign_keys = OFF")
211
+ yield
212
+ ensure
213
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
214
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
218
215
  end
219
216
  end
220
217
 
221
- def exec_delete(sql, name = 'SQL', binds = [])
222
- exec_query(sql, name, binds)
223
- @connection.changes
224
- end
225
- alias :exec_update :exec_delete
226
-
227
- def last_inserted_id(result)
228
- @connection.last_insert_row_id
229
- end
230
-
231
- def execute(sql, name = nil) #:nodoc:
232
- log(sql, name) { @connection.execute(sql) }
233
- end
234
-
235
- def begin_db_transaction #:nodoc:
236
- log('begin transaction',nil) { @connection.transaction }
237
- end
238
-
239
- def commit_db_transaction #:nodoc:
240
- log('commit transaction',nil) { @connection.commit }
241
- end
242
-
243
- def exec_rollback_db_transaction #:nodoc:
244
- log('rollback transaction',nil) { @connection.rollback }
218
+ #--
219
+ # DATABASE STATEMENTS ======================================
220
+ #++
221
+ def explain(arel, binds = [])
222
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
223
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
245
224
  end
246
225
 
247
226
  # SCHEMA STATEMENTS ========================================
248
227
 
249
- def tables(name = nil) # :nodoc:
250
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
251
- #tables currently returns both tables and views.
252
- This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
253
- Use #data_sources instead.
254
- MSG
255
-
256
- if name
257
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
258
- Passing arguments to #tables is deprecated without replacement.
259
- MSG
260
- end
261
-
262
- data_sources
263
- end
264
-
265
- def data_sources
266
- select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
267
- end
268
-
269
- def table_exists?(table_name)
270
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
271
- #table_exists? currently checks both tables and views.
272
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
273
- Use #data_source_exists? instead.
274
- MSG
275
-
276
- data_source_exists?(table_name)
277
- end
278
-
279
- def data_source_exists?(table_name)
280
- return false unless table_name.present?
281
-
282
- sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
283
- sql << " AND name = #{quote(table_name)}"
284
-
285
- select_values(sql, 'SCHEMA').any?
286
- end
287
-
288
- def views # :nodoc:
289
- select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
290
- end
291
-
292
- def view_exists?(view_name) # :nodoc:
293
- return false unless view_name.present?
294
-
295
- sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
296
- sql << " AND name = #{quote(view_name)}"
297
-
298
- select_values(sql, 'SCHEMA').any?
299
- end
300
-
301
- # Returns an array of +Column+ objects for the table specified by +table_name+.
302
- def columns(table_name) # :nodoc:
303
- table_name = table_name.to_s
304
- table_structure(table_name).map do |field|
305
- case field["dflt_value"]
306
- when /^null$/i
307
- field["dflt_value"] = nil
308
- when /^'(.*)'$/m
309
- field["dflt_value"] = $1.gsub("''", "'")
310
- when /^"(.*)"$/m
311
- field["dflt_value"] = $1.gsub('""', '"')
312
- end
313
-
314
- collation = field['collation']
315
- sql_type = field['type']
316
- type_metadata = fetch_type_metadata(sql_type)
317
- new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, table_name, nil, collation)
318
- end
319
- end
320
-
321
- # Returns an array of indexes for the given table.
322
- def indexes(table_name, name = nil) #:nodoc:
323
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
324
- sql = <<-SQL
325
- SELECT sql
326
- FROM sqlite_master
327
- WHERE name=#{quote(row['name'])} AND type='index'
328
- UNION ALL
329
- SELECT sql
330
- FROM sqlite_temp_master
331
- WHERE name=#{quote(row['name'])} AND type='index'
332
- SQL
333
- index_sql = exec_query(sql).first['sql']
334
- match = /\sWHERE\s+(.+)$/i.match(index_sql)
335
- where = match[1] if match
336
- IndexDefinition.new(
337
- table_name,
338
- row['name'],
339
- row['unique'] != 0,
340
- exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
341
- col['name']
342
- }, nil, nil, where)
343
- end
344
- end
345
-
346
228
  def primary_keys(table_name) # :nodoc:
347
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
348
- pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
229
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
230
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
349
231
  end
350
232
 
351
233
  def remove_index(table_name, options = {}) #:nodoc:
@@ -362,25 +244,22 @@ module ActiveRecord
362
244
  rename_table_indexes(table_name, new_name)
363
245
  end
364
246
 
365
- # See: http://www.sqlite.org/lang_altertable.html
366
- # SQLite has an additional restriction on the ALTER TABLE statement
367
- def valid_alter_table_type?(type)
368
- type.to_sym != :primary_key
369
- end
370
-
371
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
372
- if valid_alter_table_type?(type)
373
- super(table_name, column_name, type, options)
374
- else
247
+ def add_column(table_name, column_name, type, **options) #:nodoc:
248
+ if invalid_alter_table_type?(type, options)
375
249
  alter_table(table_name) do |definition|
376
- definition.column(column_name, type, options)
250
+ definition.column(column_name, type, **options)
377
251
  end
252
+ else
253
+ super
378
254
  end
379
255
  end
380
256
 
381
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
257
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
382
258
  alter_table(table_name) do |definition|
383
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
384
263
  end
385
264
  end
386
265
 
@@ -403,14 +282,13 @@ module ActiveRecord
403
282
 
404
283
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
405
284
  alter_table(table_name) do |definition|
406
- include_default = options_include_default?(options)
407
285
  definition[column_name].instance_eval do
408
286
  self.type = type
409
287
  self.limit = options[:limit] if options.include?(:limit)
410
- self.default = options[:default] if include_default
288
+ self.default = options[:default] if options.include?(:default)
411
289
  self.null = options[:null] if options.include?(:null)
412
290
  self.precision = options[:precision] if options.include?(:precision)
413
- self.scale = options[:scale] if options.include?(:scale)
291
+ self.scale = options[:scale] if options.include?(:scale)
414
292
  self.collation = options[:collation] if options.include?(:collation)
415
293
  end
416
294
  end
@@ -418,52 +296,128 @@ module ActiveRecord
418
296
 
419
297
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
420
298
  column = column_for(table_name, column_name)
421
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
299
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
422
300
  rename_column_indexes(table_name, column.name, new_column_name)
423
301
  end
424
302
 
425
- protected
303
+ def add_reference(table_name, ref_name, **options) # :nodoc:
304
+ super(table_name, ref_name, type: :integer, **options)
305
+ end
306
+ alias :add_belongs_to :add_reference
307
+
308
+ def foreign_keys(table_name)
309
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
310
+ fk_info.map do |row|
311
+ options = {
312
+ column: row["from"],
313
+ primary_key: row["to"],
314
+ on_delete: extract_foreign_key_action(row["on_delete"]),
315
+ on_update: extract_foreign_key_action(row["on_update"])
316
+ }
317
+ ForeignKeyDefinition.new(table_name, row["table"], options)
318
+ end
319
+ end
320
+
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
332
+ end
333
+
334
+ def get_database_version # :nodoc:
335
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
336
+ end
337
+
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."
341
+ end
342
+ end
343
+
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
+
351
+ def initialize_type_map(m = type_map)
352
+ super
353
+ register_class_with_limit m, %r(int)i, SQLite3Integer
354
+ end
426
355
 
427
356
  def table_structure(table_name)
428
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
357
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
429
358
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
430
359
  table_structure_with_collation(table_name, structure)
431
360
  end
361
+ alias column_definitions table_structure
432
362
 
433
- def alter_table(table_name, options = {}) #:nodoc:
363
+ # See: https://www.sqlite.org/lang_altertable.html
364
+ # SQLite has an additional restriction on the ALTER TABLE statement
365
+ def invalid_alter_table_type?(type, options)
366
+ type.to_sym == :primary_key || options[:primary_key] ||
367
+ options[:null] == false && options[:default].nil?
368
+ end
369
+
370
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
434
371
  altered_table_name = "a#{table_name}"
435
- 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
436
385
 
437
386
  transaction do
438
- move_table(table_name, altered_table_name,
439
- options.merge(:temporary => true))
440
- 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
441
391
  end
442
392
  end
443
393
 
444
- def move_table(from, to, options = {}, &block) #:nodoc:
394
+ def move_table(from, to, options = {}, &block)
445
395
  copy_table(from, to, options, &block)
446
396
  drop_table(from)
447
397
  end
448
398
 
449
- def copy_table(from, to, options = {}) #:nodoc:
399
+ def copy_table(from, to, options = {})
450
400
  from_primary_key = primary_key(from)
451
401
  options[:id] = false
452
- create_table(to, options) do |definition|
402
+ create_table(to, **options) do |definition|
453
403
  @definition = definition
454
- @definition.primary_key(from_primary_key) if from_primary_key.present?
404
+ if from_primary_key.is_a?(Array)
405
+ @definition.primary_keys from_primary_key
406
+ end
455
407
  columns(from).each do |column|
456
408
  column_name = options[:rename] ?
457
409
  (options[:rename][column.name] ||
458
410
  options[:rename][column.name.to_sym] ||
459
411
  column.name) : column.name
460
- next if column_name == from_primary_key
461
412
 
462
413
  @definition.column(column_name, column.type,
463
- :limit => column.limit, :default => column.default,
464
- :precision => column.precision, :scale => column.scale,
465
- :null => column.null, collation: column.collation)
414
+ limit: column.limit, default: column.default,
415
+ precision: column.precision, scale: column.scale,
416
+ null: column.null, collation: column.collation,
417
+ primary_key: column_name == from_primary_key
418
+ )
466
419
  end
420
+
467
421
  yield @definition if block_given?
468
422
  end
469
423
  copy_table_indexes(from, to, options[:rename] || {})
@@ -472,7 +426,7 @@ module ActiveRecord
472
426
  options[:rename] || {})
473
427
  end
474
428
 
475
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
429
+ def copy_table_indexes(from, to, rename = {})
476
430
  indexes(from).each do |index|
477
431
  name = index.name
478
432
  if to == "a#{from}"
@@ -481,89 +435,128 @@ module ActiveRecord
481
435
  name = name[1..-1]
482
436
  end
483
437
 
484
- to_column_names = columns(to).map(&:name)
485
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
486
- 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
487
444
  end
488
445
 
489
446
  unless columns.empty?
490
447
  # index name can't be the same
491
448
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
492
449
  opts[:unique] = true if index.unique
450
+ opts[:where] = index.where if index.where
493
451
  add_index(to, columns, opts)
494
452
  end
495
453
  end
496
454
  end
497
455
 
498
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
499
- column_mappings = Hash[columns.map {|name| [name, name]}]
456
+ def copy_table_contents(from, to, columns, rename = {})
457
+ column_mappings = Hash[columns.map { |name| [name, name] }]
500
458
  rename.each { |a| column_mappings[a.last] = a.first }
501
459
  from_columns = columns(from).collect(&:name)
502
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
460
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
503
461
  from_columns_to_copy = columns.map { |col| column_mappings[col] }
504
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
505
- quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
462
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
463
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
506
464
 
507
465
  exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
508
466
  SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
509
467
  end
510
468
 
511
- def sqlite_version
512
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
513
- end
514
-
515
- def translate_exception(exception, message)
469
+ def translate_exception(exception, message:, sql:, binds:)
516
470
  case exception.message
517
471
  # SQLite 3.8.2 returns a newly formatted error message:
518
472
  # UNIQUE constraint failed: *table_name*.*column_name*
519
473
  # Older versions of SQLite return:
520
474
  # column *column_name* is not unique
521
475
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
522
- RecordNotUnique.new(message)
476
+ RecordNotUnique.new(message, sql: sql, binds: binds)
477
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
478
+ NotNullViolation.new(message, sql: sql, binds: binds)
479
+ when /FOREIGN KEY constraint failed/i
480
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
523
481
  else
524
482
  super
525
483
  end
526
484
  end
527
485
 
528
- private
529
486
  COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
530
487
 
531
488
  def table_structure_with_collation(table_name, basic_structure)
532
489
  collation_hash = {}
533
- sql = "SELECT sql FROM
534
- (SELECT * FROM sqlite_master UNION ALL
535
- SELECT * FROM sqlite_temp_master)
536
- WHERE type='table' and name='#{ table_name }' \;"
490
+ sql = <<~SQL
491
+ SELECT sql FROM
492
+ (SELECT * FROM sqlite_master UNION ALL
493
+ SELECT * FROM sqlite_temp_master)
494
+ WHERE type = 'table' AND name = #{quote(table_name)}
495
+ SQL
537
496
 
538
497
  # Result will have following sample string
539
498
  # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
540
499
  # "password_digest" varchar COLLATE "NOCASE");
541
- result = exec_query(sql, 'SCHEMA').first
500
+ result = exec_query(sql, "SCHEMA").first
542
501
 
543
502
  if result
544
- # Splitting with left parentheses and picking up last will return all
503
+ # Splitting with left parentheses and discarding the first part will return all
545
504
  # columns separated with comma(,).
546
- columns_string = result["sql"].split('(').last
505
+ columns_string = result["sql"].split("(", 2).last
547
506
 
548
- columns_string.split(',').each do |column_string|
507
+ columns_string.split(",").each do |column_string|
549
508
  # This regex will match the column name and collation type and will save
550
509
  # the value in $1 and $2 respectively.
551
- collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
510
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
552
511
  end
553
512
 
554
513
  basic_structure.map! do |column|
555
- column_name = column['name']
514
+ column_name = column["name"]
556
515
 
557
516
  if collation_hash.has_key? column_name
558
- column['collation'] = collation_hash[column_name]
517
+ column["collation"] = collation_hash[column_name]
559
518
  end
560
519
 
561
520
  column
562
521
  end
563
522
  else
564
- basic_structure.to_hash
523
+ basic_structure.to_a
565
524
  end
566
525
  end
526
+
527
+ def arel_visitor
528
+ Arel::Visitors::SQLite.new(self)
529
+ end
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
+
543
+ def configure_connection
544
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
545
+
546
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
547
+ end
548
+
549
+ class SQLite3Integer < Type::Integer # :nodoc:
550
+ private
551
+ def _limit
552
+ # INTEGER storage class can be stored 8 bytes value.
553
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
554
+ limit || 8
555
+ end
556
+ end
557
+
558
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
567
559
  end
560
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
568
561
  end
569
562
  end