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