activerecord 3.2.6 → 6.0.0

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

Potentially problematic release.


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

Files changed (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,214 +1,258 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class JoinDependency # :nodoc:
4
- autoload :JoinPart, 'active_record/associations/join_dependency/join_part'
5
- autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
6
- autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
7
-
8
- attr_reader :join_parts, :reflections, :alias_tracker, :active_record
9
-
10
- def initialize(base, associations, joins)
11
- @active_record = base
12
- @table_joins = joins
13
- @join_parts = [JoinBase.new(base)]
14
- @associations = {}
15
- @reflections = []
16
- @alias_tracker = AliasTracker.new(base.connection, joins)
17
- @alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
18
- build(associations)
19
- end
6
+ autoload :JoinBase, "active_record/associations/join_dependency/join_base"
7
+ autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
20
8
 
21
- def graft(*associations)
22
- associations.each do |association|
23
- join_associations.detect {|a| association == a} ||
24
- build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
9
+ class Aliases # :nodoc:
10
+ def initialize(tables)
11
+ @tables = tables
12
+ @alias_cache = tables.each_with_object({}) { |table, h|
13
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
14
+ i[column.name] = column.alias
15
+ }
16
+ }
17
+ @columns_cache = tables.each_with_object({}) { |table, h|
18
+ h[table.node] = table.columns
19
+ }
25
20
  end
26
- self
27
- end
28
-
29
- def join_associations
30
- join_parts.last(join_parts.length - 1)
31
- end
32
21
 
33
- def join_base
34
- join_parts.first
35
- end
22
+ def columns
23
+ @tables.flat_map(&:column_aliases)
24
+ end
36
25
 
37
- def columns
38
- join_parts.collect { |join_part|
39
- table = join_part.aliased_table
40
- join_part.column_names_with_alias.collect{ |column_name, aliased_name|
41
- table[column_name].as Arel.sql(aliased_name)
42
- }
43
- }.flatten
44
- end
26
+ def column_aliases(node)
27
+ @columns_cache[node]
28
+ end
45
29
 
46
- def instantiate(rows)
47
- primary_key = join_base.aliased_primary_key
48
- parents = {}
30
+ def column_alias(node, column)
31
+ @alias_cache[node][column]
32
+ end
49
33
 
50
- records = rows.map { |model|
51
- primary_id = model[primary_key]
52
- parent = parents[primary_id] ||= join_base.instantiate(model)
53
- construct(parent, @associations, join_associations, model)
54
- parent
55
- }.uniq
34
+ Table = Struct.new(:node, :columns) do # :nodoc:
35
+ def column_aliases
36
+ t = node.table
37
+ columns.map { |column| t[column.name].as Arel.sql column.alias }
38
+ end
39
+ end
40
+ Column = Struct.new(:name, :alias)
41
+ end
56
42
 
57
- remove_duplicate_results!(active_record, records, @associations)
58
- records
43
+ def self.make_tree(associations)
44
+ hash = {}
45
+ walk_tree associations, hash
46
+ hash
59
47
  end
60
48
 
61
- def remove_duplicate_results!(base, records, associations)
49
+ def self.walk_tree(associations, hash)
62
50
  case associations
63
51
  when Symbol, String
64
- reflection = base.reflections[associations]
65
- remove_uniq_by_reflection(reflection, records)
52
+ hash[associations.to_sym] ||= {}
66
53
  when Array
67
- associations.each do |association|
68
- remove_duplicate_results!(base, records, association)
54
+ associations.each do |assoc|
55
+ walk_tree assoc, hash
69
56
  end
70
57
  when Hash
71
- associations.keys.each do |name|
72
- reflection = base.reflections[name]
73
- remove_uniq_by_reflection(reflection, records)
74
-
75
- parent_records = []
76
- records.each do |record|
77
- if descendant = record.send(reflection.name)
78
- if reflection.collection?
79
- parent_records.concat descendant.target.uniq
80
- else
81
- parent_records << descendant
82
- end
83
- end
84
- end
58
+ associations.each do |k, v|
59
+ cache = hash[k] ||= {}
60
+ walk_tree v, cache
61
+ end
62
+ else
63
+ raise ConfigurationError, associations.inspect
64
+ end
65
+ end
66
+
67
+ def initialize(base, table, associations, join_type)
68
+ tree = self.class.make_tree associations
69
+ @join_root = JoinBase.new(base, table, build(tree, base))
70
+ @join_type = join_type
71
+ end
72
+
73
+ def reflections
74
+ join_root.drop(1).map!(&:reflection)
75
+ end
85
76
 
