activerecord 4.2.11.2 → 6.0.2.2

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.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1582
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -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 +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +5 -3
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  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_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
7
+ def references(*args, **options)
8
+ super(*args, type: :integer, **options)
9
+ end
10
+ alias :belongs_to :references
11
+
12
+ private
13
+ def integer_like_primary_key_type(type, options)
14
+ :primary_key
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+ def default_primary_key?(column)
9
+ schema_type(column) == :integer
10
+ end
11
+
12
+ def explicit_primary_key_default?(column)
13
+ column.bigint?
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module SchemaStatements # :nodoc:
7
+ # Returns an array of indexes for the given table.
8
+ def indexes(table_name)
9
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
10
+ # Indexes SQLite creates implicitly for internal use start with "sqlite_".
11
+ # See https://www.sqlite.org/fileformat2.html#intschema
12
+ next if row["name"].starts_with?("sqlite_")
13
+
14
+ index_sql = query_value(<<~SQL, "SCHEMA")
15
+ SELECT sql
16
+ FROM sqlite_master
17
+ WHERE name = #{quote(row['name'])} AND type = 'index'
18
+ UNION ALL
19
+ SELECT sql
20
+ FROM sqlite_temp_master
21
+ WHERE name = #{quote(row['name'])} AND type = 'index'
22
+ SQL
23
+
24
+ /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
25
+
26
+ columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
+ col["name"]
28
+ end
29
+
30
+ orders = {}
31
+
32
+ if columns.any?(&:nil?) # index created with an expression
33
+ columns = expressions
34
+ else
35
+ # Add info on sort order for columns (only desc order is explicitly specified,
36
+ # asc is the default)
37
+ if index_sql # index_sql can be null in case of primary key indexes
38
+ index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
39
+ orders[order_column] = :desc
40
+ }
41
+ end
42
+ end
43
+
44
+ IndexDefinition.new(
45
+ table_name,
46
+ row["name"],
47
+ row["unique"] != 0,
48
+ columns,
49
+ where: where,
50
+ orders: orders
51
+ )
52
+ end.compact
53
+ end
54
+
55
+ def add_foreign_key(from_table, to_table, **options)
56
+ alter_table(from_table) do |definition|
57
+ to_table = strip_table_name_prefix_and_suffix(to_table)
58
+ definition.foreign_key(to_table, options)
59
+ end
60
+ end
61
+
62
+ def remove_foreign_key(from_table, to_table = nil, **options)
63
+ to_table ||= options[:to_table]
64
+ options = options.except(:name, :to_table)
65
+ foreign_keys = foreign_keys(from_table)
66
+
67
+ fkey = foreign_keys.detect do |fk|
68
+ table = to_table || begin
69
+ table = options[:column].to_s.delete_suffix("_id")
70
+ Base.pluralize_table_names ? table.pluralize : table
71
+ end
72
+ table = strip_table_name_prefix_and_suffix(table)
73
+ fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
74
+ fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
75
+ end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
76
+
77
+ foreign_keys.delete(fkey)
78
+ alter_table(from_table, foreign_keys)
79
+ end
80
+
81
+ def create_schema_dumper(options)
82
+ SQLite3::SchemaDumper.create(self, options)
83
+ end
84
+
85
+ private
86
+ def schema_creation
87
+ SQLite3::SchemaCreation.new(self)
88
+ end
89
+
90
+ def create_table_definition(*args)
91
+ SQLite3::TableDefinition.new(self, *args)
92
+ end
93
+
94
+ def new_column_from_field(table_name, field)
95
+ default = \
96
+ case field["dflt_value"]
97
+ when /^null$/i
98
+ nil
99
+ when /^'(.*)'$/m
100
+ $1.gsub("''", "'")
101
+ when /^"(.*)"$/m
102
+ $1.gsub('""', '"')
103
+ else
104
+ field["dflt_value"]
105
+ end
106
+
107
+ type_metadata = fetch_type_metadata(field["type"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
109
+ end
110
+
111
+ def data_source_sql(name = nil, type: nil)
112
+ scope = quoted_scope(name, type: type)
113
+ scope[:type] ||= "'table','view'"
114
+
115
+ sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
116
+ sql << " AND name = #{scope[:name]}" if scope[:name]
117
+ sql << " AND type IN (#{scope[:type]})"
118
+ sql
119
+ end
120
+
121
+ def quoted_scope(name = nil, type: nil)
122
+ type = \
123
+ case type
124
+ when "BASE TABLE"
125
+ "'table'"
126
+ when "VIEW"
127
+ "'view'"
128
+ end
129
+ scope = {}
130
+ scope[:name] = quote(name) if name
131
+ scope[:type] = type if type
132
+ scope
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -1,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]
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,15 +48,6 @@ 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
51
  # The SQLite3 adapter works SQLite 3.6.16 or newer
54
52
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
55
53
  #
@@ -57,11 +55,14 @@ module ActiveRecord
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,39 @@ 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
79
+ def self.represent_boolean_as_integer=(value) # :nodoc:
80
+ if value == false
81
+ raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
93
82
  end
94
83
 
95
- def clear
96
- cache.each_value do |hash|
97
- dealloc hash[:stmt]
98
- end
99
- cache.clear
100
- end
84
+ ActiveSupport::Deprecation.warn(
85
+ "`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
86
+ )
87
+ end
101
88
 
89
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
102
90
  private
103
- def cache
104
- @cache[$$]
105
- end
106
-
107
- def dealloc(stmt)
108
- stmt.close unless stmt.closed?
109
- end
91
+ def dealloc(stmt)
92
+ stmt.close unless stmt.closed?
93
+ end
110
94
  end
111
95
 
112
96
  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
97
+ super(connection, logger, config)
98
+ configure_connection
99
+ end
121
100
 
122
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
123
- @prepared_statements = true
101
+ def self.database_exists?(config)
102
+ config = config.symbolize_keys
103
+ if config[:database] == ":memory:"
104
+ return true
124
105
  else
125
- @prepared_statements = false
106
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
107
+ File.exist?(database_file)
126
108
  end
127
109
  end
128
110
 
@@ -135,55 +117,66 @@ module ActiveRecord
135
117
  end
136
118
 
137
119
  def supports_partial_index?
138
- sqlite_version >= '3.8.0'
120
+ true
121
+ end
122
+
123
+ def supports_expression_index?
124
+ database_version >= "3.9.0"
139
125
  end
140
126
 
141
- # Returns true, since this connection adapter supports prepared statement
142
- # caching.
143
- def supports_statement_cache?
127
+ def requires_reloading?
144
128
  true
145
129
  end
146
130
 
147
- # Returns true, since this connection adapter supports migrations.
148
- def supports_migrations? #:nodoc:
131
+ def supports_foreign_keys?
149
132
  true
150
133
  end
151
134
 
152
- def supports_primary_key? #:nodoc:
135
+ def supports_views?
153
136
  true
154
137
  end
155
138
 
156
- def requires_reloading?
139
+ def supports_datetime_with_precision?
157
140
  true
158
141
  end
159
142
 
160
- def supports_views?
143
+ def supports_json?
161
144
  true
162
145
  end
163
146
 
147
+ def supports_common_table_expressions?
148
+ database_version >= "3.8.3"
149
+ end
150
+
151
+ def supports_insert_on_conflict?
152
+ database_version >= "3.24.0"
153
+ end
154
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
155
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
156
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
157
+
164
158
  def active?
165
- @active != false
159
+ !@connection.closed?
160
+ end
161
+
162
+ def reconnect!
163
+ super
164
+ connect if @connection.closed?
166
165
  end
167
166
 
168
167
  # Disconnects from the database if already connected. Otherwise, this
169
168
  # method does nothing.
170
169
  def disconnect!
171
170
  super
172
- @active = false
173
171
  @connection.close rescue nil
174
172
  end
175
173
 
176
- # Clears the prepared statements cache.
177
- def clear_cache!
178
- @statements.clear
179
- end
180
-
181
174
  def supports_index_sort_order?
182
175
  true
183
176
  end
184
177
 
185
178
  # Returns 62. SQLite supports index names up to 64
186
- # characters. The rest is used by rails internally to perform
179
+ # characters. The rest is used by Rails internally to perform
187
180
  # temporary rename operations
188
181
  def allowed_index_name_length
189
182
  index_name_length - 2
@@ -202,224 +195,43 @@ module ActiveRecord
202
195
  true
203
196
  end
204
197
 
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)
198
+ def supports_lazy_transactions?
199
+ true
233
200
  end
