activerecord 4.2.0 → 6.1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1221 -796
  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 +267 -249
  8. data/lib/active_record/association_relation.rb +45 -7
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +172 -67
  11. data/lib/active_record/associations/association_scope.rb +105 -129
  12. data/lib/active_record/associations/belongs_to_association.rb +85 -59
  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 -33
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
  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 +168 -279
  22. data/lib/active_record/associations/collection_proxy.rb +263 -155
  23. data/lib/active_record/associations/foreign_association.rb +33 -0
  24. data/lib/active_record/associations/has_many_association.rb +57 -84
  25. data/lib/active_record/associations/has_many_through_association.rb +70 -82
  26. data/lib/active_record/associations/has_one_association.rb +74 -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 -73
  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 +175 -164
  32. data/lib/active_record/associations/preloader/association.rb +107 -112
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +99 -96
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +49 -24
  37. data/lib/active_record/associations.rb +1845 -1597
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -138
  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 +59 -36
  46. data/lib/active_record/attribute_methods/write.rb +25 -56
  47. data/lib/active_record/attribute_methods.rb +153 -162
  48. data/lib/active_record/attributes.rb +234 -70
  49. data/lib/active_record/autosave_association.rb +157 -69
  50. data/lib/active_record/base.rb +49 -50
  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 +46 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
  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 +196 -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 +271 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
  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 +78 -161
  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 -57
  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 +5 -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 +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  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 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  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 +171 -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 +499 -293
  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 +595 -382
  119. data/lib/active_record/connection_adapters/schema_cache.rb +191 -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 +146 -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 -389
  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 +488 -243
  133. data/lib/active_record/counter_cache.rb +71 -50
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +273 -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 +212 -94
  143. data/lib/active_record/errors.rb +225 -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 +11 -6
  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 +273 -496
  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 +64 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +52 -0
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +103 -95
  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 +298 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +685 -309
  170. data/lib/active_record/model_schema.rb +420 -113
  171. data/lib/active_record/nested_attributes.rb +265 -216
  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 +574 -135
  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 +175 -54
  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 +533 -216
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +485 -310
  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 +326 -244
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +318 -256
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +99 -84
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
  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 +139 -96
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -409
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +23 -21
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -342
  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 -26
  209. data/lib/active_record/scoping/default.rb +96 -82
  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 +133 -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 +366 -129
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
  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 +291 -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 +181 -152
  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 -41
  231. data/lib/active_record/type/date_time.rb +4 -38
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  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 +33 -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 +65 -48
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +44 -28
  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 -10
  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.rb +7 -5
  339. metadata +175 -65
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  347. data/lib/active_record/attribute.rb +0 -149
  348. data/lib/active_record/attribute_decorators.rb +0 -66
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -30
  363. data/lib/active_record/type/decimal.rb +0 -40
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -55
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -36
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -101
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  374. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,37 +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
- class SQLite3String < Type::String # :nodoc:
54
- def type_cast_for_database(value)
55
- if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
- value.encode(Encoding::UTF_8)
57
- else
58
- super
59
- end
60
- end
61
- end
62
-
63
- # The SQLite3 adapter works SQLite 3.6.16 or newer
64
- # 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).
65
53
  #
66
54
  # Options:
67
55
  #
68
56
  # * <tt>:database</tt> - Path to the database file.
69
57
  class SQLite3Adapter < AbstractAdapter
70
- ADAPTER_NAME = 'SQLite'.freeze
71
- include Savepoints
58
+ ADAPTER_NAME = "SQLite"
59
+
60
+ include SQLite3::Quoting
61
+ include SQLite3::SchemaStatements
62
+ include SQLite3::DatabaseStatements
72
63
 
73
64
  NATIVE_DATABASE_TYPES = {
74
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
65
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
75
66
  string: { name: "varchar" },
76
67
  text: { name: "text" },
77
68
  integer: { name: "integer" },
@@ -81,70 +72,29 @@ module ActiveRecord
81
72
  time: { name: "time" },
82
73
  date: { name: "date" },
83
74
  binary: { name: "blob" },
84
- boolean: { name: "boolean" }
75
+ boolean: { name: "boolean" },
76
+ json: { name: "json" },
85
77
  }
86
78
 
