activerecord 4.2.0 → 6.0.5.1

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

Potentially problematic release.


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

Files changed (373) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +852 -801
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +267 -249
  9. data/lib/active_record/association_relation.rb +26 -6
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +137 -55
  12. data/lib/active_record/associations/association_scope.rb +110 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +67 -54
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -29
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +150 -275
  23. data/lib/active_record/associations/collection_proxy.rb +253 -152
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +35 -84
  26. data/lib/active_record/associations/has_many_through_association.rb +62 -80
  27. data/lib/active_record/associations/has_one_association.rb +62 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +159 -162
  33. data/lib/active_record/associations/preloader/association.rb +102 -113
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +96 -95
  36. data/lib/active_record/associations/singular_association.rb +18 -45
  37. data/lib/active_record/associations/through_association.rb +49 -24
  38. data/lib/active_record/associations.rb +1737 -1596
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -134
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
  48. data/lib/active_record/attribute_methods/write.rb +33 -56
  49. data/lib/active_record/attribute_methods.rb +124 -143
  50. data/lib/active_record/attributes.rb +213 -74
  51. data/lib/active_record/autosave_association.rb +125 -54
  52. data/lib/active_record/base.rb +60 -49
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +36 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  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 +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -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 +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  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 +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
  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 +119 -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 +102 -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 +299 -364
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +277 -233
  130. data/lib/active_record/counter_cache.rb +71 -50
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +172 -89
  138. data/lib/active_record/errors.rb +189 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +11 -6
  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 +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +225 -497
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  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 +48 -0
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +99 -98
  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/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +636 -290
  165. data/lib/active_record/model_schema.rb +344 -112
  166. data/lib/active_record/nested_attributes.rb +265 -215
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +559 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +166 -47
  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 +341 -202
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +461 -302
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +270 -249
  182. data/lib/active_record/relation/delegation.rb +76 -84
  183. data/lib/active_record/relation/finder_methods.rb +287 -255
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +86 -68
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +112 -92
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +612 -392
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +18 -17
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +533 -340
  201. data/lib/active_record/result.rb +79 -43
  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 -20
  207. data/lib/active_record/scoping/default.rb +98 -82
  208. data/lib/active_record/scoping/named.rb +91 -33
  209. data/lib/active_record/scoping.rb +45 -27
  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 +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +309 -99
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +243 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +222 -146
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -41
  228. data/lib/active_record/type/date_time.rb +4 -38
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +29 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +21 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +43 -46
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +44 -21
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -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/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -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/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -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/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +174 -63
  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 -149
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  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/integer.rb +0 -11
  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 -30
  362. data/lib/active_record/type/decimal.rb +0 -40
  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 -55
  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 -36
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -101
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  373. /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
+ 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,66 @@ module ActiveRecord
157
117
  end
158
118
 
159
119
  def supports_partial_index?
160
- sqlite_version >= '3.8.0'
120
+ true
161
121
  end
162
122
 
163
- # Returns true, since this connection adapter supports prepared statement
164
- # caching.
165
- def supports_statement_cache?
123
+ def supports_expression_index?
124
+ database_version >= "3.9.0"
125
+ end
126
+
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_common_table_expressions?
148
+ database_version >= "3.8.3"
149
+ end
150
+
151
+ def supports_insert_on_conflict?
152
+ database_version >= "3.24.0"
153
+ end
154
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
155
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
156
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
157
+
186
158
  def active?
187
- @active != false
159
+ !@connection.closed?
160
+ end
161
+
162
+ def reconnect!
163
+ super
164
+ connect if @connection.closed?
188
165
  end
189
166
 
190
167
  # Disconnects from the database if already connected. Otherwise, this
191
168
  # method does nothing.
192
169
  def disconnect!
193
170
  super
194
- @active = false
195
171
  @connection.close rescue nil
196
172
  end
197
173
 
198
- # Clears the prepared statements cache.
199
- def clear_cache!
200
- @statements.clear
201
- end
202
-
203
174
  def supports_index_sort_order?
204
175
  true
205
176
  end
206
177
 
207
178
  # Returns 62. SQLite supports index names up to 64
208
- # characters. The rest is used by rails internally to perform
179
+ # characters. The rest is used by Rails internally to perform
209
180
  # temporary rename operations
210
181
  def allowed_index_name_length
211
182
  index_name_length - 2
@@ -224,217 +195,43 @@ module ActiveRecord
224
195
  true
225
196
  end
226
197
 
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)
198
+ def supports_lazy_transactions?
199
+ true
249
200
  end
250
201
 