234
201
 
235
- def quote_table_name_for_assignment(table, attr)
236
- quote_column_name(attr)
237
- end
202
+ # REFERENTIAL INTEGRITY ====================================
238
203
 
239
- def quote_column_name(name) #:nodoc:
240
- %Q("#{name.to_s.gsub('"', '""')}")
241
- end
204
+ def disable_referential_integrity # :nodoc:
205
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
206
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
242
207
 
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
208
+ begin
209
+ execute("PRAGMA defer_foreign_keys = ON")
210
+ execute("PRAGMA foreign_keys = OFF")
211
+ yield
212
+ ensure
213
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
214
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
250
215
  end
251
216
  end
252
217
 
253
218
  #--
254
219
  # DATABASE STATEMENTS ======================================
255
220
  #++
256
-
257
221
  def explain(arel, binds = [])
258
222
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
259
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
260
- end
261
-
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
275
-
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
301
-
302
- ActiveRecord::Result.new(cols, stmt.to_a)
303
- end
304
- end
305
-
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 }
223
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
350
224
  end
351
225
 
352
226
  # SCHEMA STATEMENTS ========================================
353
227
 
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
365
- end
366
- alias data_sources tables
367
-
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
390
-
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
228
+ def primary_keys(table_name) # :nodoc:
229
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
230
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
414
231
  end
