activerecord 4.2.9 → 6.1.4.1

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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  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 +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  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 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,14 +1,23 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'arel/visitors/bind_visitor'
1
+ # frozen_string_literal: true
4
2
 
5
- gem 'sqlite3', '~> 1.3.6'
6
- 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"
7
15
 
8
16
  module ActiveRecord
9
17
  module ConnectionHandling # :nodoc:
10
- # sqlite3 adapter reuses sqlite_connection.
11
18
  def sqlite3_connection(config)
19
+ config = config.symbolize_keys
20
+
12
21
  # Require database.
13
22
  unless config[:database]
14
23
  raise ArgumentError, "No database file specified. Missing argument: database"
@@ -17,7 +26,7 @@ module ActiveRecord
17
26
  # Allow database path relative to Rails.root, but only if the database
18
27
  # path is not the special path that tells sqlite to build a database only
19
28
  # in memory.
20
- if ':memory:' != config[:database]
29
+ if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:")
21
30
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
22
31
  dirname = File.dirname(config[:database])
23
32
  Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -25,15 +34,13 @@ module ActiveRecord
25
34
 
26
35
  db = SQLite3::Database.new(
27
36
  config[:database].to_s,
28
- :results_as_hash => true
37
+ config.merge(results_as_hash: true)
29
38
  )
30
39
 
31
- db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
32
-
33
40
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
34
41
  rescue Errno::ENOENT => error
35
42
  if error.message.include?("No such file or directory")
36
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
43
+ raise ActiveRecord::NoDatabaseError
37
44
  else
38
45
  raise
39
46
  end
@@ -41,27 +48,21 @@ module ActiveRecord
41
48
  end
42
49
 
43
50
  module ConnectionAdapters #:nodoc:
44
- class SQLite3Binary < Type::Binary # :nodoc:
45
- def cast_value(value)
46
- if value.encoding != Encoding::ASCII_8BIT
47
- value = value.force_encoding(Encoding::ASCII_8BIT)
48
- end
49
- value
50
- end
51
- end
52
-
53
- # The SQLite3 adapter works SQLite 3.6.16 or newer
54
- # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
51
+ # The SQLite3 adapter works with the sqlite3-ruby drivers
52
+ # (available as gem from https://rubygems.org/gems/sqlite3).
55
53
  #
56
54
  # Options:
57
55
  #
58
56
  # * <tt>:database</tt> - Path to the database file.
59
57
  class SQLite3Adapter < AbstractAdapter
60
- ADAPTER_NAME = 'SQLite'.freeze
61
- include Savepoints
58
+ ADAPTER_NAME = "SQLite"
59
+
60
+ include SQLite3::Quoting
61
+ include SQLite3::SchemaStatements
62
+ include SQLite3::DatabaseStatements
62
63
 
63
64
  NATIVE_DATABASE_TYPES = {
64
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
65
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
65
66
  string: { name: "varchar" },
66
67
  text: { name: "text" },
67
68
  integer: { name: "integer" },
@@ -71,58 +72,29 @@ module ActiveRecord
71
72
  time: { name: "time" },
72
73
  date: { name: "date" },
73
74
  binary: { name: "blob" },
74
- boolean: { name: "boolean" }
75
+ boolean: { name: "boolean" },
76
+ json: { name: "json" },
75
77
  }
76
78
 
77
- class StatementPool < ConnectionAdapters::StatementPool
78
- def initialize(connection, max)
79
- super
80
- @cache = Hash.new { |h,pid| h[pid] = {} }
81
- end
82
-
83
- def each(&block); cache.each(&block); end
84
- def key?(key); cache.key?(key); end
85
- def [](key); cache[key]; end
86
- def length; cache.length; end
87
-
88
- def []=(sql, key)
89
- while @max <= cache.size
90
- dealloc(cache.shift.last[:stmt])
91
- end
92
- cache[sql] = key
93
- end
94
-
95
- def clear
96
- cache.each_value do |hash|
97
- dealloc hash[:stmt]
98
- end
99
- cache.clear
100
- end
101
-
79
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
102
80
  private