251
- def quote_table_name_for_assignment(table, attr)
252
- quote_column_name(attr)
253
- end
202
+ # REFERENTIAL INTEGRITY ====================================
254
203
 
255
- def quote_column_name(name) #:nodoc:
256
- %Q("#{name.to_s.gsub('"', '""')}")
257
- end
204
+ def disable_referential_integrity # :nodoc:
205
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
206
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
258
207
 
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
208
+ begin
209
+ execute("PRAGMA defer_foreign_keys = ON")
210
+ execute("PRAGMA foreign_keys = OFF")
211
+ yield
212
+ ensure
213
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
214
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
266
215
  end
267
216
  end
268
217
 
269
218
  #--
270
219
  # DATABASE STATEMENTS ======================================
271
220
  #++
272
-
273
221
  def explain(arel, binds = [])
274
222
  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 }
223
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
366
224
  end
367
225
 
368
226
  # SCHEMA STATEMENTS ========================================
369
227
 
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?
228
+ def primary_keys(table_name) # :nodoc:
229
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
230
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
385
231
  end
386
232
 
387
- # Returns an array of +Column+ objects for the table specified by +table_name+.
388
- def columns(table_name) #:nodoc:
389
- table_structure(table_name).map do |field|
390
- case field["dflt_value"]
391
- when /^null$/i
392
- field["dflt_value"] = nil
393
- when /^'(.*)'$/m
394
- field["dflt_value"] = $1.gsub("''", "'")
395
- when /^"(.*)"$/m
396
- field["dflt_value"] = $1.gsub('""', '"')
397
- end
398
-
399
- sql_type = field['type']
400
- cast_type = lookup_cast_type(sql_type)
401
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
402
- end
403
- end
404
-
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:
233
+ def remove_index(table_name, options = {}) #:nodoc:
234
+ index_name = index_name_for_remove(table_name, options)
438
235
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
439
236
  end
440
237
 
@@ -447,35 +244,34 @@ module ActiveRecord
447
244
  rename_table_indexes(table_name, new_name)
448
245
  end
449
246
 
450
- # See: http://www.sqlite.org/lang_altertable.html
451
- # SQLite has an additional restriction on the ALTER TABLE statement
452
- def valid_alter_table_type?(type)
453
- type.to_sym != :primary_key
454
- end
455
-
456
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
457
- if valid_alter_table_type?(type)
458
- super(table_name, column_name, type, options)
459
- else
247
+ def add_column(table_name, column_name, type, **options) #:nodoc:
248
+ if invalid_alter_table_type?(type, options)
460
249
  alter_table(table_name) do |definition|
461
- definition.column(column_name, type, options)
250
+ definition.column(column_name, type, **options)
462
251
  end
252
+ else
253
+ super
463
254
  end
464
255
  end
465
256
 
466
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
257
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
467
258
  alter_table(table_name) do |definition|
468
259
  definition.remove_column column_name
260
+ definition.foreign_keys.delete_if do |_, fk_options|
261
+ fk_options[:column] == column_name.to_s
262
+ end
469
263
  end
470
264
  end
471
265
 
472
- def change_column_default(table_name, column_name, default) #:nodoc:
266
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
267
+ default = extract_new_default_value(default_or_changes)
268
+
473
269
  alter_table(table_name) do |definition|
474
270
  definition[column_name].default = default
475
271
  end
476
272
  end
477
273
 
478
- def change_column_null(table_name, column_name, null, default = nil)
274
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
479
275
  unless null || default.nil?
480
276
  exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
481
277
  end
@@ -486,81 +282,152 @@ module ActiveRecord
486
282
 
487
283
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
488
284
  alter_table(table_name) do |definition|
489
- include_default = options_include_default?(options)
490
285
  definition[column_name].instance_eval do
491
- self.type = type
286
+ self.type = aliased_types(type.to_s, type)
492
287
  self.limit = options[:limit] if options.include?(:limit)
493
- self.default = options[:default] if include_default
288
+ self.default = options[:default] if options.include?(:default)
494
289
  self.null = options[:null] if options.include?(:null)
495
290
  self.precision = options[:precision] if options.include?(:precision)
496
- self.scale = options[:scale] if options.include?(:scale)
291
+ self.scale = options[:scale] if options.include?(:scale)
292
+ self.collation = options[:collation] if options.include?(:collation)
293
+ self.options.merge!(options)
497
294
  end
498
295
  end
499
296
  end
500
297
 
501
298
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
502
299
  column = column_for(table_name, column_name)
503
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
300
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
504
301
  rename_column_indexes(table_name, column.name, new_column_name)
505
302
  end
506
303
 