86
- remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
77
+ def join_constraints(joins_to_add, alias_tracker)
78
+ @alias_tracker = alias_tracker
79
+
80
+ construct_tables!(join_root)
81
+ joins = make_join_constraints(join_root, join_type)
82
+
83
+ joins.concat joins_to_add.flat_map { |oj|
84
+ construct_tables!(oj.join_root)
85
+ if join_root.match? oj.join_root
86
+ walk(join_root, oj.join_root, oj.join_type)
87
+ else
88
+ make_join_constraints(oj.join_root, oj.join_type)
87
89
  end
90
+ }
91
+ end
92
+
93
+ def instantiate(result_set, &block)
94
+ primary_key = aliases.column_alias(join_root, join_root.primary_key)
95
+
96
+ seen = Hash.new { |i, object_id|
97
+ i[object_id] = Hash.new { |j, child_class|
98
+ j[child_class] = {}
99
+ }
100
+ }
101
+
102
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
103
+ parents = model_cache[join_root]
104
+ column_aliases = aliases.column_aliases join_root
105
+
106
+ message_bus = ActiveSupport::Notifications.instrumenter
107
+
108
+ payload = {
109
+ record_count: result_set.length,
110
+ class_name: join_root.base_klass.name
111
+ }
112
+
113
+ message_bus.instrument("instantiation.active_record", payload) do
114
+ result_set.each { |row_hash|
115
+ parent_key = primary_key ? row_hash[primary_key] : row_hash
116
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
117
+ construct(parent, join_root, row_hash, seen, model_cache)
118
+ }
88
119
  end
120
+
121
+ parents.values
122
+ end
123
+
124
+ def apply_column_aliases(relation)
125
+ relation._select!(-> { aliases.columns })
89
126
  end
90
127
 
91
128
  protected
129
+ attr_reader :join_root, :join_type
92
130
 
93
- def cache_joined_association(association)
94
- associations = []
95
- parent = association.parent
96
- while parent != join_base
97
- associations.unshift(parent.reflection.name)
98
- parent = parent.parent
99
- end
100
- ref = @associations
101
- associations.each do |key|
102
- ref = ref[key]
131
+ private
132
+ attr_reader :alias_tracker
133
+
134
+ def aliases
135
+ @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
136
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
137
+ Aliases::Column.new column_name, "t#{i}_r#{j}"
138
+ }
139
+ Aliases::Table.new(join_part, columns)
140
+ }
103
141
  end
104
- ref[association.reflection.name] ||= {}
105
- end
106
142
 
107
- def build(associations, parent = nil, join_type = Arel::InnerJoin)
108
- parent ||= join_parts.last
109
- case associations
110
- when Symbol, String
111
- reflection = parent.reflections[associations.to_s.intern] or
112
- raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
113
- unless join_association = find_join_association(reflection, parent)
114
- @reflections << reflection
115
- join_association = build_join_association(reflection, parent)
116
- join_association.join_type = join_type
117
- @join_parts << join_association
118
- cache_joined_association(join_association)
119
- end
120
- join_association
121
- when Array
122
- associations.each do |association|
123
- build(association, parent, join_type)
143
+ def construct_tables!(join_root)
144
+ join_root.each_children do |parent, child|
145
+ child.tables = table_aliases_for(parent, child)
124
146
  end
125
- when Hash
126
- associations.keys.sort_by { |a| a.to_s }.each do |name|
127
- join_association = build(name, parent, join_type)
128
- build(associations[name], join_association, join_type)
147
+ end
148
+
149
+ def make_join_constraints(join_root, join_type)
150
+ join_root.children.flat_map do |child|
151
+ make_constraints(join_root, child, join_type)
129
152
  end
130
- else
131
- raise ConfigurationError, associations.inspect
132
153
  end
133
- end
134
154
 