87
- class Version
88
- include Comparable
89
-
90
- def initialize(version_string)
91
- @version = version_string.split('.').map { |v| v.to_i }
92
- end
93
-
94
- def <=>(version_string)
95
- @version <=> version_string.split('.').map { |v| v.to_i }
96
- end
97
- end
98
-
99
- class StatementPool < ConnectionAdapters::StatementPool
100
- def initialize(connection, max)
101
- super
102
- @cache = Hash.new { |h,pid| h[pid] = {} }
103
- end
104
-
105
- def each(&block); cache.each(&block); end
106
- def key?(key); cache.key?(key); end
107
- def [](key); cache[key]; end
108
- def length; cache.length; end
109
-
110
- def []=(sql, key)
111
- while @max <= cache.size
112
- dealloc(cache.shift.last[:stmt])
113
- end
114
- cache[sql] = key
115
- end
116
-
117
- def clear
118
- cache.each_value do |hash|
119
- dealloc hash[:stmt]
120
- end
121
- cache.clear
122
- end
123
-
79
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
124
80
  private
125
- def cache
126
- @cache[$$]
127
- end
128
-
129
- def dealloc(stmt)
130
- stmt.close unless stmt.closed?
131
- end
81
+ def dealloc(stmt)
82
+ stmt.close unless stmt.closed?
83
+ end
132
84
  end
133
85
 
134
86
  def initialize(connection, logger, connection_options, config)
