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