activerecord 4.2.11.3 → 6.0.2.2

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

Potentially problematic release.


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

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1587
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,40 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
- class StatementPool
5
+ class StatementPool # :nodoc:
4
6
  include Enumerable
5
7
 
6
- def initialize(connection, max = 1000)
7
- @connection = connection
8
- @max = max
8
+ DEFAULT_STATEMENT_LIMIT = 1000
9
+
10
+ def initialize(statement_limit = nil)
11
+ @cache = Hash.new { |h, pid| h[pid] = {} }
12
+ @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
9
13
  end
10
14
 
11
- def each
12
- raise NotImplementedError
15
+ def each(&block)
16
+ cache.each(&block)
13
17
  end
14
18
 
15
19
  def key?(key)
16
- raise NotImplementedError
20
+ cache.key?(key)
17
21
  end
18
22
 
19
23
  def [](key)
20
- raise NotImplementedError
24
+ cache[key]
21
25
  end
22
26
 
23
27
  def length
24
- raise NotImplementedError
28
+ cache.length
25
29
  end
26
30
 
27
- def []=(sql, key)
28
- raise NotImplementedError
31
+ def []=(sql, stmt)
32
+ while @statement_limit <= cache.size
33
+ dealloc(cache.shift.last)
34
+ end
35
+ cache[sql] = stmt
29
36
  end
30
37
 
31
38
  def clear
32
- raise NotImplementedError
39
+ cache.each_value do |stmt|
40
+ dealloc stmt
41
+ end
42
+ cache.clear
33
43
  end
34
44
 
35
45
  def delete(key)
36
- raise NotImplementedError
46
+ dealloc cache[key]
47
+ cache.delete(key)
37
48
  end
49
+
50
+ private
51
+
52
+ def cache
53
+ @cache[Process.pid]
54
+ end
55
+
56
+ def dealloc(stmt)
57
+ raise NotImplementedError
58
+ end
38
59
  end
39
60
  end
40
61
  end
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionHandling
3
- RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
5
+ RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
4
6
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
5
7
 
6
8
  # Establishes the connection to the database. Accepts a hash as input where
7
9
  # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
8
- # example for regular databases (MySQL, Postgresql, etc):
10
+ # example for regular databases (MySQL, PostgreSQL, etc):
9
11
  #
10
12
  # ActiveRecord::Base.establish_connection(
11
- # adapter: "mysql",
13
+ # adapter: "mysql2",
12
14
  # host: "localhost",
13
15
  # username: "myuser",
14
16
  # password: "mypass",
@@ -35,49 +37,166 @@ module ActiveRecord
35
37
  # "postgres://myuser:mypass@localhost/somedatabase"
36
38
  # )
37
39
  #
38
- # In case <tt>ActiveRecord::Base.configurations</tt> is set (Rails
39
- # automatically loads the contents of config/database.yml into it),
40
+ # In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]
41
+ # is set (Rails automatically loads the contents of config/database.yml into it),
40
42
  # a symbol can also be given as argument, representing a key in the
41
43
  # configuration hash:
42
44
  #
43
45
  # ActiveRecord::Base.establish_connection(:production)
44
46
  #
45
- # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
47
+ # The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
46
48
  # may be returned on an error.
47
- def establish_connection(spec = nil)
48
- spec ||= DEFAULT_ENV.call.to_sym
49
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
50
- spec = resolver.spec(spec)
49
+ def establish_connection(config_or_env = nil)
50
+ config_hash = resolve_config_for_connection(config_or_env)
51
+ connection_handler.establish_connection(config_hash)
52
+ end
53
+
54
+ # Connects a model to the databases specified. The +database+ keyword
55
+ # takes a hash consisting of a +role+ and a +database_key+.
56
+ #
57
+ # This will create a connection handler for switching between connections,
58
+ # look up the config hash using the +database_key+ and finally
59
+ # establishes a connection to that config.
60
+ #
61
+ # class AnimalsModel < ApplicationRecord
62
+ # self.abstract_class = true
63
+ #
64
+ # connects_to database: { writing: :primary, reading: :primary_replica }
65
+ # end
66
+ #
67
+ # Returns an array of established connections.
68
+ def connects_to(database: {})
69
+ connections = []
51
70
 
52
- unless respond_to?(spec.adapter_method)
53
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
71
+ database.each do |role, database_key|
72
+ config_hash = resolve_config_for_connection(database_key)
73
+ handler = lookup_connection_handler(role.to_sym)
74
+
75
+ connections << handler.establish_connection(config_hash)
54
76
  end
55
77
 
56
- remove_connection
57
- connection_handler.establish_connection self, spec
78
+ connections
58
79
  end
59
80
 