415
232
 
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
421
-
422
- def remove_index!(table_name, index_name) #:nodoc:
233
+ def remove_index(table_name, options = {}) #:nodoc:
234
+ index_name = index_name_for_remove(table_name, options)
423
235
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
424
236
  end
425
237
 
@@ -432,35 +244,34 @@ module ActiveRecord
432
244
  rename_table_indexes(table_name, new_name)
433
245
  end
434
246
 
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
247
  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
248
+ if invalid_alter_table_type?(type, options)
445
249
  alter_table(table_name) do |definition|
446
250
  definition.column(column_name, type, options)
447
251
  end
252
+ else
253
+ super
448
254
  end
449
255
  end
450
256
 
451
257
  def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
452
258
  alter_table(table_name) do |definition|
453
259
  definition.remove_column column_name
260
+ definition.foreign_keys.delete_if do |_, fk_options|
261
+ fk_options[:column] == column_name.to_s
262
+ end
454
263
  end
455
264
  end
456
265
 
457
- def change_column_default(table_name, column_name, default) #:nodoc:
266
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
267
+ default = extract_new_default_value(default_or_changes)
268
+
458
269
  alter_table(table_name) do |definition|
459
270
  definition[column_name].default = default
460
271
  end
461
272
  end
462
273
 
463
- def change_column_null(table_name, column_name, null, default = nil)
274
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
464
275
  unless null || default.nil?
465
276
  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
277
  end
@@ -471,80 +282,150 @@ module ActiveRecord
471
282
 
472
283
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
473
284
  alter_table(table_name) do |definition|
474
- include_default = options_include_default?(options)
475
285
  definition[column_name].instance_eval do
476
286
  self.type = type
477
287
  self.limit = options[:limit] if options.include?(:limit)
478
- self.default = options[:default] if include_default
288
+ self.default = options[:default] if options.include?(:default)
479
289
  self.null = options[:null] if options.include?(:null)
480
290
  self.precision = options[:precision] if options.include?(:precision)