135
- super(connection, logger)
136
-
137
- @active = nil
138
- @statements = StatementPool.new(@connection,
139
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
140
- @config = config
141
-
142
- @visitor = Arel::Visitors::SQLite.new self
87
+ super(connection, logger, config)
88
+ configure_connection
89
+ end
143
90
 
144
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
145
- @prepared_statements = true
91
+ def self.database_exists?(config)
92
+ config = config.symbolize_keys
93
+ if config[:database] == ":memory:"
94
+ true
146
95
  else
147
- @prepared_statements = false
96
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
97
+ File.exist?(database_file)
148
98
  end
149
99
  end
150
100
 
@@ -156,26 +106,27 @@ module ActiveRecord
156
106
  true
157
107
  end
158
108
 
159
- def supports_partial_index?
160
- sqlite_version >= '3.8.0'
109
+ def supports_transaction_isolation?
110
+ true
161
111
  end
162
112
 
163
- # Returns true, since this connection adapter supports prepared statement
164
- # caching.
165
- def supports_statement_cache?
113
+ def supports_partial_index?
166
114
  true
167
115
  end
168
116
 
169
- # Returns true, since this connection adapter supports migrations.
170
- def supports_migrations? #:nodoc:
117
+ def supports_expression_index?
118
+ database_version >= "3.9.0"
119
+ end
120
+
121
+ def requires_reloading?
171
122
  true
172
123
  end
173
124
 
174
- def supports_primary_key? #:nodoc:
125
+ def supports_foreign_keys?
175
126
  true
176
127
  end
177
128
 
178
- def requires_reloading?
129
+ def supports_check_constraints?
179
130
  true
180
131
  end
181
132
 
@@ -183,34 +134,45 @@ module ActiveRecord
183
134
  true
184
135
  end
185
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
+
186
156
  def active?
187
- @active != false
157
+ !@connection.closed?
158
+ end
159
+
160
+ def reconnect!
161
+ super
162
+ connect if @connection.closed?
188
163
  end
189
164
 
190
165
  # Disconnects from the database if already connected. Otherwise, this
191
166
  # method does nothing.
192
167
  def disconnect!
193
168
  super
194
- @active = false
195
169
  @connection.close rescue nil
196
170
  end
197
171
 
198
- # Clears the prepared statements cache.
199
- def clear_cache!
200
- @statements.clear
201
- end
202
-
203
172
  def supports_index_sort_order?
204
173
  true
205
174
  end
206
175
 
207
- # Returns 62. SQLite supports index names up to 64
208
- # characters. The rest is used by rails internally to perform
209
- # temporary rename operations
210
- def allowed_index_name_length
211
- index_name_length - 2
212
- end
213
-
214
176
  def native_database_types #:nodoc:
215
177
  NATIVE_DATABASE_TYPES
216
178
  end
@@ -224,217 +186,38 @@ module ActiveRecord
224
186
  true
225
187
  end
226
188
 
227
- # QUOTING ==================================================
228
-
229
- def _quote(value) # :nodoc:
230
- case value
231
- when Type::Binary::Data
232
- "x'#{value.hex}'"
233
- else
234
- super
235
- end
236
- end
237
-
238
- def _type_cast(value) # :nodoc:
239
- case value
240
- when BigDecimal
241
- value.to_f
242
- else
243
- super
244
- end
245
- end
246
-
247
- def quote_string(s) #:nodoc:
248
- @connection.class.quote(s)
249
- end
250
-
251
- def quote_table_name_for_assignment(table, attr)
252
- quote_column_name(attr)
253
- end
254
-
255
- def quote_column_name(name) #:nodoc:
256
- %Q("#{name.to_s.gsub('"', '""')}")
257
- end
258
-
259
- # Quote date/time values for use in SQL input. Includes microseconds
260
- # if the value is a Time responding to usec.
261
- def quoted_date(value) #:nodoc:
262
- if value.respond_to?(:usec)
263
- "#{super}.#{sprintf("%06d", value.usec)}"
264
- else
265
- super
266
- end
267
- end
268
-
269
- #--
270
- # DATABASE STATEMENTS ======================================
271
- #++
272
-
273
- def explain(arel, binds = [])
274
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
275
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
189
+ def supports_lazy_transactions?
190
+ true
276
191
  end
277
192
 
278
- class ExplainPrettyPrinter
279
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
280
- # the output of the SQLite shell:
281
- #
282
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
283
- # 0|1|1|SCAN TABLE posts (~100000 rows)
284
- #
285
- def pp(result) # :nodoc:
286
- result.rows.map do |row|
287
- row.join('|')
288
- end.join("\n") + "\n"
289
- end
290
- end
193
+ # REFERENTIAL INTEGRITY ====================================
291
194
 
292
- def exec_query(sql, name = nil, binds = [])
293
- type_casted_binds = binds.map { |col, val|
294
- [col, type_cast(val, col)]
295
- }
296
-
297
- log(sql, name, type_casted_binds) do
298
- # Don't cache statements if they are not prepared
299
- if without_prepared_statement?(binds)
300
- stmt = @connection.prepare(sql)
301
- begin
302
- cols = stmt.columns
303
- records = stmt.to_a
304
- ensure
305
- stmt.close
306
- end
307
- stmt = records
308
- else
309
- cache = @statements[sql] ||= {
310
- :stmt => @connection.prepare(sql)
311
- }
312
- stmt = cache[:stmt]
313
- cols = cache[:cols] ||= stmt.columns
314
- stmt.reset!
315
- stmt.bind_params type_casted_binds.map { |_, val| val }
316
- 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")
317
198
 
318
- 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}")
319
206
  end
320
207
  end
321
208
 
322
- def exec_delete(sql, name = 'SQL', binds = [])
323
- exec_query(sql, name, binds)
324
- @connection.changes
325
- end
326
- alias :exec_update :exec_delete
327
-
328
- def last_inserted_id(result)
329
- @connection.last_insert_row_id
330
- end
331
-
332
- def execute(sql, name = nil) #:nodoc:
333
- log(sql, name) { @connection.execute(sql) }
334
- end
335
-
336
- def update_sql(sql, name = nil) #:nodoc:
337
- super
338
- @connection.changes
339
- end
340
-
341
- def delete_sql(sql, name = nil) #:nodoc:
342
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
343
- super sql, name
344
- end
345
-
346
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
347
- super
348
- id_value || @connection.last_insert_row_id
349
- end
350
- alias :create :insert_sql
351
-
352
- def select_rows(sql, name = nil, binds = [])
353
- exec_query(sql, name, binds).rows
354
- end
355
-
356
- def begin_db_transaction #:nodoc:
357
- log('begin transaction',nil) { @connection.transaction }
358
- end
359
-
360
- def commit_db_transaction #:nodoc:
361
- log('commit transaction',nil) { @connection.commit }
362
- end
363
-
364
- def rollback_db_transaction #:nodoc:
365
- log('rollback transaction',nil) { @connection.rollback }
366
- end
367
-
368
209
  # SCHEMA STATEMENTS ========================================
369
210
 
370
- def tables(name = nil, table_name = nil) #:nodoc:
371
- sql = <<-SQL
372
- SELECT name
373
- FROM sqlite_master
374
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
375
- SQL
376
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
377
-
378
- exec_query(sql, 'SCHEMA').map do |row|
379
- row['name']
380
- end
381
- end
382
-
383
- def table_exists?(table_name)
384
- table_name && tables(nil, table_name).any?
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"] }
385
214
  end
386
215
 
387
- # Returns an array of +Column+ objects for the table specified by +table_name+.
388
- def columns(table_name) #:nodoc:
389
- table_structure(table_name).map do |field|
390
- case field["dflt_value"]
391
- when /^null$/i
392
- field["dflt_value"] = nil
393
- when /^'(.*)'$/m
394
- field["dflt_value"] = $1.gsub("''", "'")
395
- when /^"(.*)"$/m
396
- field["dflt_value"] = $1.gsub('""', '"')
397
- end
398
-
399
- sql_type = field['type']
400
- cast_type = lookup_cast_type(sql_type)
401
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
402
- end
403
- 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)
404
218
 