60
- class MergeAndResolveDefaultUrlConfig # :nodoc:
61
- def initialize(raw_configurations)
62
- @raw_config = raw_configurations.dup
63
- @env = DEFAULT_ENV.call.to_s
64
- end
81
+ # Connects to a database or role (ex writing, reading, or another
82
+ # custom role) for the duration of the block.
83
+ #
84
+ # If a role is passed, Active Record will look up the connection
85
+ # based on the requested role:
86
+ #
87
+ # ActiveRecord::Base.connected_to(role: :writing) do
88
+ # Dog.create! # creates dog using dog writing connection
89
+ # end
90
+ #
91
+ # ActiveRecord::Base.connected_to(role: :reading) do
92
+ # Dog.create! # throws exception because we're on a replica
93
+ # end
94
+ #
95
+ # ActiveRecord::Base.connected_to(role: :unknown_role) do
96
+ # # raises exception due to non-existent role
97
+ # end
98
+ #
99
+ # For cases where you may want to connect to a database outside of the model,
100
+ # you can use +connected_to+ with a +database+ argument. The +database+ argument
101
+ # expects a symbol that corresponds to the database key in your config.
102
+ #
103
+ # ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
104
+ # Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
105
+ # end
106
+ #
107
+ # This will connect to a new database for the queries inside the block. By
108
+ # default the `:writing` role will be used since all connections must be assigned
109
+ # a role. If you would like to use a different role you can pass a hash to database:
110
+ #
111
+ # ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
112
+ # # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
113
+ # Dog.run_a_long_query
114
+ # end
115
+ #
116
+ # When using the database key a new connection will be established every time. It is not
117
+ # recommended to use this outside of one-off scripts.
118
+ def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
119
+ if database && role
120
+ raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
121
+ elsif database
122
+ if database.is_a?(Hash)
123
+ role, database = database.first
124
+ role = role.to_sym
125
+ end
65
126
 
66
- # Returns fully resolved connection hashes.
67
- # Merges connection information from `ENV['DATABASE_URL']` if available.
68
- def resolve
69
- ConnectionAdapters::ConnectionSpecification::Resolver.new(config).resolve_all
70
- end
127
+ config_hash = resolve_config_for_connection(database)
128
+ handler = lookup_connection_handler(role)
129
+
130
+ handler.establish_connection(config_hash)
71
131
 
72
- private
73
- def config
74
- @raw_config.dup.tap do |cfg|
75
- if url = ENV['DATABASE_URL']
76
- cfg[@env] ||= {}
77
- cfg[@env]["url"] ||= url
78
- end
132
+ with_handler(role, &blk)
133
+ elsif role
134
+ if role == writing_role
135
+ with_handler(role.to_sym) do
136
+ connection_handler.while_preventing_writes(prevent_writes, &blk)
79
137
  end
138
+ else
139
+ with_handler(role.to_sym, &blk)
80
140
  end
141
+ else
142
+ raise ArgumentError, "must provide a `database` or a `role`."
143
+ end
144
+ end
145
+
146
+ # Returns true if role is the current connected role.
147
+ #
148
+ # ActiveRecord::Base.connected_to(role: :writing) do
149
+ # ActiveRecord::Base.connected_to?(role: :writing) #=> true
150
+ # ActiveRecord::Base.connected_to?(role: :reading) #=> false
151
+ # end
152
+ def connected_to?(role:)
153
+ current_role == role.to_sym
154
+ end
155
+
156
+ # Returns the symbol representing the current connected role.
157
+ #
158
+ # ActiveRecord::Base.connected_to(role: :writing) do
159
+ # ActiveRecord::Base.current_role #=> :writing
160
+ # end
161
+ #
162
+ # ActiveRecord::Base.connected_to(role: :reading) do
163
+ # ActiveRecord::Base.current_role #=> :reading
164
+ # end
165
+ def current_role
166
+ connection_handlers.key(connection_handler)
167
+ end
168
+
169
+ def lookup_connection_handler(handler_key) # :nodoc:
170
+ handler_key ||= ActiveRecord::Base.writing_role
171
+ connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
172
+ end
173
+
174
+ def with_handler(handler_key, &blk) # :nodoc:
175
+ handler = lookup_connection_handler(handler_key)
176
+ swap_connection_handler(handler, &blk)
177
+ end
178
+
179
+ def resolve_config_for_connection(config_or_env) # :nodoc:
180
+ raise "Anonymous class is not allowed." unless name
181
+
182
+ config_or_env ||= DEFAULT_ENV.call.to_sym
183
+ pool_name = primary_class? ? "primary" : name
184
+ self.connection_specification_name = pool_name
185
+
186
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
187
+ config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
188
+ config_hash[:name] = pool_name
189
+
190
+ config_hash
191
+ end
192
+
193
+ # Clears the query cache for all connections associated with the current thread.
194
+ def clear_query_caches_for_current_thread
195
+ ActiveRecord::Base.connection_handlers.each_value do |handler|
196
+ handler.connection_pool_list.each do |pool|
197
+ pool.connection.clear_query_cache if pool.active_connection?
198
+ end
199
+ end
81
200
  end