507
- protected
304
+ def add_reference(table_name, ref_name, **options) # :nodoc:
305
+ super(table_name, ref_name, type: :integer, **options)
306
+ end
307
+ alias :add_belongs_to :add_reference
308
+
309
+ def foreign_keys(table_name)
310
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
311
+ fk_info.map do |row|
312
+ options = {
313
+ column: row["from"],
314
+ primary_key: row["to"],
315
+ on_delete: extract_foreign_key_action(row["on_delete"]),
316
+ on_update: extract_foreign_key_action(row["on_update"])
317
+ }
318
+ ForeignKeyDefinition.new(table_name, row["table"], options)
319
+ end
320
+ end
321
+
322
+ def build_insert_sql(insert) # :nodoc:
323
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
324
+
325
+ if insert.skip_duplicates?
326
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
327
+ elsif insert.update_duplicates?
328
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
329
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
330
+ end
331
+
332
+ sql
333
+ end
334
+
335
+ def get_database_version # :nodoc:
336
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
337
+ end
338
+
339
+ def check_version # :nodoc:
340
+ if database_version < "3.8.0"
341
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
342
+ end
343
+ end
344
+
345
+ private
346
+ # See https://www.sqlite.org/limits.html,
347
+ # the default value is 999 when not configured.
348
+ def bind_params_length
349
+ 999
350
+ end
508
351
 
509
- def initialize_type_map(m)
352
+ def initialize_type_map(m = type_map)
510
353
  super
511
- m.register_type(/binary/i, SQLite3Binary.new)
512
- register_class_with_limit m, %r(char)i, SQLite3String
354
+ register_class_with_limit m, %r(int)i, SQLite3Integer
513
355
  end
514
356
 
515
357
  def table_structure(table_name)
516
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
358
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
517
359
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
518
- structure
360
+ table_structure_with_collation(table_name, structure)
519
361
  end
362
+ alias column_definitions table_structure
520
363
 
521
- def alter_table(table_name, options = {}) #:nodoc:
364
+ # See: https://www.sqlite.org/lang_altertable.html
365
+ # SQLite has an additional restriction on the ALTER TABLE statement
366
+ def invalid_alter_table_type?(type, options)
367
+ type.to_sym == :primary_key || options[:primary_key] ||
368
+ options[:null] == false && options[:default].nil?
369
+ end
370
+
371
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
522
372
  altered_table_name = "a#{table_name}"
523
- caller = lambda {|definition| yield definition if block_given?}
373
+
374
+ caller = lambda do |definition|
375
+ rename = options[:rename] || {}
376
+ foreign_keys.each do |fk|
377
+ if column = rename[fk.options[:column]]
378
+ fk.options[:column] = column
379
+ end
380
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
381
+ definition.foreign_key(to_table, **fk.options)
382
+ end
383
+
384
+ yield definition if block_given?
385
+ end
524
386
 
525
387
  transaction do
526
- move_table(table_name, altered_table_name,
527
- options.merge(:temporary => true))
528
- move_table(altered_table_name, table_name, &caller)
388
+ disable_referential_integrity do
389
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
390
+ move_table(altered_table_name, table_name, &caller)
391
+ end
529
392
  end
530
393
  end
531
394
 
532
- def move_table(from, to, options = {}, &block) #:nodoc:
395
+ def move_table(from, to, options = {}, &block)
533
396
  copy_table(from, to, options, &block)
534
397
  drop_table(from)
535
398
  end
536
399
 
537
- def copy_table(from, to, options = {}) #:nodoc:
400
+ def copy_table(from, to, options = {})
538
401
  from_primary_key = primary_key(from)
539
402
  options[:id] = false
540
- create_table(to, options) do |definition|
403
+ create_table(to, **options) do |definition|
541
404
  @definition = definition
542
- @definition.primary_key(from_primary_key) if from_primary_key.present?
405
+ if from_primary_key.is_a?(Array)
406
+ @definition.primary_keys from_primary_key
407
+ end
543
408
  columns(from).each do |column|
544
409
  column_name = options[:rename] ?
545
410
  (options[:rename][column.name] ||
546
411
  options[:rename][column.name.to_sym] ||
547
412
  column.name) : column.name
548
- next if column_name == from_primary_key
549
413
 
550
414
  @definition.column(column_name, column.type,
551
- :limit => column.limit, :default => column.default,
552
- :precision => column.precision, :scale => column.scale,
553
- :null => column.null)
415
+ limit: column.limit, default: column.default,
416
+ precision: column.precision, scale: column.scale,
417
+ null: column.null, collation: column.collation,
418
+ primary_key: column_name == from_primary_key
419
+ )
554
420
  end