103
- def cache
104
- @cache[$$]
105
- end
106
-
107
- def dealloc(stmt)
108
- stmt.close unless stmt.closed?
109
- end
81
+ def dealloc(stmt)
82
+ stmt.close unless stmt.closed?
83
+ end
110
84
  end
111
85
 
112
86
  def initialize(connection, logger, connection_options, config)
113
- super(connection, logger)
114
-
115
- @active = nil
116
- @statements = StatementPool.new(@connection,
117
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
118
- @config = config
119
-
120
- @visitor = Arel::Visitors::SQLite.new self
87
+ super(connection, logger, config)
88
+ configure_connection
89
+ end
121
90
 
122
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
123
- @prepared_statements = true
91
+ def self.database_exists?(config)
92
+ config = config.symbolize_keys
93
+ if config[:database] == ":memory:"
94
+ true
124
95
  else
125
- @prepared_statements = false
96
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
97
+ File.exist?(database_file)
126
98
  end
127
99
  end
128
100
 
@@ -134,26 +106,27 @@ module ActiveRecord
134
106
  true
135
107
  end
136
108
 
137
- def supports_partial_index?
138
- sqlite_version >= '3.8.0'
109
+ def supports_transaction_isolation?
110
+ true
139
111
  end
140
112
 
141
- # Returns true, since this connection adapter supports prepared statement
142
- # caching.
143
- def supports_statement_cache?
113
+ def supports_partial_index?
144
114
  true
145
115
  end
146
116
 
147
- # Returns true, since this connection adapter supports migrations.
148
- def supports_migrations? #:nodoc:
117
+ def supports_expression_index?
118
+ database_version >= "3.9.0"
119
+ end
120
+
121
+ def requires_reloading?
149
122
  true
150
123
  end
151
124
 
152
- def supports_primary_key? #:nodoc:
125
+ def supports_foreign_keys?
153
126
  true
154
127
  end
155
128
 
156
- def requires_reloading?
129
+ def supports_check_constraints?
157
130
  true
158
131
  end
159
132
 
@@ -161,34 +134,45 @@ module ActiveRecord
161
134
  true
162
135
  end
163
136
 
137
+ def supports_datetime_with_precision?
138
+ true
139
+ end
140
+
141
+ def supports_json?
142
+ true
143
+ end
144
+
145
+ def supports_common_table_expressions?
146
+ database_version >= "3.8.3"
147
+ end
148
+
149
+ def supports_insert_on_conflict?
150
+ database_version >= "3.24.0"
151
+ end
152
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
153
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
154
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
155
+
164
156
  def active?
165
- @active != false
157
+ !@connection.closed?
158
+ end
159
+
160
+ def reconnect!
161
+ super
162
+ connect if @connection.closed?
166
163
  end
167
164
 
168
165
  # Disconnects from the database if already connected. Otherwise, this
169
166
  # method does nothing.
170
167
  def disconnect!
171
168
  super
172
- @active = false
173
169
  @connection.close rescue nil
174
170
  end
175
171
 
176
- # Clears the prepared statements cache.
177
- def clear_cache!
178
- @statements.clear
179
- end
180
-
181
172
  def supports_index_sort_order?
182
173
  true
183
174
  end
184
175
 
185
- # Returns 62. SQLite supports index names up to 64
186
- # characters. The rest is used by rails internally to perform
187
- # temporary rename operations
188
- def allowed_index_name_length
189
- index_name_length - 2
190
- end
191
-
192
176
  def native_database_types #:nodoc:
193
177
  NATIVE_DATABASE_TYPES
194
178
  end
@@ -202,224 +186,38 @@ module ActiveRecord
202
186
  true
203
187
  end
204
188
 
205
- # QUOTING ==================================================
206
-
207
- def _quote(value) # :nodoc:
208
- case value
209
- when Type::Binary::Data
210
- "x'#{value.hex}'"
211
- else
212
- super
213
- end
214
- end
215
-
216
- def _type_cast(value) # :nodoc:
217
- case value
218
- when BigDecimal
219
- value.to_f
220
- when String
221
- if value.encoding == Encoding::ASCII_8BIT
222
- super(value.encode(Encoding::UTF_8))
223
- else
224
- super
225
- end
226
- else
227
- super
228
- end
229
- end
230
-
231
- def quote_string(s) #:nodoc:
232
- @connection.class.quote(s)
233
- end
234
-
235
- def quote_table_name_for_assignment(table, attr)
236
- quote_column_name(attr)
237
- end
238
-
239
- def quote_column_name(name) #:nodoc:
240
- %Q("#{name.to_s.gsub('"', '""')}")
241
- end
242
-
243
- # Quote date/time values for use in SQL input. Includes microseconds
244
- # if the value is a Time responding to usec.
245
- def quoted_date(value) #:nodoc:
246
- if value.respond_to?(:usec)
247
- "#{super}.#{sprintf("%06d", value.usec)}"
248
- else
249
- super
250
- end
251
- end
252
-
253
- #--
254
- # DATABASE STATEMENTS ======================================
255
- #++
256
-
257
- def explain(arel, binds = [])
258
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
259
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
189
+ def supports_lazy_transactions?
190
+ true
260
191
  end
261
192
 
262
- class ExplainPrettyPrinter
263
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
264
- # the output of the SQLite shell:
265
- #
266
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
267
- # 0|1|1|SCAN TABLE posts (~100000 rows)
268
- #
269
- def pp(result) # :nodoc:
270
- result.rows.map do |row|
271
- row.join('|')
272
- end.join("\n") + "\n"
273
- end
274
- end
193
+ # REFERENTIAL INTEGRITY ====================================
275
194
 
276
- def exec_query(sql, name = nil, binds = [])
277
- type_casted_binds = binds.map { |col, val|
278
- [col, type_cast(val, col)]
279
- }
280
-
281
- log(sql, name, type_casted_binds) do
282
- # Don't cache statements if they are not prepared
283
- if without_prepared_statement?(binds)
284
- stmt = @connection.prepare(sql)
285
- begin
286
- cols = stmt.columns
287
- records = stmt.to_a
288
- ensure
289
- stmt.close
290
- end
291
- stmt = records
292
- else
293
- cache = @statements[sql] ||= {
294
- :stmt => @connection.prepare(sql)
295
- }
296
- stmt = cache[:stmt]
297
- cols = cache[:cols] ||= stmt.columns
298
- stmt.reset!
299
- stmt.bind_params type_casted_binds.map { |_, val| val }
300
- end
195
+ def disable_referential_integrity # :nodoc:
196
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
197
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
301
198
 
302
- ActiveRecord::Result.new(cols, stmt.to_a)
199
+ begin
200
+ execute("PRAGMA defer_foreign_keys = ON")
201
+ execute("PRAGMA foreign_keys = OFF")
202
+ yield
203
+ ensure
204
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
205
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
303
206
  end
304
207
  end
305
208
 
306
- def exec_delete(sql, name = 'SQL', binds = [])
307
- exec_query(sql, name, binds)
308
- @connection.changes
309
- end
310
- alias :exec_update :exec_delete
311
-
312
- def last_inserted_id(result)
313
- @connection.last_insert_row_id
314
- end
315
-
316
- def execute(sql, name = nil) #:nodoc:
317
- log(sql, name) { @connection.execute(sql) }
318
- end
319
-
320
- def update_sql(sql, name = nil) #:nodoc:
321
- super
322
- @connection.changes
323
- end
324
-
325
- def delete_sql(sql, name = nil) #:nodoc:
326
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
327
- super sql, name
328
- end
329
-
330
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
331
- super
332
- id_value || @connection.last_insert_row_id
333
- end
334
- alias :create :insert_sql
335
-
336
- def select_rows(sql, name = nil, binds = [])
337
- exec_query(sql, name, binds).rows
338
- end
339
-
340
- def begin_db_transaction #:nodoc:
341
- log('begin transaction',nil) { @connection.transaction }
342
- end
343
-
344
- def commit_db_transaction #:nodoc:
345
- log('commit transaction',nil) { @connection.commit }
346
- end
347
-
348
- def exec_rollback_db_transaction #:nodoc:
349
- log('rollback transaction',nil) { @connection.rollback }
350
- end
351
-
352
209
  # SCHEMA STATEMENTS ========================================
353
210
 
354
- def tables(name = nil, table_name = nil) #:nodoc:
355
- sql = <<-SQL
356
- SELECT name
357
- FROM sqlite_master
358
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
359
- SQL
360
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
361
-
362
- exec_query(sql, 'SCHEMA').map do |row|
363
- row['name']
364
- end
211
+ def primary_keys(table_name) # :nodoc:
212
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
213
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
365
214
  end
366
- alias data_sources tables
367
215
 
368
- def table_exists?(table_name)
369
- table_name && tables(nil, table_name).any?
370
- end
371
- alias data_source_exists? table_exists?
372
-
373
- # Returns an array of +Column+ objects for the table specified by +table_name+.
374
- def columns(table_name) #:nodoc:
375
- table_structure(table_name).map do |field|
376
- case field["dflt_value"]
377
- when /^null$/i
378
- field["dflt_value"] = nil
379
- when /^'(.*)'$/m
380
- field["dflt_value"] = $1.gsub("''", "'")
381
- when /^"(.*)"$/m
382
- field["dflt_value"] = $1.gsub('""', '"')
383
- end
384
-
385
- sql_type = field['type']
386
- cast_type = lookup_cast_type(sql_type)
387
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
388
- end
389
- end
216
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
217
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
390
218
 
391
- # Returns an array of indexes for the given table.
392
- def indexes(table_name, name = nil) #:nodoc:
393
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
394
- sql = <<-SQL
395
- SELECT sql
396
- FROM sqlite_master
397
- WHERE name=#{quote(row['name'])} AND type='index'
398
- UNION ALL
399
- SELECT sql
400
- FROM sqlite_temp_master
401
- WHERE name=#{quote(row['name'])} AND type='index'
402
- SQL
403
- index_sql = exec_query(sql).first['sql']
404
- match = /\sWHERE\s+(.+)$/i.match(index_sql)
405
- where = match[1] if match
406
- IndexDefinition.new(
407
- table_name,
408
- row['name'],
409
- row['unique'] != 0,
410
- exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
411
- col['name']
412
- }, nil, nil, where)
413
- end
414
- end
415
-
416
- def primary_key(table_name) #:nodoc:
417
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
418
- return nil unless pks.count == 1
419
- pks[0]['name']
420
- end
219
+ index_name = index_name_for_remove(table_name, column_name, options)
421
220
 
422
- def remove_index!(table_name, index_name) #:nodoc:
423
221
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
424
222
  end
425
223
 
@@ -428,39 +226,40 @@ module ActiveRecord
428
226
  # Example:
429
227
  # rename_table('octopuses', 'octopi')
430
228
  def rename_table(table_name, new_name)
229
+ schema_cache.clear_data_source_cache!(table_name.to_s)
230
+ schema_cache.clear_data_source_cache!(new_name.to_s)
431
231
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
432
232
  rename_table_indexes(table_name, new_name)
433
233
  end
434
234
 
435
- # See: http://www.sqlite.org/lang_altertable.html
436
- # SQLite has an additional restriction on the ALTER TABLE statement
437
- def valid_alter_table_type?(type)
438
- type.to_sym != :primary_key
439
- end
440
-
441
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
442
- if valid_alter_table_type?(type)
443
- super(table_name, column_name, type, options)
444
- else
235
+ def add_column(table_name, column_name, type, **options) #:nodoc:
236
+ if invalid_alter_table_type?(type, options)
445
237
  alter_table(table_name) do |definition|
446
- definition.column(column_name, type, options)
238
+ definition.column(column_name, type, **options)
447
239
  end
240
+ else
241
+ super
448
242
  end
449
243
  end
450
244
 
451
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
245
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
452
246
  alter_table(table_name) do |definition|
453
247
  definition.remove_column column_name
248
+ definition.foreign_keys.delete_if do |_, fk_options|
249
+ fk_options[:column] == column_name.to_s
250
+ end
454
251
  end
455
252
  end
456
253
 
457
- def change_column_default(table_name, column_name, default) #:nodoc:
254
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
255
+ default = extract_new_default_value(default_or_changes)
256
+
458
257
  alter_table(table_name) do |definition|
459
258
  definition[column_name].default = default
460
259
  end
461
260
  end
462
261
 
463
- def change_column_null(table_name, column_name, null, default = nil)
262
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
464
263
  unless null || default.nil?
465
264
  exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
466
265
  end
@@ -469,82 +268,163 @@ module ActiveRecord
469
268
  end
470
269
  end
471
270
 
472
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
271
+ def change_column(table_name, column_name, type, **options) #:nodoc:
473
272
  alter_table(table_name) do |definition|
474
- include_default = options_include_default?(options)
475
273
  definition[column_name].instance_eval do
476
- self.type = type
477
- self.limit = options[:limit] if options.include?(:limit)
478
- self.default = options[:default] if include_default
479
- self.null = options[:null] if options.include?(:null)
480
- self.precision = options[:precision] if options.include?(:precision)
481
- self.scale = options[:scale] if options.include?(:scale)
274
+ self.type = aliased_types(type.to_s, type)
275
+ self.options.merge!(options)
482
276
  end
483
277
  end
484
278
  end
485
279
 
486
280
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
487
281
  column = column_for(table_name, column_name)
488
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
282
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
489
283
  rename_column_indexes(table_name, column.name, new_column_name)
490
284
  end
491
285
 
492
- protected
286
+ def add_reference(table_name, ref_name, **options) # :nodoc:
287
+ super(table_name, ref_name, type: :integer, **options)
288
+ end
289
+ alias :add_belongs_to :add_reference
290
+
291
+ def foreign_keys(table_name)
292
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
293
+ fk_info.map do |row|
294
+ options = {
295
+ column: row["from"],
296
+ primary_key: row["to"],
297
+ on_delete: extract_foreign_key_action(row["on_delete"]),
298
+ on_update: extract_foreign_key_action(row["on_update"])
299
+ }
300
+ ForeignKeyDefinition.new(table_name, row["table"], options)
301
+ end
302
+ end
303
+
304
+ def build_insert_sql(insert) # :nodoc:
305
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
306
+
307
+ if insert.skip_duplicates?
308
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
309
+ elsif insert.update_duplicates?
310
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
311
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
312
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
313
+ end
314
+
315
+ sql
316
+ end
317
+
318
+ def shared_cache? # :nodoc:
319
+ @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
320
+ end
321
+
322
+ def get_database_version # :nodoc:
323
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
324
+ end
325
+
326
+ def check_version # :nodoc:
327
+ if database_version < "3.8.0"
328
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
329
+ end
330
+ end
331
+
332
+ private
333
+ # See https://www.sqlite.org/limits.html,
334
+ # the default value is 999 when not configured.
335
+ def bind_params_length
336
+ 999
337
+ end
493
338
 
494
- def initialize_type_map(m)
339
+ def initialize_type_map(m = type_map)
495
340
  super
496
- m.register_type(/binary/i, SQLite3Binary.new)
341
+ register_class_with_limit m, %r(int)i, SQLite3Integer
497
342
  end
498
343
 
499
344
  def table_structure(table_name)
500
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
345
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
501
346
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
502
- structure
347
+ table_structure_with_collation(table_name, structure)
348
+ end
349
+ alias column_definitions table_structure
350
+
351
+ # See: https://www.sqlite.org/lang_altertable.html
352
+ # SQLite has an additional restriction on the ALTER TABLE statement
353
+ def invalid_alter_table_type?(type, options)
354
+ type.to_sym == :primary_key || options[:primary_key] ||
355
+ options[:null] == false && options[:default].nil?
503
356
  end
504
357
 
505
- def alter_table(table_name, options = {}) #:nodoc:
358
+ def alter_table(
359
+ table_name,
360
+ foreign_keys = foreign_keys(table_name),
361
+ check_constraints = check_constraints(table_name),
362
+ **options
363
+ )
506
364
  altered_table_name = "a#{table_name}"
507
- caller = lambda {|definition| yield definition if block_given?}
365
+
366
+ caller = lambda do |definition|
367
+ rename = options[:rename] || {}
368
+ foreign_keys.each do |fk|
369
+ if column = rename[fk.options[:column]]
370
+ fk.options[:column] = column
371
+ end
372
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
373
+ definition.foreign_key(to_table, **fk.options)
374
+ end
375
+
376
+ check_constraints.each do |chk|
377
+ definition.check_constraint(chk.expression, **chk.options)
378
+ end
379
+
380
+ yield definition if block_given?
381
+ end
508
382
 
509
383
  transaction do
510
- move_table(table_name, altered_table_name,
511
- options.merge(:temporary => true))
512
- move_table(altered_table_name, table_name, &caller)
384
+ disable_referential_integrity do
385
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
386
+ move_table(altered_table_name, table_name, &caller)
387
+ end
513
388
  end
514
389
  end
515
390
 
516
- def move_table(from, to, options = {}, &block) #:nodoc:
391
+ def move_table(from, to, options = {}, &block)
517
392
  copy_table(from, to, options, &block)
518
393
  drop_table(from)
519
394
  end
520
395
 
521
- def copy_table(from, to, options = {}) #:nodoc:
396
+ def copy_table(from, to, options = {})
522
397
  from_primary_key = primary_key(from)
523
398
  options[:id] = false
524
- create_table(to, options) do |definition|
399
+ create_table(to, **options) do |definition|
525
400
  @definition = definition
526
- @definition.primary_key(from_primary_key) if from_primary_key.present?
401
+ if from_primary_key.is_a?(Array)
402
+ @definition.primary_keys from_primary_key
403
+ end
404
+
527
405
  columns(from).each do |column|
528
406
  column_name = options[:rename] ?
529
407
  (options[:rename][column.name] ||
530
408
  options[:rename][column.name.to_sym] ||
531
409
  column.name) : column.name
532
- next if column_name == from_primary_key
533
410
 
534
411
  @definition.column(column_name, column.type,
535
- :limit => column.limit, :default => column.default,
536
- :precision => column.precision, :scale => column.scale,
537
- :null => column.null)
412
+ limit: column.limit, default: column.default,
413
+ precision: column.precision, scale: column.scale,
414
+ null: column.null, collation: column.collation,
415
+ primary_key: column_name == from_primary_key
416
+ )
538
417
  end