481
- self.scale = options[:scale] if options.include?(:scale)
291
+ self.scale = options[:scale] if options.include?(:scale)
292
+ self.collation = options[:collation] if options.include?(:collation)
482
293
  end
483
294
  end
484
295
  end
485
296
 
486
297
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
487
298
  column = column_for(table_name, column_name)
488
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
299
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
489
300
  rename_column_indexes(table_name, column.name, new_column_name)
490
301
  end
491
302
 
492
- protected
303
+ def add_reference(table_name, ref_name, **options) # :nodoc:
304
+ super(table_name, ref_name, type: :integer, **options)
305
+ end
306
+ alias :add_belongs_to :add_reference
493
307
 
494
- def initialize_type_map(m)
308
+ def foreign_keys(table_name)
309
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
310
+ fk_info.map do |row|
311
+ options = {
312
+ column: row["from"],
313
+ primary_key: row["to"],
314
+ on_delete: extract_foreign_key_action(row["on_delete"]),
315
+ on_update: extract_foreign_key_action(row["on_update"])
316
+ }
317
+ ForeignKeyDefinition.new(table_name, row["table"], options)
318
+ end
319
+ end
320
+
321
+ def build_insert_sql(insert) # :nodoc:
322
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
323
+
324
+ if insert.skip_duplicates?
325
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
326
+ elsif insert.update_duplicates?
327
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
328
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
329
+ end
330
+
331
+ sql
332
+ end
333
+
334
+ def get_database_version # :nodoc:
335
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
336
+ end
337
+
338
+ def check_version # :nodoc:
339
+ if database_version < "3.8.0"
340
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
341
+ end
342
+ end
343
+
344
+ private
345
+ # See https://www.sqlite.org/limits.html,
346
+ # the default value is 999 when not configured.
347
+ def bind_params_length
348
+ 999
349
+ end
350
+
351
+ def initialize_type_map(m = type_map)
495
352
  super
496
- m.register_type(/binary/i, SQLite3Binary.new)
353
+ register_class_with_limit m, %r(int)i, SQLite3Integer
497
354
  end
498
355
 
499
356
  def table_structure(table_name)
500
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
357
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
501
358
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
502
- structure
359
+ table_structure_with_collation(table_name, structure)
360
+ end
361
+ alias column_definitions table_structure
362
+
363
+ # See: https://www.sqlite.org/lang_altertable.html
364
+ # SQLite has an additional restriction on the ALTER TABLE statement
365
+ def invalid_alter_table_type?(type, options)
366
+ type.to_sym == :primary_key || options[:primary_key]
503
367
  end
504
368
 
505
- def alter_table(table_name, options = {}) #:nodoc:
369
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
506
370
  altered_table_name = "a#{table_name}"
507
- caller = lambda {|definition| yield definition if block_given?}
371
+
372
+ caller = lambda do |definition|
373
+ rename = options[:rename] || {}
374
+ foreign_keys.each do |fk|
375
+ if column = rename[fk.options[:column]]
376
+ fk.options[:column] = column
377
+ end
378
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
379
+ definition.foreign_key(to_table, fk.options)
380
+ end
381
+
382
+ yield definition if block_given?
383
+ end
508
384
 
509
385
  transaction do
510
- move_table(table_name, altered_table_name,
511
- options.merge(:temporary => true))
512
- move_table(altered_table_name, table_name, &caller)
386
+ disable_referential_integrity do
387
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
388
+ move_table(altered_table_name, table_name, &caller)
389
+ end
513
390
  end
514
391
  end
515
392
 
516
- def move_table(from, to, options = {}, &block) #:nodoc:
393
+ def move_table(from, to, options = {}, &block)
517
394
  copy_table(from, to, options, &block)
518
395
  drop_table(from)
519
396
  end
520
397
 
521
- def copy_table(from, to, options = {}) #:nodoc:
398
+ def copy_table(from, to, options = {})
522
399
  from_primary_key = primary_key(from)
523
400
  options[:id] = false
524
401
  create_table(to, options) do |definition|
525
402
  @definition = definition
