activerecord 4.2.11.3 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +613 -1643
  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 +11 -6
  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 +125 -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 +131 -287
  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 +137 -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 +91 -37
  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 +796 -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 +234 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -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 +460 -204
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
  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 +200 -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 +64 -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 +551 -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 +118 -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 +290 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +176 -41
  129. data/lib/active_record/core.rb +251 -231
  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 +6 -4
  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 +92 -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 +312 -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 +328 -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 +518 -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 +277 -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 +575 -394
  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 +86 -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 +224 -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 +216 -150
  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 +51 -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 +164 -59
  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,62 @@ 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_insert_on_conflict?
148
+ database_version >= "3.24.0"
149
+ end
150
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
151
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
152
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
153
+
164
154
  def active?
165
- @active != false
155
+ !@connection.closed?
156
+ end
157
+
158
+ def reconnect!
159
+ super
160
+ connect if @connection.closed?
166
161
  end
167
162
 
168
163
  # Disconnects from the database if already connected. Otherwise, this
169
164
  # method does nothing.
170
165
  def disconnect!
171
166
  super
172
- @active = false
173
167
  @connection.close rescue nil
174
168
  end
175
169
 
176
- # Clears the prepared statements cache.
177
- def clear_cache!
178
- @statements.clear
179
- end
180
-
181
170
  def supports_index_sort_order?
182
171
  true
183
172
  end
184
173
 
185
174
  # Returns 62. SQLite supports index names up to 64
186
- # characters. The rest is used by rails internally to perform
175
+ # characters. The rest is used by Rails internally to perform
187
176
  # temporary rename operations
188
177
  def allowed_index_name_length
189
178
  index_name_length - 2
@@ -202,224 +191,43 @@ module ActiveRecord
202
191
  true
203
192
  end
204
193
 
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
194
+ def supports_lazy_transactions?
195
+ true
229
196
  end
230
197
 
231
- def quote_string(s) #:nodoc:
232
- @connection.class.quote(s)
233
- end
198
+ # REFERENTIAL INTEGRITY ====================================
234
199
 
235
- def quote_table_name_for_assignment(table, attr)
236
- quote_column_name(attr)
237
- end
238
-
239
- def quote_column_name(name) #:nodoc:
240
- %Q("#{name.to_s.gsub('"', '""')}")
241
- end
200
+ def disable_referential_integrity # :nodoc:
201
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
202
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
242
203
 
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
204
+ begin
205
+ execute("PRAGMA defer_foreign_keys = ON")
206
+ execute("PRAGMA foreign_keys = OFF")
207
+ yield
208
+ ensure
209
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
210
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
250
211
  end
251
212
  end
252
213
 
253
214
  #--
254
215
  # DATABASE STATEMENTS ======================================
255
216
  #++
256
-
257
217
  def explain(arel, binds = [])
258
218
  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 }
219
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
350
220
  end
351
221
 
352
222
  # SCHEMA STATEMENTS ========================================
353
223
 
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
414
- end
415
-
416
- def primary_key(table_name) #:nodoc:
417
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
418
- return nil unless pks.count == 1
419
- pks[0]['name']
224
+ def primary_keys(table_name) # :nodoc:
225
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
226
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
420
227
  end
421
228
 
422
- def remove_index!(table_name, index_name) #:nodoc:
229
+ def remove_index(table_name, options = {}) #:nodoc:
230
+ index_name = index_name_for_remove(table_name, options)
423
231
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
424
232
  end
425
233
 
@@ -432,35 +240,34 @@ module ActiveRecord
432
240
  rename_table_indexes(table_name, new_name)
433
241
  end
434
242
 
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
243
  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
244
+ if invalid_alter_table_type?(type, options)
445
245
  alter_table(table_name) do |definition|
446
246
  definition.column(column_name, type, options)
447
247
  end
248
+ else
249
+ super
448
250
  end
449
251
  end
450
252
 
451
253
  def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
452
254
  alter_table(table_name) do |definition|
453
255
  definition.remove_column column_name
256
+ definition.foreign_keys.delete_if do |_, fk_options|
257
+ fk_options[:column] == column_name.to_s
258
+ end
454
259
  end
455
260
  end
456
261
 
457
- def change_column_default(table_name, column_name, default) #:nodoc:
262
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
263
+ default = extract_new_default_value(default_or_changes)
264
+
458
265
  alter_table(table_name) do |definition|
459
266
  definition[column_name].default = default
460
267
  end
461
268
  end
462
269
 
463
- def change_column_null(table_name, column_name, null, default = nil)
270
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
464
271
  unless null || default.nil?
465
272
  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
273
  end
@@ -471,80 +278,150 @@ module ActiveRecord
471
278
 
472
279
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
473
280
  alter_table(table_name) do |definition|
474
- include_default = options_include_default?(options)
475
281
  definition[column_name].instance_eval do
476
282
  self.type = type