405
- # Returns an array of indexes for the given table.
406
- def indexes(table_name, name = nil) #:nodoc:
407
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
408
- sql = <<-SQL
409
- SELECT sql
410
- FROM sqlite_master
411
- WHERE name=#{quote(row['name'])} AND type='index'
412
- UNION ALL
413
- SELECT sql
414
- FROM sqlite_temp_master
415
- WHERE name=#{quote(row['name'])} AND type='index'
416
- SQL
417
- index_sql = exec_query(sql).first['sql']
418
- match = /\sWHERE\s+(.+)$/i.match(index_sql)
419
- where = match[1] if match
420
- IndexDefinition.new(
421
- table_name,
422
- row['name'],
423
- row['unique'] != 0,
424
- exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
425
- col['name']
426
- }, nil, nil, where)
427
- end
428
- end
219
+ index_name = index_name_for_remove(table_name, column_name, options)
429
220
 
430
- def primary_key(table_name) #:nodoc:
431
- column = table_structure(table_name).find { |field|
432
- field['pk'] == 1
433
- }
434
- column && column['name']
435
- end
436
-
437
- def remove_index!(table_name, index_name) #:nodoc:
438
221
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
439
222
  end
440
223
 
@@ -443,39 +226,40 @@ module ActiveRecord
443
226
  # Example:
444
227
  # rename_table('octopuses', 'octopi')
445
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)
446
231
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
447
232
  rename_table_indexes(table_name, new_name)
448
233
  end
449
234
 
450
- # See: http://www.sqlite.org/lang_altertable.html
451
- # SQLite has an additional restriction on the ALTER TABLE statement
452
- def valid_alter_table_type?(type)
453
- type.to_sym != :primary_key
454
- end
455
-
456
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
457
- if valid_alter_table_type?(type)
458
- super(table_name, column_name, type, options)
459
- else
235
+ def add_column(table_name, column_name, type, **options) #:nodoc:
236
+ if invalid_alter_table_type?(type, options)
460
237
  alter_table(table_name) do |definition|
461
- definition.column(column_name, type, options)
238
+ definition.column(column_name, type, **options)
462
239
  end
240
+ else
241
+ super
463
242
  end
464
243
  end
465
244
 
466
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
245
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
467
246
  alter_table(table_name) do |definition|
468
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
469
251
  end
470
252
  end
471
253
 
472
- 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
+
473
257
  alter_table(table_name) do |definition|
474
258
  definition[column_name].default = default
475
259
  end
476
260
  end
477
261
 
478
- def change_column_null(table_name, column_name, null, default = nil)
262
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
479
263
  unless null || default.nil?
480
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")
481
265
  end
@@ -484,83 +268,163 @@ module ActiveRecord
484
268
  end
485
269
  end
486
270
 
487
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
271
+ def change_column(table_name, column_name, type, **options) #:nodoc:
488
272
  alter_table(table_name) do |definition|
489
- include_default = options_include_default?(options)
490
273
  definition[column_name].instance_eval do
491
- self.type = type
492
- self.limit = options[:limit] if options.include?(:limit)
493
- self.default = options[:default] if include_default
494
- self.null = options[:null] if options.include?(:null)
495
- self.precision = options[:precision] if options.include?(:precision)
496
- self.scale = options[:scale] if options.include?(:scale)
274
+ self.type = aliased_types(type.to_s, type)
275
+ self.options.merge!(options)
497
276
  end
498
277
  end
499
278
  end
500
279
 
501
280
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
502
281
  column = column_for(table_name, column_name)
503
- 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 })
504
283
  rename_column_indexes(table_name, column.name, new_column_name)
505
284
  end
506
285
 