418
+
539
419
  yield @definition if block_given?
540
420
  end
541
421
  copy_table_indexes(from, to, options[:rename] || {})
542
422
  copy_table_contents(from, to,
543
- @definition.columns.map {|column| column.name},
423
+ @definition.columns.map(&:name),
544
424
  options[:rename] || {})
545
425
  end
546
426
 
547
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
427
+ def copy_table_indexes(from, to, rename = {})
548
428
  indexes(from).each do |index|
549
429
  name = index.name
550
430
  if to == "a#{from}"
@@ -553,60 +433,129 @@ module ActiveRecord
553
433
  name = name[1..-1]
554
434
  end
555
435
 
556
- to_column_names = columns(to).map { |c| c.name }
557
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
558
- to_column_names.include?(column)
436
+ columns = index.columns
437
+ if columns.is_a?(Array)
438
+ to_column_names = columns(to).map(&:name)
439
+ columns = columns.map { |c| rename[c] || c }.select do |column|
440
+ to_column_names.include?(column)
441
+ end
559
442
  end
560
443
 
561
444
  unless columns.empty?
562
445
  # index name can't be the same
563
- opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
564
- opts[:unique] = true if index.unique
565
- add_index(to, columns, opts)
446
+ options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
447
+ options[:unique] = true if index.unique
448
+ options[:where] = index.where if index.where
449
+ add_index(to, columns, **options)
566
450
  end