477
283
  self.limit = options[:limit] if options.include?(:limit)
478
- self.default = options[:default] if include_default
284
+ self.default = options[:default] if options.include?(:default)
479
285
  self.null = options[:null] if options.include?(:null)
480
286
  self.precision = options[:precision] if options.include?(:precision)
481
- self.scale = options[:scale] if options.include?(:scale)
287
+ self.scale = options[:scale] if options.include?(:scale)
288
+ self.collation = options[:collation] if options.include?(:collation)
482
289
  end
483
290
  end
484
291
  end
485
292
 
486
293
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
487
294
  column = column_for(table_name, column_name)
488
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
295
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
489
296
  rename_column_indexes(table_name, column.name, new_column_name)
490
297
  end
491
298
 
492
- protected
299
+ def add_reference(table_name, ref_name, **options) # :nodoc:
300
+ super(table_name, ref_name, type: :integer, **options)
301
+ end
302
+ alias :add_belongs_to :add_reference
303
+
304
+ def foreign_keys(table_name)
305
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
306
+ fk_info.map do |row|
307
+ options = {
308
+ column: row["from"],
309
+ primary_key: row["to"],
310
+ on_delete: extract_foreign_key_action(row["on_delete"]),
311
+ on_update: extract_foreign_key_action(row["on_update"])
312
+ }
313
+ ForeignKeyDefinition.new(table_name, row["table"], options)
314
+ end
315
+ end
316
+
317
+ def build_insert_sql(insert) # :nodoc:
318
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
319
+
320
+ if insert.skip_duplicates?
321
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
322
+ elsif insert.update_duplicates?
323
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
324
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
325
+ end
326
+
327
+ sql
328
+ end
493
329
 
494
- def initialize_type_map(m)
330
+ def get_database_version # :nodoc:
331
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
332
+ end
333
+
334
+ def check_version # :nodoc:
335
+ if database_version < "3.8.0"
336
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
337
+ end
338
+ end
339
+
340
+ private
341
+ # See https://www.sqlite.org/limits.html,
342
+ # the default value is 999 when not configured.
343
+ def bind_params_length
344
+ 999
345
+ end
346
+
347
+ def initialize_type_map(m = type_map)
495
348
  super
496
- m.register_type(/binary/i, SQLite3Binary.new)
349
+ register_class_with_limit m, %r(int)i, SQLite3Integer
497
350
  end
498
351
 
499
352
  def table_structure(table_name)
500
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
353
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
501
354
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
502
- structure
355
+ table_structure_with_collation(table_name, structure)
503
356
  end
357
+ alias column_definitions table_structure
504
358
 
505
- def alter_table(table_name, options = {}) #:nodoc:
359
+ # See: https://www.sqlite.org/lang_altertable.html
360
+ # SQLite has an additional restriction on the ALTER TABLE statement
361
+ def invalid_alter_table_type?(type, options)
362
+ type.to_sym == :primary_key || options[:primary_key]
363
+ end
364
+
365
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
506
366
  altered_table_name = "a#{table_name}"
507
- caller = lambda {|definition| yield definition if block_given?}
367
+
368
+ caller = lambda do |definition|
369
+ rename = options[:rename] || {}
370
+ foreign_keys.each do |fk|
371
+ if column = rename[fk.options[:column]]
372
+ fk.options[:column] = column
373
+ end
374
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
375
+ definition.foreign_key(to_table, fk.options)
376
+ end
377
+
378
+ yield definition if block_given?
379
+ end
508
380
 
509
381
  transaction do
510
- move_table(table_name, altered_table_name,
511
- options.merge(:temporary => true))
512
- move_table(altered_table_name, table_name, &caller)
382
+ disable_referential_integrity do
383
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
384
+ move_table(altered_table_name, table_name, &caller)
385
+ end
513
386
  end
514
387
  end
515
388
 
516
- def move_table(from, to, options = {}, &block) #:nodoc:
389
+ def move_table(from, to, options = {}, &block)
517
390
  copy_table(from, to, options, &block)
518
391
  drop_table(from)
519
392
  end
520
393
 
521
- def copy_table(from, to, options = {}) #:nodoc:
394
+ def copy_table(from, to, options = {})
522
395
  from_primary_key = primary_key(from)
523
396
  options[:id] = false
524
397
  create_table(to, options) do |definition|
525
398
  @definition = definition
526
- @definition.primary_key(from_primary_key) if from_primary_key.present?
399
+ if from_primary_key.is_a?(Array)
400
+ @definition.primary_keys from_primary_key
401
+ end
527
402
  columns(from).each do |column|
528
403
  column_name = options[:rename] ?
529
404
  (options[:rename][column.name] ||
530
405
  options[:rename][column.name.to_sym] ||
531
406
  column.name) : column.name
532
- next if column_name == from_primary_key
533
407
 