507
- 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
508
325
 
509
- def initialize_type_map(m)
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
338
+
339
+ def initialize_type_map(m = type_map)
510
340
  super
511
- m.register_type(/binary/i, SQLite3Binary.new)
512
- register_class_with_limit m, %r(char)i, SQLite3String
341
+ register_class_with_limit m, %r(int)i, SQLite3Integer
513
342
  end
514
343
 
515
344
  def table_structure(table_name)
516
- 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")
517
346
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
518
- structure
347
+ table_structure_with_collation(table_name, structure)
519
348
  end
349
+ alias column_definitions table_structure
520
350
 
521
- def alter_table(table_name, options = {}) #:nodoc:
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?
356
+ end
357
+
358
+ def alter_table(
359
+ table_name,
360
+ foreign_keys = foreign_keys(table_name),
361
+ check_constraints = check_constraints(table_name),
362
+ **options
363
+ )
522
364
  altered_table_name = "a#{table_name}"
523
- 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
524
382
 
525
383
  transaction do
526
- move_table(table_name, altered_table_name,
527
- options.merge(:temporary => true))
528
- 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
529
388
  end
530
389
  end
531
390
 
532
- def move_table(from, to, options = {}, &block) #:nodoc:
391
+ def move_table(from, to, options = {}, &block)
533
392
  copy_table(from, to, options, &block)
534
393
  drop_table(from)
535
394
  end
536
395
 
537
- def copy_table(from, to, options = {}) #:nodoc:
396
+ def copy_table(from, to, options = {})
538
397
  from_primary_key = primary_key(from)
539
398
  options[:id] = false
540
- create_table(to, options) do |definition|
399
+ create_table(to, **options) do |definition|
541
400
  @definition = definition
542
- @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
+
543
405
  columns(from).each do |column|
544
406
  column_name = options[:rename] ?
545
407
  (options[:rename][column.name] ||
546
408
  options[:rename][column.name.to_sym] ||
547
409
  column.name) : column.name
548
- next if column_name == from_primary_key
549
410
 
550
411
  @definition.column(column_name, column.type,
551
- :limit => column.limit, :default => column.default,
552
- :precision => column.precision, :scale => column.scale,
553
- :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
+ )
554
417
  end
418
+
555
419
  yield @definition if block_given?
556
420
  end
557
421
  copy_table_indexes(from, to, options[:rename] || {})
558
422
  copy_table_contents(from, to,
559
- @definition.columns.map {|column| column.name},
423
+ @definition.columns.map(&:name),
560
424
  options[:rename] || {})
561
425
  end
562
426
 
563
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
427
+ def copy_table_indexes(from, to, rename = {})
564
428
  indexes(from).each do |index|
565
429
  name = index.name
566
430
  if to == "a#{from}"
@@ -569,60 +433,129 @@ module ActiveRecord
569
433
  name = name[1..-1]
570
434
  end
571
435
 
572
- to_column_names = columns(to).map { |c| c.name }
573
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
574
- 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
575
442
  end
576
443
 
577
444
  unless columns.empty?
578
445
  # index name can't be the same
579
- opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
580
- opts[:unique] = true if index.unique
581
- 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)
582
450
  end
583
451
  end
584
452
  end
585
453
 
586
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
587
- 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] }]
588
456
  rename.each { |a| column_mappings[a.last] = a.first }
589
- from_columns = columns(from).collect {|col| col.name}
590
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
591
- 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
592
486
 
593
- 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
594
495
 
595
- 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")
596
500
 
597
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
598
- 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
599
505
 
600
- column_values = columns.map do |col|
601
- 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
602
510
  end
603
511
 
604
- sql << column_values * ', '
605
- sql << ')'
606
- 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
607
523
  end
608
524
  end
609
525
 
610
- def sqlite_version
611
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
526
+ def arel_visitor
527
+ Arel::Visitors::SQLite.new(self)
612
528
  end
613
529
 
614
- def translate_exception(exception, message)
615
- case exception.message
616
- # SQLite 3.8.2 returns a newly formatted error message:
617
- # UNIQUE constraint failed: *table_name*.*column_name*
618
- # Older versions of SQLite return:
619
- # column *column_name* is not unique
620
- when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
621
- RecordNotUnique.new(message, exception)
622
- else
623
- super
624
- 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
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
625
555
  end
556
+
557
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
626
558
  end
559
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
627
560
  end
628
561
  end