567
451
  end
568
452
  end
569
453
 
570
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
571
- column_mappings = Hash[columns.map {|name| [name, name]}]
454
+ def copy_table_contents(from, to, columns, rename = {})
455
+ column_mappings = Hash[columns.map { |name| [name, name] }]
572
456
  rename.each { |a| column_mappings[a.last] = a.first }
573
- from_columns = columns(from).collect {|col| col.name}
574
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
575
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
457
+ from_columns = columns(from).collect(&:name)
458
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
459
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
460
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
461
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
462
+
463
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
464
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
465
+ end
466
+
467
+ def translate_exception(exception, message:, sql:, binds:)
468
+ # SQLite 3.8.2 returns a newly formatted error message:
469
+ # UNIQUE constraint failed: *table_name*.*column_name*
470
+ # Older versions of SQLite return:
471
+ # column *column_name* is not unique
472
+ if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
473
+ RecordNotUnique.new(message, sql: sql, binds: binds)
474
+ elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
475
+ NotNullViolation.new(message, sql: sql, binds: binds)
476
+ elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
477
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
478
+ elsif exception.message.match?(/called on a closed database/i)
479
+ ConnectionNotEstablished.new(exception)
480
+ else
481
+ super
482
+ end
483
+ end
484
+
485
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
576
486
 