82
201
 
83
202
  # Returns the connection currently associated with the class. This can
@@ -87,12 +206,18 @@ module ActiveRecord
87
206
  retrieve_connection
88
207
  end
89
208
 
90
- def connection_id
91
- ActiveRecord::RuntimeRegistry.connection_id
209
+ attr_writer :connection_specification_name
210
+
211
+ # Return the specification name from the current class or its parent.
212
+ def connection_specification_name
213
+ if !defined?(@connection_specification_name) || @connection_specification_name.nil?
214
+ return self == Base ? "primary" : superclass.connection_specification_name
215
+ end
216
+ @connection_specification_name
92
217
  end
93
218
 
94
- def connection_id=(connection_id)
95
- ActiveRecord::RuntimeRegistry.connection_id = connection_id
219
+ def primary_class? # :nodoc:
220
+ self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
96
221
  end
97
222
 
98
223
  # Returns the configuration of the associated connection as a hash:
@@ -106,20 +231,28 @@ module ActiveRecord
106
231
  end
107
232
 
108
233
  def connection_pool
109
- connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
234
+ connection_handler.retrieve_connection_pool(connection_specification_name) || raise(ConnectionNotEstablished)
110
235
  end
111
236
 
112
237
  def retrieve_connection
113
- connection_handler.retrieve_connection(self)
238
+ connection_handler.retrieve_connection(connection_specification_name)
114
239
  end
115
240
 
116
241
  # Returns +true+ if Active Record is connected.
117
242
  def connected?
118
- connection_handler.connected?(self)
243
+ connection_handler.connected?(connection_specification_name)
119
244
  end
120
245
 
121
- def remove_connection(klass = self)
122
- connection_handler.remove_connection(klass)
246
+ def remove_connection(name = nil)
247
+ name ||= @connection_specification_name if defined?(@connection_specification_name)
248
+ # if removing a connection that has a pool, we reset the
249
+ # connection_specification_name so it will use the parent
250
+ # pool.
251
+ if connection_handler.retrieve_connection_pool(name)
252
+ self.connection_specification_name = nil
253
+ end
254
+
255
+ connection_handler.remove_connection(name)
123
256
  end
124
257
 
125
258
  def clear_cache! # :nodoc:
@@ -127,6 +260,15 @@ module ActiveRecord
127
260
  end
128
261
 
129
262
  delegate :clear_active_connections!, :clear_reloadable_connections!,
130
- :clear_all_connections!, :to => :connection_handler
263
+ :clear_all_connections!, :flush_idle_connections!, to: :connection_handler
264
+
265
+ private
266
+
267
+ def swap_connection_handler(handler, &blk) # :nodoc:
268
+ old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
269
+ yield
270
+ ensure
271
+ ActiveRecord::Base.connection_handler = old_handler
272
+ end
131
273
  end
132
274
  end
@@ -1,7 +1,9 @@
1
- require 'thread'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/core_ext/object/duplicable'
4
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+ require "active_support/core_ext/string/filters"
5
+ require "active_support/parameter_filter"
6
+ require "concurrent/map"
5
7
 
6
8
  module ActiveRecord
7
9
  module Core
@@ -16,9 +18,16 @@ module ActiveRecord
16
18
  # retrieved on both a class and instance level by calling +logger+.
17
19
  mattr_accessor :logger, instance_writer: false
18
20
 
21
+ ##
22
+ # :singleton-method:
23
+ #
24
+ # Specifies if the methods calling database queries should be logged below
25
+ # their relevant queries. Defaults to false.
26
+ mattr_accessor :verbose_query_logs, instance_writer: false, default: false
27
+
19
28
  ##
20
29
  # Contains the database configuration - as is typically stored in config/database.yml -
21
- # as a Hash.
30
+ # as an ActiveRecord::DatabaseConfigurations object.
22
31
  #
23
32
  # For example, the following database.yml...
24
33
  #
@@ -32,22 +41,18 @@ module ActiveRecord
32
41
  #
33
42
  # ...would result in ActiveRecord::Base.configurations to look like this:
34
43
  #
35
- # {
36
- # 'development' => {
37
- # 'adapter' => 'sqlite3',
38
- # 'database' => 'db/development.sqlite3'
39
- # },
40
- # 'production' => {
41
- # 'adapter' => 'sqlite3',
42
- # 'database' => 'db/production.sqlite3'
43
- # }
44
- # }
44
+ # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
+ # @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
47
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
+ # @spec_name="primary", @config={"adapter"=>"mysql2", "database"=>"db/production.sqlite3"}>
49
+ # ]>
45
50
  def self.configurations=(config)