135
- def find_join_association(name_or_reflection, parent)
136
- if String === name_or_reflection
137
- name_or_reflection = name_or_reflection.to_sym
155
+ def make_constraints(parent, child, join_type)
156
+ foreign_table = parent.table
157
+ foreign_klass = parent.base_klass
158
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
159
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
138
160
  end
139
161
 
140
- join_associations.detect { |j|
141
- j.reflection == name_or_reflection && j.parent == parent
142
- }
143
- end
162
+ def table_aliases_for(parent, node)
163
+ node.reflection.chain.map { |reflection|
164
+ alias_tracker.aliased_table_for(
165
+ reflection.table_name,
166
+ table_alias_for(reflection, parent, reflection != node.reflection),
167
+ reflection.klass.type_caster
168
+ )
169
+ }
170
+ end
144
171
 
145
- def remove_uniq_by_reflection(reflection, records)
146
- if reflection && reflection.collection?
147
- records.each { |record| record.send(reflection.name).target.uniq! }
172
+ def table_alias_for(reflection, parent, join)
173
+ name = reflection.alias_candidate(parent.table_name)
174
+ join ? "#{name}_join" : name
148
175
  end
149
- end
150
176
 
151
- def build_join_association(reflection, parent)
152
- JoinAssociation.new(reflection, self, parent)
153
- end
177
+ def walk(left, right, join_type)
178
+ intersection, missing = right.children.map { |node1|
179
+ [left.children.find { |node2| node1.match? node2 }, node1]
180
+ }.partition(&:first)
154
181
 
155
- def construct(parent, associations, join_parts, row)
156
- case associations
157
- when Symbol, String
158
- name = associations.to_s
182
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
183
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
184
+ end
159
185
 
160
- join_part = join_parts.detect { |j|
161
- j.reflection.name.to_s == name &&
162
- j.parent_table_name == parent.class.table_name }
186
+ def find_reflection(klass, name)
187
+ klass._reflect_on_association(name) ||
188
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
189
+ end
163
190
 
164
- raise(ConfigurationError, "No such association") unless join_part
191
+ def build(associations, base_klass)
192
+ associations.map do |name, right|
193
+ reflection = find_reflection base_klass, name
194
+ reflection.check_validity!
195
+ reflection.check_eager_loadable!
165
196
 
166
- join_parts.delete(join_part)
167
- construct_association(parent, join_part, row)
168
- when Array
169
- associations.each do |association|
170
- construct(parent, association, join_parts, row)
197
+ if reflection.polymorphic?
198
+ raise EagerLoadPolymorphicError.new(reflection)
199
+ end
200
+
201
+ JoinAssociation.new(reflection, build(right, reflection.klass))
171
202
  end
172
- when Hash
173
- associations.sort_by { |k,_| k.to_s }.each do |association_name, assoc|
174
- association = construct(parent, association_name, join_parts, row)
175
- construct(association, assoc, join_parts, row) if association
203
+ end
204
+
205
+ def construct(ar_parent, parent, row, seen, model_cache)
206
+ return if ar_parent.nil?
207
+
208
+ parent.children.each do |node|
209
+ if node.reflection.collection?
210
+ other = ar_parent.association(node.reflection.name)
211
+ other.loaded!
212
+ elsif ar_parent.association_cached?(node.reflection.name)
213
+ model = ar_parent.association(node.reflection.name).target
214
+ construct(model, node, row, seen, model_cache)
215
+ next
216
+ end
217
+
218
+ key = aliases.column_alias(node, node.primary_key)
219
+ id = row[key]
220
+ if id.nil?
221
+ nil_association = ar_parent.association(node.reflection.name)
222
+ nil_association.loaded!
223
+ next
224
+ end
225
+
226
+ model = seen[ar_parent.object_id][node][id]
227
+
228
+ if model
229
+ construct(model, node, row, seen, model_cache)
230
+ else
231
+ model = construct_model(ar_parent, node, row, model_cache, id)
232
+
233
+ seen[ar_parent.object_id][node][id] = model
234
+ construct(model, node, row, seen, model_cache)
235
+ end
176
236
  end
177
- else
178
- raise ConfigurationError, associations.inspect
179
237
  end
180
- end
181
238
 