534
408
  @definition.column(column_name, column.type,
535
- :limit => column.limit, :default => column.default,
536
- :precision => column.precision, :scale => column.scale,
537
- :null => column.null)
409
+ limit: column.limit, default: column.default,
410
+ precision: column.precision, scale: column.scale,
411
+ null: column.null, collation: column.collation,
412
+ primary_key: column_name == from_primary_key
413
+ )
538
414
  end
415
+
539
416
  yield @definition if block_given?
540
417
  end
541
418
  copy_table_indexes(from, to, options[:rename] || {})
542
419
  copy_table_contents(from, to,
543
- @definition.columns.map {|column| column.name},
420
+ @definition.columns.map(&:name),
544
421
  options[:rename] || {})
545
422
  end
546
423
 
547
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
424
+ def copy_table_indexes(from, to, rename = {})
548
425
  indexes(from).each do |index|
549
426
  name = index.name
550
427
  if to == "a#{from}"
@@ -553,60 +430,128 @@ module ActiveRecord
553
430
  name = name[1..-1]
554
431
  end
555
432
 
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)
433
+ columns = index.columns
434
+ if columns.is_a?(Array)
435
+ to_column_names = columns(to).map(&:name)
436
+ columns = columns.map { |c| rename[c] || c }.select do |column|
437
+ to_column_names.include?(column)
438
+ end
559
439
  end
560
440
 
561
441
  unless columns.empty?
562
442
  # index name can't be the same
563
443
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
564
444
  opts[:unique] = true if index.unique
445
+ opts[:where] = index.where if index.where
565
446
  add_index(to, columns, opts)
566
447
  end
567
448
  end
568
449
  end
569
450
 
570
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
571
- column_mappings = Hash[columns.map {|name| [name, name]}]
451
+ def copy_table_contents(from, to, columns, rename = {})
452
+ column_mappings = Hash[columns.map { |name| [name, name] }]
572
453
  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
454
+ from_columns = columns(from).collect(&:name)
455
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
456
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
457
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
458
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
459
+
460
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
461
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
592
462
  end
593
463
 
594
- def sqlite_version
595
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
596
- end
597
-
598
- def translate_exception(exception, message)
464
+ def translate_exception(exception, message:, sql:, binds:)
599
465
  case exception.message
600
466
  # SQLite 3.8.2 returns a newly formatted error message:
601
467
  # UNIQUE constraint failed: *table_name*.*column_name*
602
468
  # Older versions of SQLite return:
603
469
  # column *column_name* is not unique
604
470
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
605
- RecordNotUnique.new(message, exception)
471
+ RecordNotUnique.new(message, sql: sql, binds: binds)
472
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
473
+ NotNullViolation.new(message, sql: sql, binds: binds)
474
+ when /FOREIGN KEY constraint failed/i
475
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
606
476
  else
607
477
  super
608
478
  end
609
479
  end
480
+
481
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
482
+
483
+ def table_structure_with_collation(table_name, basic_structure)
484
+ collation_hash = {}
485
+ sql = <<~SQL
486
+ SELECT sql FROM
487
+ (SELECT * FROM sqlite_master UNION ALL
488
+ SELECT * FROM sqlite_temp_master)
489
+ WHERE type = 'table' AND name = #{quote(table_name)}
490
+ SQL
491
+
492
+ # Result will have following sample string
493
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
494
+ # "password_digest" varchar COLLATE "NOCASE");
495
+ result = exec_query(sql, "SCHEMA").first
496
+
497
+ if result
498
+ # Splitting with left parentheses and discarding the first part will return all
499
+ # columns separated with comma(,).
500
+ columns_string = result["sql"].split("(", 2).last
501
+
502
+ columns_string.split(",").each do |column_string|
503
+ # This regex will match the column name and collation type and will save
504
+ # the value in $1 and $2 respectively.
505
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
506
+ end
507
+
508
+ basic_structure.map! do |column|
509
+ column_name = column["name"]
510
+
511
+ if collation_hash.has_key? column_name
512
+ column["collation"] = collation_hash[column_name]
513
+ end
514
+
515
+ column
516
+ end
517
+ else
518
+ basic_structure.to_a
519
+ end
520
+ end
521
+
522
+ def arel_visitor
523
+ Arel::Visitors::SQLite.new(self)
524
+ end
525
+
526
+ def build_statement_pool
527
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
528
+ end
529
+
530
+ def connect
531
+ @connection = ::SQLite3::Database.new(
532
+ @config[:database].to_s,
533
+ @config.merge(results_as_hash: true)
534
+ )
535
+ configure_connection
536
+ end
537
+
538
+ def configure_connection
539
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
540
+
541
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
542
+ end
543
+
544
+ class SQLite3Integer < Type::Integer # :nodoc:
545
+ private
546
+ def _limit
547
+ # INTEGER storage class can be stored 8 bytes value.
548
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
549
+ limit || 8
550
+ end
551
+ end
552
+
553
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
610
554
  end
555
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
611
556
  end
612
557
  end