46
- @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
51
+ @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
47
52
  end
48
53
  self.configurations = {}
49
54
 
50
- # Returns fully resolved configurations hash
55
+ # Returns fully resolved ActiveRecord::DatabaseConfigurations object
51
56
  def self.configurations
52
57
  @@configurations
53
58
  end
@@ -56,8 +61,7 @@ module ActiveRecord
56
61
  # :singleton-method:
57
62
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
58
63
  # dates and times from the database. This is set to :utc by default.
59
- mattr_accessor :default_timezone, instance_writer: false
60
- self.default_timezone = :utc
64
+ mattr_accessor :default_timezone, instance_writer: false, default: :utc
61
65
 
62
66
  ##
63
67
  # :singleton-method:
@@ -67,58 +71,85 @@ module ActiveRecord
67
71
  # ActiveRecord::Schema file which can be loaded into any database that
68
72
  # supports migrations. Use :ruby if you want to have different database
69
73
  # adapters for, e.g., your development and test environments.
70
- mattr_accessor :schema_format, instance_writer: false
71
- self.schema_format = :ruby
74
+ mattr_accessor :schema_format, instance_writer: false, default: :ruby
75
+
76
+ ##
77
+ # :singleton-method:
78
+ # Specifies if an error should be raised if the query has an order being
79
+ # ignored when doing batch queries. Useful in applications where the
80
+ # scope being ignored is error-worthy, rather than a warning.
81
+ mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
82
+
83
+ # :singleton-method:
84
+ # Specify the behavior for unsafe raw query methods. Values are as follows
85
+ # deprecated - Warnings are logged when unsafe raw SQL is passed to
86
+ # query methods.
87
+ # disabled - Unsafe raw SQL passed to query methods results in
88
+ # UnknownAttributeReference exception.
89
+ mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :deprecated
72
90
 
73
91
  ##
74
92
  # :singleton-method:
75
93
  # Specify whether or not to use timestamps for migration versions
76
- mattr_accessor :timestamped_migrations, instance_writer: false
77
- self.timestamped_migrations = true
94
+ mattr_accessor :timestamped_migrations, instance_writer: false, default: true
78
95
 
79
96
  ##
80
97
  # :singleton-method:
81
98
  # Specify whether schema dump should happen at the end of the
82
- # db:migrate rake task. This is true by default, which is useful for the
99
+ # db:migrate rails command. This is true by default, which is useful for the
83
100
  # development environment. This should ideally be false in the production
84
101
  # environment where dumping schema is rarely needed.
85
- mattr_accessor :dump_schema_after_migration, instance_writer: false
86
- self.dump_schema_after_migration = true
102
+ mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
103
+
104
+ ##
105
+ # :singleton-method:
106
+ # Specifies which database schemas to dump when calling db:structure:dump.
107
+ # If the value is :schema_search_path (the default), any schemas listed in
108
+ # schema_search_path are dumped. Use :all to dump all schemas regardless
109
+ # of schema_search_path, or a string of comma separated schemas for a
110
+ # custom list.
111
+ mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
112
+
113
+ ##
114
+ # :singleton-method:
115
+ # Specify a threshold for the size of query result sets. If the number of
116
+ # records in the set exceeds the threshold, a warning is logged. This can
117
+ # be used to identify queries which load thousands of records and
118
+ # potentially cause memory bloat.
119
+ mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
87
120
 
88
121
  mattr_accessor :maintain_test_schema, instance_accessor: false
89
122
 
90
- def self.disable_implicit_join_references=(value)
91
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
92
- Implicit join references were removed with Rails 4.1.
93
- Make sure to remove this configuration because it does nothing.
94
- MSG
95
- end
123
+ mattr_accessor :belongs_to_required_by_default, instance_accessor: false
124
+
125
+ mattr_accessor :connection_handlers, instance_accessor: false, default: {}
126
+
127
+ mattr_accessor :writing_role, instance_accessor: false, default: :writing
128
+
129
+ mattr_accessor :reading_role, instance_accessor: false, default: :reading
96
130
 
97
131
  class_attribute :default_connection_handler, instance_writer: false
98
- class_attribute :find_by_statement_cache
132
+
133
+ self.filter_attributes = []
99
134
 
100
135
  def self.connection_handler
101
- ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
136
+ Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
102
137
  end
103
138
 
104
139
  def self.connection_handler=(handler)
105
- ActiveRecord::RuntimeRegistry.connection_handler = handler
140
+ Thread.current.thread_variable_set("ar_connection_handler", handler)
106
141
  end
107
142
 