421
+
555
422
  yield @definition if block_given?
556
423
  end
557
424
  copy_table_indexes(from, to, options[:rename] || {})
558
425
  copy_table_contents(from, to,
559
- @definition.columns.map {|column| column.name},
426
+ @definition.columns.map(&:name),
560
427
  options[:rename] || {})
561
428
  end
562
429
 
563
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
430
+ def copy_table_indexes(from, to, rename = {})
564
431
  indexes(from).each do |index|
565
432
  name = index.name
566
433
  if to == "a#{from}"
@@ -569,60 +436,128 @@ module ActiveRecord
569
436
  name = name[1..-1]
570
437
  end
571
438
 
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)
439
+ columns = index.columns
440
+ if columns.is_a?(Array)
441
+ to_column_names = columns(to).map(&:name)
442
+ columns = columns.map { |c| rename[c] || c }.select do |column|
443
+ to_column_names.include?(column)
444
+ end
575
445
  end
576
446
 
577
447
  unless columns.empty?
578
448
  # index name can't be the same
579
449
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
580
450
  opts[:unique] = true if index.unique
451
+ opts[:where] = index.where if index.where
581
452
  add_index(to, columns, opts)
582
453
  end
583
454
  end
584
455
  end
585
456
 
586
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
587
- column_mappings = Hash[columns.map {|name| [name, name]}]
457
+ def copy_table_contents(from, to, columns, rename = {})
458
+ column_mappings = Hash[columns.map { |name| [name, name] }]
588
459
  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] }]
460
+ from_columns = columns(from).collect(&:name)
461
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
462
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
463
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
464
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
596
465
 
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(*)'))
466
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
467
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
612
468
  end
613
469
 
614
- def translate_exception(exception, message)
470
+ def translate_exception(exception, message:, sql:, binds:)
615
471
  case exception.message
616
472
  # SQLite 3.8.2 returns a newly formatted error message:
617
473
  # UNIQUE constraint failed: *table_name*.*column_name*
618
474
  # Older versions of SQLite return:
619
475
  # column *column_name* is not unique
620
476
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
621
- RecordNotUnique.new(message, exception)
477
+ RecordNotUnique.new(message, sql: sql, binds: binds)
478
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
479
+ NotNullViolation.new(message, sql: sql, binds: binds)
480
+ when /FOREIGN KEY constraint failed/i
481
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
622
482
  else
623
483
  super
624
484
  end
625
485
  end
486
+
487
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
488
+
489
+ def table_structure_with_collation(table_name, basic_structure)
490
+ collation_hash = {}
491
+ sql = <<~SQL
492
+ SELECT sql FROM
493
+ (SELECT * FROM sqlite_master UNION ALL
494
+ SELECT * FROM sqlite_temp_master)
495
+ WHERE type = 'table' AND name = #{quote(table_name)}
496
+ SQL
497
+
498
+ # Result will have following sample string
499
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
500
+ # "password_digest" varchar COLLATE "NOCASE");
501
+ result = exec_query(sql, "SCHEMA").first
502
+
503
+ if result
504
+ # Splitting with left parentheses and discarding the first part will return all
505
+ # columns separated with comma(,).
506
+ columns_string = result["sql"].split("(", 2).last
507
+
508
+ columns_string.split(",").each do |column_string|
509
+ # This regex will match the column name and collation type and will save
510
+ # the value in $1 and $2 respectively.
511
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
512
+ end
513
+
514
+ basic_structure.map! do |column|
515
+ column_name = column["name"]
516
+
517
+ if collation_hash.has_key? column_name
518
+ column["collation"] = collation_hash[column_name]
519
+ end
520
+
521
+ column
522
+ end
523
+ else
524
+ basic_structure.to_a
525
+ end
526
+ end
527
+
528
+ def arel_visitor
529
+ Arel::Visitors::SQLite.new(self)
530
+ end
531
+
532
+ def build_statement_pool
533
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
534
+ end
535
+
536
+ def connect
537
+ @connection = ::SQLite3::Database.new(
538
+ @config[:database].to_s,
539
+ @config.merge(results_as_hash: true)
540
+ )
541
+ configure_connection
542
+ end
543
+
544
+ def configure_connection
545
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
546
+
547
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
548
+ end
549
+
550
+ class SQLite3Integer < Type::Integer # :nodoc:
551
+ private
552
+ def _limit
553
+ # INTEGER storage class can be stored 8 bytes value.
554
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
555
+ limit || 8
556
+ end
557
+ end
558
+
559
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
626
560
  end
561
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
627
562
  end
628
563
  end