577
- quoted_to = quote_table_name(to)
487
+ def table_structure_with_collation(table_name, basic_structure)
488
+ collation_hash = {}
489
+ sql = <<~SQL
490
+ SELECT sql FROM
491
+ (SELECT * FROM sqlite_master UNION ALL
492
+ SELECT * FROM sqlite_temp_master)
493
+ WHERE type = 'table' AND name = #{quote(table_name)}
494
+ SQL
578
495
 
579
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
496
+ # Result will have following sample string
497
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
498
+ # "password_digest" varchar COLLATE "NOCASE");
499
+ result = query_value(sql, "SCHEMA")
580
500
 
581
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
582
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
501
+ if result
502
+ # Splitting with left parentheses and discarding the first part will return all
503
+ # columns separated with comma(,).
504
+ columns_string = result.split("(", 2).last
583
505
 
584
- column_values = columns.map do |col|
585
- quote(row[column_mappings[col]], raw_column_mappings[col])
506
+ columns_string.split(",").each do |column_string|
507
+ # This regex will match the column name and collation type and will save
508
+ # the value in $1 and $2 respectively.
509
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
586
510
  end
587
511
 
588
- sql << column_values * ', '
589
- sql << ')'
590
- exec_query sql
512
+ basic_structure.map do |column|
513
+ column_name = column["name"]
514
+
515
+ if collation_hash.has_key? column_name
516
+ column["collation"] = collation_hash[column_name]
517
+ end
518
+
519
+ column
520
+ end
521
+ else
522
+ basic_structure.to_a
591
523
  end
592
524
  end
593
525
 
594
- def sqlite_version
595
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
526
+ def arel_visitor
527
+ Arel::Visitors::SQLite.new(self)
596
528
  end
597
529
 
598
- def translate_exception(exception, message)
599
- case exception.message
600
- # SQLite 3.8.2 returns a newly formatted error message:
601
- # UNIQUE constraint failed: *table_name*.*column_name*
602
- # Older versions of SQLite return:
603
- # column *column_name* is not unique
604
- when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
605
- RecordNotUnique.new(message, exception)
606
- else
607
- super
608
- end
530
+ def build_statement_pool
531
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
532
+ end
533
+
534
+ def connect
535
+ @connection = ::SQLite3::Database.new(
536
+ @config[:database].to_s,
537
+ @config.merge(results_as_hash: true)
538
+ )
539
+ configure_connection
609
540
  end
541
+
542
+ def configure_connection
543
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
544
+
545
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
546
+ end
547
+
548
+ class SQLite3Integer < Type::Integer # :nodoc:
549
+ private
550
+ def _limit
551
+ # INTEGER storage class can be stored 8 bytes value.
552
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
553
+ limit || 8
554
+ end
555
+ end
556
+
557
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
610
558
  end
559
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
611
560
  end
612
561
  end