182
- def construct_association(record, join_part, row)
183
- return if record.id.to_s != join_part.parent.record_id(row).to_s
239
+ def construct_model(record, node, row, model_cache, id)
240
+ other = record.association(node.reflection.name)
184
241
 
185
- macro = join_part.reflection.macro
186
- if macro == :has_one
187
- return record.association(join_part.reflection.name).target if record.association_cache.key?(join_part.reflection.name)
188
- association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
189
- set_target_and_inverse(join_part, association, record)
190
- else
191
- association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
192
- case macro
193
- when :has_many, :has_and_belongs_to_many
194
- other = record.association(join_part.reflection.name)
195
- other.loaded!
196
- other.target.push(association) if association
197
- other.set_inverse_instance(association)
198
- when :belongs_to
199
- set_target_and_inverse(join_part, association, record)
242
+ model = model_cache[node][id] ||=
243
+ node.instantiate(row, aliases.column_aliases(node)) do |m|
244
+ other.set_inverse_instance(m)
245
+ end
246
+
247
+ if node.reflection.collection?
248
+ other.target.push(model)
200
249
  else
201
- raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}"
250
+ other.target = model
202
251
  end
203
- end
204
- association
205
- end
206
252
 
207
- def set_target_and_inverse(join_part, association, record)
208
- other = record.association(join_part.reflection.name)
209
- other.target = association
210
- other.set_inverse_instance(association)
211
- end
253
+ model.readonly! if node.readonly?
254
+ model
255
+ end
212
256
  end
213
257
  end
214
258
  end
@@ -1,126 +1,132 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class Preloader
4
6
  class Association #:nodoc:
5
- attr_reader :owners, :reflection, :preload_options, :model, :klass
6
-
7
- def initialize(klass, owners, reflection, preload_options)
8
- @klass = klass
9
- @owners = owners
10
- @reflection = reflection
11
- @preload_options = preload_options || {}
12
- @model = owners.first && owners.first.class
13
- @scoped = nil
14
- @owners_by_key = nil
7
+ def initialize(klass, owners, reflection, preload_scope)
8
+ @klass = klass
9
+ @owners = owners
10
+ @reflection = reflection
11
+ @preload_scope = preload_scope
12
+ @model = owners.first && owners.first.class
15
13
  end
16
14
 
17
15
  def run
18
- unless owners.first.association(reflection.name).loaded?
19
- preload
16
+ if !preload_scope || preload_scope.empty_scope?
17
+ owners.each do |owner|
18
+ associate_records_to_owner(owner, records_by_owner[owner] || [])
19
+ end
20
+ else
21
+ # Custom preload scope is used and
22
+ # the association can not be marked as loaded
23
+ # Loading into a Hash instead
24
+ records_by_owner
20
25
  end
26
+ self
21
27
  end
22
28
 
23
- def preload
24
- raise NotImplementedError
29
+ def records_by_owner
30
+ # owners can be duplicated when a relation has a collection association join
31
+ # #compare_by_identity makes such owners different hash keys
32
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
33
+ owners_by_key[convert_key(record[association_key_name])].each do |owner|
34
+ (result[owner] ||= []) << record
35
+ end
36
+ end
25
37
  end
26
38
 
27
- def scoped
28
- @scoped ||= build_scope
39
+ def preloaded_records
40
+ return @preloaded_records if defined?(@preloaded_records)
41
+ @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
29
42
  end
30
43
 
31
- def records_for(ids)
32
- scoped.where(association_key.in(ids))
33
- end
44
+ private
45
+ attr_reader :owners, :reflection, :preload_scope, :model, :klass
34
46
 
35
- def table
36
- klass.arel_table
37
- end
47
+ # The name of the key on the associated records
48
+ def association_key_name
49
+ reflection.join_primary_key(klass)
50
+ end
38
51
 
39
- # The name of the key on the associated records
40
- def association_key_name
41
- raise NotImplementedError
42
- end
52
+ # The name of the key on the model which declares the association
53
+ def owner_key_name
54
+ reflection.join_foreign_key
55
+ end
43
56
 