526
- @definition.primary_key(from_primary_key) if from_primary_key.present?
403
+ if from_primary_key.is_a?(Array)
404
+ @definition.primary_keys from_primary_key
405
+ end
527
406
  columns(from).each do |column|
528
407
  column_name = options[:rename] ?
529
408
  (options[:rename][column.name] ||
530
409
  options[:rename][column.name.to_sym] ||
531
410
  column.name) : column.name
532
- next if column_name == from_primary_key
533
411
 
534
412
  @definition.column(column_name, column.type,
535
- :limit => column.limit, :default => column.default,
536
- :precision => column.precision, :scale => column.scale,
537
- :null => column.null)
413
+ limit: column.limit, default: column.default,
414
+ precision: column.precision, scale: column.scale,
415
+ null: column.null, collation: column.collation,
416
+ primary_key: column_name == from_primary_key
417
+ )
538
418
  end
419
+
539
420
  yield @definition if block_given?
540
421
  end
541
422
  copy_table_indexes(from, to, options[:rename] || {})
542
423
  copy_table_contents(from, to,
543
- @definition.columns.map {|column| column.name},
424
+ @definition.columns.map(&:name),
544
425
  options[:rename] || {})
545
426
  end
546
427
 
547
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
428
+ def copy_table_indexes(from, to, rename = {})
548
429
  indexes(from).each do |index|
549
430
  name = index.name
550
431
  if to == "a#{from}"
@@ -553,60 +434,128 @@ module ActiveRecord
553
434
  name = name[1..-1]
554
435
  end
555
436
 
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)
437
+ columns = index.columns
438
+ if columns.is_a?(Array)
439
+ to_column_names = columns(to).map(&:name)
440
+ columns = columns.map { |c| rename[c] || c }.select do |column|
441
+ to_column_names.include?(column)
442
+ end
559
443
  end
560
444
 
561
445
  unless columns.empty?
562
446
  # index name can't be the same
563
447
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
564
448
  opts[:unique] = true if index.unique
449
+ opts[:where] = index.where if index.where
565
450
  add_index(to, columns, opts)
566
451
  end
567
452
  end
568
453
  end
569
454
 
570
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
571
- column_mappings = Hash[columns.map {|name| [name, name]}]
455
+ def copy_table_contents(from, to, columns, rename = {})
456
+ column_mappings = Hash[columns.map { |name| [name, name] }]
572
457
  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) } * ','
576
-
577
- quoted_to = quote_table_name(to)
578
-
579
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
580
-
581
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
582
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
583
-
584
- column_values = columns.map do |col|
585
- quote(row[column_mappings[col]], raw_column_mappings[col])
586
- end
587
-
588
- sql << column_values * ', '
589
- sql << ')'
590
- exec_query sql
591
- end
458
+ from_columns = columns(from).collect(&:name)
459
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
460
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
461
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
462
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
463
+
464
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
465
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
592
466
  end
593
467
 
594
- def sqlite_version
595
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
596
- end
597
-
598
- def translate_exception(exception, message)
468
+ def translate_exception(exception, message:, sql:, binds:)
599
469
  case exception.message
600
470
  # SQLite 3.8.2 returns a newly formatted error message:
601
471
  # UNIQUE constraint failed: *table_name*.*column_name*
602
472
  # Older versions of SQLite return:
603
473
  # column *column_name* is not unique
604
474
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
605
- RecordNotUnique.new(message, exception)
475
+ RecordNotUnique.new(message, sql: sql, binds: binds)
476
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
477
+ NotNullViolation.new(message, sql: sql, binds: binds)
478
+ when /FOREIGN KEY constraint failed/i
479
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
606
480
  else
607
481
  super
608
482
  end
609
483
  end
484
+
485
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
486
+
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
495
+
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 = exec_query(sql, "SCHEMA").first
500
+
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["sql"].split("(", 2).last
505
+
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
510
+ end
511
+
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
523
+ end
524
+ end
525
+
526
+ def arel_visitor
527
+ Arel::Visitors::SQLite.new(self)
528
+ end
529
+
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
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