108
143
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
109
144
  end
110
145
 
111
146
  module ClassMethods
112
- def allocate
113
- define_attribute_methods
114
- super
115
- end
116
-
117
147
  def initialize_find_by_cache # :nodoc:
118
- self.find_by_statement_cache = {}.extend(Mutex_m)
148
+ @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
119
149
  end
120
150
 
121
151
  def inherited(child_class) # :nodoc:
152
+ # initialize cache at class definition for thread safety
122
153
  child_class.initialize_find_by_cache
123
154
  super
124
155
  end
@@ -126,90 +157,88 @@ module ActiveRecord
126
157
  def find(*ids) # :nodoc:
127
158
  # We don't have cache keys for this stuff yet
128
159
  return super unless ids.length == 1
129
- # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
130
- return super if ids.first.kind_of?(Symbol)
131
160
  return super if block_given? ||
132
161
  primary_key.nil? ||
133
- default_scopes.any? ||
134
- current_scope ||
135
- columns_hash.include?(inheritance_column) ||
136
- ids.first.kind_of?(Array)
137
-
138
- id = ids.first
139
- if ActiveRecord::Base === id
140
- id = id.id
141
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
142
- You are passing an instance of ActiveRecord::Base to `find`.
143
- Please pass the id of the object by calling `.id`
144
- MSG
145
- end
162
+ scope_attributes? ||
163
+ columns_hash.key?(inheritance_column) && !base_class?
164
+
165
+ id = ids.first
166
+
167
+ return super if StatementCache.unsupported_value?(id)
168
+
146
169
  key = primary_key
147
170
 
148
- s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
149
- find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
150
- where(key => params.bind).limit(1)
151
- }
171
+ statement = cached_find_by_statement(key) { |params|
172
+ where(key => params.bind).limit(1)
152
173
  }
153
- record = s.execute([id], self, connection).first
174
+
175
+ record = statement.execute([id], connection)&.first
154
176
  unless record
155
- raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
177
+ raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
156
178
  end
157
179
  record
158
- rescue RangeError
159
- raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
160
180
  end
161
181
 
162
182
  def find_by(*args) # :nodoc:
163
- return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
164
- return super if default_scopes.any?
183
+ return super if scope_attributes? || reflect_on_all_aggregations.any? ||
184
+ columns_hash.key?(inheritance_column) && !base_class?
165
185
 
166
186
  hash = args.first
167
187
 