44
- # This is overridden by HABTM as the condition should be on the foreign_key column in
45
- # the join table
46
- def association_key
47
- table[association_key_name]
48
- end
57
+ def associate_records_to_owner(owner, records)
58
+ association = owner.association(reflection.name)
59
+ if reflection.collection?
60
+ association.target = records
61
+ else
62
+ association.target = records.first
63
+ end
64
+ end
49
65
 
50
- # The name of the key on the model which declares the association
51
- def owner_key_name
52
- raise NotImplementedError
53
- end
66
+ def owner_keys
67
+ @owner_keys ||= owners_by_key.keys
68
+ end
54
69
 
55
- # We're converting to a string here because postgres will return the aliased association
56
- # key in a habtm as a string (for whatever reason)
57
- def owners_by_key
58
- @owners_by_key ||= owners.group_by do |owner|
59
- key = owner[owner_key_name]
60
- key && key.to_s
70
+ def owners_by_key
71
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
72
+ key = convert_key(owner[owner_key_name])
73
+ (result[key] ||= []) << owner if key
74
+ end
61
75
  end
62
- end
63
76
 
64
- def options
65
- reflection.options
66
- end
77
+ def key_conversion_required?
78
+ unless defined?(@key_conversion_required)
79
+ @key_conversion_required = (association_key_type != owner_key_type)
80
+ end
67
81
 
68
- private
82
+ @key_conversion_required
83
+ end
69
84
 
70
- def associated_records_by_owner
71
- owners_map = owners_by_key
72
- owner_keys = owners_map.keys.compact
85
+ def convert_key(key)
86
+ if key_conversion_required?
87
+ key.to_s
88
+ else
89
+ key
90
+ end
91
+ end
73
92
 
74
- if klass.nil? || owner_keys.empty?
75
- records = []
76
- else
77
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
78
- # Make several smaller queries if necessary or make one query if the adapter supports it
79
- sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
80
- records = sliced.map { |slice| records_for(slice) }.flatten
93
+ def association_key_type
94
+ @klass.type_for_attribute(association_key_name).type
81
95
  end
82
96
 
83
- # Each record may have multiple owners, and vice-versa
84
- records_by_owner = Hash[owners.map { |owner| [owner, []] }]
85
- records.each do |record|
86
- owner_key = record[association_key_name].to_s
97
+ def owner_key_type
98
+ @model.type_for_attribute(owner_key_name).type
99
+ end
87
100
 
88
- owners_map[owner_key].each do |owner|
89
- records_by_owner[owner] << record
101
+ def records_for(ids)
102
+ scope.where(association_key_name => ids).load do |record|
103
+ # Processing only the first owner
104
+ # because the record is modified but not an owner
105
+ owner = owners_by_key[convert_key(record[association_key_name])].first
106
+ association = owner.association(reflection.name)
107
+ association.set_inverse_instance(record)
90
108
  end
91
109
  end
92
- records_by_owner
93
- end
94
-
95
- def build_scope
96
- scope = klass.scoped
97
110
 
98
- scope = scope.where(process_conditions(options[:conditions]))
99
- scope = scope.where(process_conditions(preload_options[:conditions]))
100
-
101
- scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star])
102
- scope = scope.includes(preload_options[:include] || options[:include])
111
+ def scope
112
+ @scope ||= build_scope
113
+ end
103
114
 
104
- if options[:as]
105
- scope = scope.where(
106
- klass.table_name => {
107
- reflection.type => model.base_class.sti_name
108
- }
109
- )
115
+ def reflection_scope
116
+ @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
110
117
  end
111
118
 
112
- scope
113
- end
119
+ def build_scope
120
+ scope = klass.scope_for_association
114
121
 
115
- def process_conditions(conditions)
116
- if conditions.respond_to?(:to_proc)
117
- conditions = klass.send(:instance_eval, &conditions)
118
- end
122
+ if reflection.type && !reflection.through_reflection?
123
+ scope.where!(reflection.type => model.polymorphic_name)
124
+ end
119
125
 
120
- if conditions
121
- klass.send(:sanitize_sql, conditions)
126
+ scope.merge!(reflection_scope) if reflection.scope
127
+ scope.merge!(preload_scope) if preload_scope
128
+ scope
122
129
  end
123
- end
124
130
  end
125
131
  end
126
132
  end