168
- return super if hash.values.any? { |v|
169
- v.nil? || Array === v || Hash === v
188
+ return super if !(Hash === hash) || hash.values.any? { |v|
189
+ StatementCache.unsupported_value?(v)
170
190
  }
171
191
 
172
192
  # We can't cache Post.find_by(author: david) ...yet
173
193
  return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
174
194
 
175
- key = hash.keys
195
+ keys = hash.keys
176
196
 
177
- klass = self
178
- s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
179
- find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
180
- wheres = key.each_with_object({}) { |param,o|
181
- o[param] = params.bind
182
- }
183
- klass.where(wheres).limit(1)
197
+ statement = cached_find_by_statement(keys) { |params|
198
+ wheres = keys.each_with_object({}) { |param, o|
199
+ o[param] = params.bind
184
200
  }
201
+ where(wheres).limit(1)
185
202
  }
186
203
  begin
187
- s.execute(hash.values, self, connection).first
188
- rescue TypeError => e
189
- raise ActiveRecord::StatementInvalid.new(e.message, e)
190
- rescue RangeError
191
- nil
204
+ statement.execute(hash.values, connection)&.first
205
+ rescue TypeError
206
+ raise ActiveRecord::StatementInvalid
192
207
  end
193
208
  end
194
209
 
195
210
  def find_by!(*args) # :nodoc:
196
- find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
211
+ find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
197
212
  end
198
213
 
199
214
  def initialize_generated_modules # :nodoc:
200
215
  generated_association_methods
201
216
  end
202
217
 
203
- def generated_association_methods
218
+ def generated_association_methods # :nodoc:
204
219
  @generated_association_methods ||= begin
205
220
  mod = const_set(:GeneratedAssociationMethods, Module.new)
221
+ private_constant :GeneratedAssociationMethods
206
222
  include mod
223
+
207
224
  mod
208
225
  end
209
226
  end
210
227
 
228
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
229
+ def filter_attributes
230
+ if defined?(@filter_attributes)
231
+ @filter_attributes
232
+ else
233
+ superclass.filter_attributes
234
+ end
235
+ end
236
+
237
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
238
+ attr_writer :filter_attributes
239
+
211
240
  # Returns a string like 'Post(id:integer, title:string, body:text)'
212
- def inspect
241
+ def inspect # :nodoc:
213
242
  if self == Base
214
243
  super
215
244
  elsif abstract_class?
@@ -217,48 +246,66 @@ module ActiveRecord
217
246
  elsif !connected?
218
247
  "#{super} (call '#{super}.connection' to establish a connection)"
219
248
  elsif table_exists?
220
- attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
249
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
221
250
  "#{super}(#{attr_list})"
222
251
  else
223
252
  "#{super}(Table doesn't exist)"
224
253
  end
225
254
  end
226
255
 
227
- # Overwrite the default class equality method to provide support for association proxies.
228
- def ===(object)
256
+ # Overwrite the default class equality method to provide support for decorated models.
257
+ def ===(object) # :nodoc:
229
258
  object.is_a?(self)
230
259
  end
231
260
 
232
261
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
233
262
  #
234
263
  # class Post < ActiveRecord::Base
235
- # scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
264
+ # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
236
265
  # end
237
266
  def arel_table # :nodoc:
238
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
267
+ @arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
239
268
  end
240
269
 
241
- # Returns the Arel engine.
242
- def arel_engine # :nodoc:
243
- @arel_engine ||=
244
- if Base == self || connection_handler.retrieve_connection_pool(self)
245
- self
246
- else
247
- superclass.arel_engine
248
- end
270
+ def arel_attribute(name, table = arel_table) # :nodoc:
271
+ name = name.to_s
272
+ name = attribute_aliases[name] || name
273
+ table[name]
274
+ end
275
+
276
+ def predicate_builder # :nodoc:
277
+ @predicate_builder ||= PredicateBuilder.new(table_metadata)
278
+ end
279
+
280
+ def type_caster # :nodoc:
281
+ TypeCaster::Map.new(self)
282
+ end
283
+
284
+ def _internal? # :nodoc:
285
+ false
249
286
  end
250
287
 
251
288
  private
252
289
 
253
- def relation #:nodoc:
254
- relation = Relation.create(self, arel_table)
290
+ def cached_find_by_statement(key, &block)
291
+ cache = @find_by_statement_cache[connection.prepared_statements]
292
+ cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
293
+ end
294
+
295
+ def relation
296
+ relation = Relation.create(self)
255
297
 
256
- if finder_needs_type_condition?
257
- relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
258
- else
259
- relation
298
+ if finder_needs_type_condition? && !ignore_default_scope?
299
+ relation.where!(type_condition)
300
+ relation.create_with!(inheritance_column.to_s => sti_name)
301
+ else
302
+ relation
303
+ end
304
+ end
305
+
306
+ def table_metadata
307
+ TableMetadata.new(self, arel_table)
260
308
  end
261
- end
262
309
  end
263
310
 
264
311
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -269,16 +316,14 @@ module ActiveRecord
269
316
  # ==== Example:
270
317
  # # Instantiates a single new object
271
318
  # User.new(first_name: 'Jamie')
272
- def initialize(attributes = nil, options = {})
273
- @attributes = self.class._default_attributes.dup
274
- self.class.define_attribute_methods
319
+ def initialize(attributes = nil)
320
+ @new_record = true
321
+ @attributes = self.class._default_attributes.deep_dup
275
322
 
276
323
  init_internals
277
324
  initialize_internals_callback
278
325
 
279
- # +options+ argument is only needed to make protected_attributes gem easier to hook.
280
- # Remove it when we drop support to this gem.
281
- init_attributes(attributes, options) if attributes
326
+ assign_attributes(attributes) if attributes
282
327
 
283
328
  yield self if block_given?
284
329
  _run_initialize_callbacks
@@ -286,7 +331,7 @@ module ActiveRecord
286
331
 
287
332
  # Initialize an empty model object from +coder+. +coder+ should be
288
333
  # the result of previously encoding an Active Record model, using
289
- # `encode_with`
334
+ # #encode_with.
290
335
  #
291
336
  # class Post < ActiveRecord::Base
292
337
  # end
@@ -298,15 +343,23 @@ module ActiveRecord
298
343
  # post = Post.allocate
299
344
  # post.init_with(coder)
300
345
  # post.title # => 'hello world'
301
- def init_with(coder)
346
+ def init_with(coder, &block)
302
347
  coder = LegacyYamlAdapter.convert(self.class, coder)
303
- @attributes = coder['attributes']
348
+ attributes = self.class.yaml_encoder.decode(coder)
349
+ init_with_attributes(attributes, coder["new_record"], &block)
350
+ end
304
351
 
305
- init_internals
352
+ ##
353
+ # Initialize an empty model object from +attributes+.
354
+ # +attributes+ should be an attributes object, and unlike the
355
+ # `initialize` method, no assignment calls are made per attribute.
356
+ def init_with_attributes(attributes, new_record = false) # :nodoc:
357
+ @new_record = new_record
358
+ @attributes = attributes
306
359
 
307
- @new_record = coder['new_record']
360
+ init_internals
308
361
 
309
- self.class.define_attribute_methods
362
+ yield self if block_given?
310
363
 
311
364
  _run_find_callbacks
312
365
  _run_initialize_callbacks
@@ -342,23 +395,22 @@ module ActiveRecord
342
395
 
343
396
  ##
344
397
  def initialize_dup(other) # :nodoc:
345
- @attributes = @attributes.dup
346
- @attributes.reset(self.class.primary_key)
398
+ @attributes = @attributes.deep_dup
399
+ @attributes.reset(@primary_key)
347
400
 
348
401
  _run_initialize_callbacks
349
402
 
350
- @aggregation_cache = {}
351
- @association_cache = {}
352
-
353
- @new_record = true
354
- @destroyed = false
403
+ @new_record = true
404
+ @destroyed = false
405
+ @_start_transaction_state = nil
406
+ @transaction_state = nil
355
407
 
356
408
  super
357
409
  end
358
410
 
359
411
  # Populate +coder+ with attributes about this record that should be
360
412
  # serialized. The structure of +coder+ defined in this method is
361
- # guaranteed to match the structure of +coder+ passed to the +init_with+
413
+ # guaranteed to match the structure of +coder+ passed to the #init_with
362
414
  # method.
363
415
  #
364
416
  # Example:
@@ -369,11 +421,9 @@ module ActiveRecord
369
421
  # Post.new.encode_with(coder)
370
422
  # coder # => {"attributes" => {"id" => nil, ... }}
371
423
  def encode_with(coder)
372
- # FIXME: Remove this when we better serialize attributes
373
- coder['raw_attributes'] = attributes_before_type_cast
374
- coder['attributes'] = @attributes
375
- coder['new_record'] = new_record?
376
- coder['active_record_yaml_version'] = 0
424
+ self.class.yaml_encoder.encode(@attributes, coder)
425
+ coder["new_record"] = new_record?
426
+ coder["active_record_yaml_version"] = 2
377
427
  end
378
428
 
379
429
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -397,7 +447,7 @@ module ActiveRecord
397
447
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
398
448
  def hash
399
449
  if id
400
- id.hash
450
+ self.class.hash ^ id.hash
401
451
  else
402
452
  super
403
453
  end
@@ -413,18 +463,27 @@ module ActiveRecord
413
463
 
414
464
  # Returns +true+ if the attributes hash has been frozen.
415
465
  def frozen?
466
+ sync_with_transaction_state if @transaction_state&.finalized?
416
467
  @attributes.frozen?
417
468
  end
418
469
 
419
470
  # Allows sort on objects
420
471
  def <=>(other_object)
421
472
  if other_object.is_a?(self.class)
422
- self.to_key <=> other_object.to_key
473
+ to_key <=> other_object.to_key
423
474
  else
424
475
  super
425
476
  end
426
477
  end
427
478
 
479
+ def present? # :nodoc:
480
+ true
481
+ end
482
+
483
+ def blank? # :nodoc:
484
+ false
485
+ end
486
+
428
487
  # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
429
488
  # attributes will be marked as read only since they cannot be saved.
430
489
  def readonly?
@@ -445,135 +504,100 @@ module ActiveRecord
445
504
  # We check defined?(@attributes) not to issue warnings if the object is
446
505
  # allocated but not initialized.
447
506
  inspection = if defined?(@attributes) && @attributes
448
- self.class.column_names.collect { |name|
449
- if has_attribute?(name)
450
- "#{name}: #{attribute_for_inspect(name)}"
451
- end
452
- }.compact.join(", ")
453
- else
454
- "not initialized"
455
- end
507
+ self.class.attribute_names.collect do |name|
508
+ if has_attribute?(name)
509
+ attr = _read_attribute(name)
510
+ value = if attr.nil?
511
+ attr.inspect
512
+ else
513
+ attr = format_for_inspect(attr)
514
+ inspection_filter.filter_param(name, attr)
515
+ end
516
+ "#{name}: #{value}"
517
+ end
518
+ end.compact.join(", ")
519
+ else
520
+ "not initialized"
521
+ end
522
+
456
523
  "#<#{self.class} #{inspection}>"
457
524
  end
458
525
 
459
- # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
526
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
460
527
  # when pp is required.
461
528
  def pretty_print(pp)
462
529
  return super if custom_inspect_method_defined?
463
530
  pp.object_address_group(self) do
464
531
  if defined?(@attributes) && @attributes
465
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
466
- pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
467
- column_value = read_attribute(column_name)
468
- pp.breakable ' '
532
+ attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
533
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
534
+ pp.breakable " "
469
535
  pp.group(1) do
470
- pp.text column_name
471
- pp.text ':'
536
+ pp.text attr_name
537
+ pp.text ":"
472
538
  pp.breakable
473
- pp.pp column_value
539
+ value = _read_attribute(attr_name)
540
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
541
+ pp.pp value
474
542
  end
475
543
  end
476
544
  else
477
- pp.breakable ' '
478
- pp.text 'not initialized'
545
+ pp.breakable " "
546
+ pp.text "not initialized"
479
547
  end
480
548
  end
481
549
  end
482
550
 
483
551
  # Returns a hash of the given methods with their names as keys and returned values as values.
484
552
  def slice(*methods)
485
- Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
553
+ Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
486
554
  end
487
555
 
488
556
  private
489
557
 
490
- def set_transaction_state(state) # :nodoc:
491
- @transaction_state = state
492
- end
558
+ # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
559
+ # the array, and then rescues from the possible +NoMethodError+. If those elements are
560
+ # +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
561
+ # which significantly impacts upon performance.
562
+ #
563
+ # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
564
+ #
565
+ # See also https://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
566
+ def to_ary
567
+ nil
568
+ end
493
569
 
494
- def has_transactional_callbacks? # :nodoc:
495
- !_rollback_callbacks.empty? || !_commit_callbacks.empty?
496
- end
570
+ def init_internals
571
+ @primary_key = self.class.primary_key
572
+ @readonly = false
573
+ @destroyed = false
574
+ @marked_for_destruction = false
575
+ @destroyed_by_association = nil
576
+ @_start_transaction_state = nil
577
+ @transaction_state = nil
497
578
 
498
- # Updates the attributes on this particular ActiveRecord object so that
499
- # if it is associated with a transaction, then the state of the AR object
500
- # will be updated to reflect the current state of the transaction
501
- #
502
- # The @transaction_state variable stores the states of the associated
503
- # transaction. This relies on the fact that a transaction can only be in
504
- # one rollback or commit (otherwise a list of states would be required)
505
- # Each AR object inside of a transaction carries that transaction's
506
- # TransactionState.
507
- #
508
- # This method checks to see if the ActiveRecord object's state reflects
509
- # the TransactionState, and rolls back or commits the ActiveRecord object
510
- # as appropriate.
511
- #
512
- # Since ActiveRecord objects can be inside multiple transactions, this
513
- # method recursively goes through the parent of the TransactionState and
514
- # checks if the ActiveRecord object reflects the state of the object.
515
- def sync_with_transaction_state
516
- update_attributes_from_transaction_state(@transaction_state, 0)
517
- end
579
+ self.class.define_attribute_methods
580
+ end
518
581
 
519
- def update_attributes_from_transaction_state(transaction_state, depth)
520
- @reflects_state = [false] if depth == 0
582
+ def initialize_internals_callback
583
+ end
521
584
 
522
- if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
523
- unless @reflects_state[depth]
524
- restore_transaction_record_state if transaction_state.rolledback?
525
- clear_transaction_record_state
526
- @reflects_state[depth] = true
527
- end
585
+ def custom_inspect_method_defined?
586
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
587
+ end
528
588
 
529
- if transaction_state.parent && !@reflects_state[depth+1]
530
- update_attributes_from_transaction_state(transaction_state.parent, depth+1)
589
+ class InspectionMask < DelegateClass(::String)
590
+ def pretty_print(pp)
591
+ pp.text __getobj__
531
592
  end
532
593
  end
533
- end
534
-
535
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
536
- # of the array, and then rescues from the possible NoMethodError. If those elements are
537
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
538
- # which significantly impacts upon performance.
539
- #
540
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
541
- #
542
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
543
- def to_ary # :nodoc:
544
- nil
545
- end
594
+ private_constant :InspectionMask
546
595
 
547
- def init_internals
548
- @aggregation_cache = {}
549
- @association_cache = {}
550
- @readonly = false
551
- @destroyed = false
552
- @marked_for_destruction = false
553
- @destroyed_by_association = nil
554
- @new_record = true
555
- @txn = nil
556
- @_start_transaction_state = {}
557
- @transaction_state = nil
558
- end
559
-
560
- def initialize_internals_callback
561
- end
562
-
563
- # This method is needed to make protected_attributes gem easier to hook.
564
- # Remove it when we drop support to this gem.
565
- def init_attributes(attributes, options)
566
- assign_attributes(attributes)
567
- end
568
-
569
- def thaw
570
- if frozen?
571
- @attributes = @attributes.dup
596
+ def inspection_filter
597
+ @inspection_filter ||= begin
598
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
599
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
600
+ end
572
601
  end
573
- end
574
-
575
- def custom_inspect_method_defined?
576
- self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
577
- end
578
602
  end
579
603
  end