activerecord 4.2.0 → 6.0.0

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

Potentially problematic release.


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

Files changed (372) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +612 -971
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +267 -248
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +135 -56
  11. data/lib/active_record/associations/association_scope.rb +103 -131
  12. data/lib/active_record/associations/belongs_to_association.rb +67 -54
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  14. data/lib/active_record/associations/builder/association.rb +27 -40
  15. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  16. data/lib/active_record/associations/builder/collection_association.rb +10 -29
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +60 -70
  18. data/lib/active_record/associations/builder/has_many.rb +8 -4
  19. data/lib/active_record/associations/builder/has_one.rb +46 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +138 -274
  22. data/lib/active_record/associations/collection_proxy.rb +252 -151
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -83
  25. data/lib/active_record/associations/has_many_through_association.rb +62 -80
  26. data/lib/active_record/associations/has_one_association.rb +62 -49
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +38 -80
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +138 -162
  32. data/lib/active_record/associations/preloader/association.rb +90 -119
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +92 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1737 -1596
  38. data/lib/active_record/attribute_assignment.rb +56 -183
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +15 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +174 -134
  42. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  43. data/lib/active_record/attribute_methods/query.rb +6 -5
  44. data/lib/active_record/attribute_methods/read.rb +20 -76
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +33 -55
  48. data/lib/active_record/attribute_methods.rb +124 -143
  49. data/lib/active_record/attributes.rb +214 -74
  50. data/lib/active_record/autosave_association.rb +115 -46
  51. data/lib/active_record/base.rb +60 -49
  52. data/lib/active_record/callbacks.rb +100 -74
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -290
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +247 -108
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +366 -227
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +706 -222
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -87
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +468 -194
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +535 -597
  68. data/lib/active_record/connection_adapters/column.rb +56 -43
  69. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -195
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +65 -115
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  98. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +474 -286
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -363
  116. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +288 -359
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +176 -41
  128. data/lib/active_record/core.rb +266 -233
  129. data/lib/active_record/counter_cache.rb +68 -50
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +87 -105
  136. data/lib/active_record/enum.rb +164 -88
  137. data/lib/active_record/errors.rb +189 -53
  138. data/lib/active_record/explain.rb +23 -11
  139. data/lib/active_record/explain_registry.rb +4 -2
  140. data/lib/active_record/explain_subscriber.rb +11 -6
  141. data/lib/active_record/fixture_set/file.rb +35 -9
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +226 -495
  147. data/lib/active_record/gem_version.rb +4 -2
  148. data/lib/active_record/inheritance.rb +158 -112
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +123 -29
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +3 -2
  154. data/lib/active_record/locking/optimistic.rb +91 -98
  155. data/lib/active_record/locking/pessimistic.rb +18 -6
  156. data/lib/active_record/log_subscriber.rb +76 -33
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +177 -90
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +8 -6
  163. data/lib/active_record/migration.rb +634 -288
  164. data/lib/active_record/model_schema.rb +314 -112
  165. data/lib/active_record/nested_attributes.rb +266 -214
  166. data/lib/active_record/no_touching.rb +15 -2
  167. data/lib/active_record/null_relation.rb +24 -37
  168. data/lib/active_record/persistence.rb +559 -124
  169. data/lib/active_record/query_cache.rb +19 -23
  170. data/lib/active_record/querying.rb +43 -29
  171. data/lib/active_record/railtie.rb +148 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +2 -0
  174. data/lib/active_record/railties/controller_runtime.rb +34 -33
  175. data/lib/active_record/railties/databases.rake +338 -202
  176. data/lib/active_record/readonly_attributes.rb +5 -4
  177. data/lib/active_record/reflection.rb +460 -299
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +207 -55
  180. data/lib/active_record/relation/calculations.rb +269 -248
  181. data/lib/active_record/relation/delegation.rb +70 -80
  182. data/lib/active_record/relation/finder_methods.rb +279 -255
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +83 -69
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  192. data/lib/active_record/relation/predicate_builder.rb +116 -92
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +574 -391
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +18 -16
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +518 -340
  200. data/lib/active_record/result.rb +79 -42
  201. data/lib/active_record/runtime_registry.rb +6 -4
  202. data/lib/active_record/sanitization.rb +144 -121
  203. data/lib/active_record/schema.rb +21 -24
  204. data/lib/active_record/schema_dumper.rb +112 -93
  205. data/lib/active_record/schema_migration.rb +24 -20
  206. data/lib/active_record/scoping/default.rb +101 -84
  207. data/lib/active_record/scoping/named.rb +86 -33
  208. data/lib/active_record/scoping.rb +45 -26
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +5 -5
  211. data/lib/active_record/statement_cache.rb +73 -36
  212. data/lib/active_record/store.rb +127 -42
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +309 -99
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +58 -88
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +82 -31
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +86 -40
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +215 -139
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +4 -41
  227. data/lib/active_record/type/date_time.rb +4 -38
  228. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  229. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +30 -15
  233. data/lib/active_record/type/text.rb +2 -2
  234. data/lib/active_record/type/time.rb +11 -16
  235. data/lib/active_record/type/type_map.rb +15 -17
  236. data/lib/active_record/type/unsigned_integer.rb +9 -7
  237. data/lib/active_record/type.rb +78 -23
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +13 -4
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +14 -13
  245. data/lib/active_record/validations/uniqueness.rb +43 -46
  246. data/lib/active_record/validations.rb +39 -35
  247. data/lib/active_record/version.rb +3 -1
  248. data/lib/active_record.rb +43 -21
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
  335. data/lib/rails/generators/active_record/migration.rb +31 -1
  336. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +166 -60
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  347. data/lib/active_record/attribute.rb +0 -149
  348. data/lib/active_record/attribute_set/builder.rb +0 -86
  349. data/lib/active_record/attribute_set.rb +0 -77
  350. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  351. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  352. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  353. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  354. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  355. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  356. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  357. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  358. data/lib/active_record/type/big_integer.rb +0 -13
  359. data/lib/active_record/type/binary.rb +0 -50
  360. data/lib/active_record/type/boolean.rb +0 -30
  361. data/lib/active_record/type/decimal.rb +0 -40
  362. data/lib/active_record/type/decorator.rb +0 -14
  363. data/lib/active_record/type/float.rb +0 -19
  364. data/lib/active_record/type/integer.rb +0 -55
  365. data/lib/active_record/type/mutable.rb +0 -16
  366. data/lib/active_record/type/numeric.rb +0 -36
  367. data/lib/active_record/type/string.rb +0 -36
  368. data/lib/active_record/type/time_value.rb +0 -38
  369. data/lib/active_record/type/value.rb +0 -101
  370. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  371. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  372. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Batches
5
+ class BatchEnumerator
6
+ include Enumerable
7
+
8
+ def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
9
+ @of = of
10
+ @relation = relation
11
+ @start = start
12
+ @finish = finish
13
+ end
14
+
15
+ # Looping through a collection of records from the database (using the
16
+ # +all+ method, for example) is very inefficient since it will try to
17
+ # instantiate all the objects at once.
18
+ #
19
+ # In that case, batch processing methods allow you to work with the
20
+ # records in batches, thereby greatly reducing memory consumption.
21
+ #
22
+ # Person.in_batches.each_record do |person|
23
+ # person.do_awesome_stuff
24
+ # end
25
+ #
26
+ # Person.where("age > 21").in_batches(of: 10).each_record do |person|
27
+ # person.party_all_night!
28
+ # end
29
+ #
30
+ # If you do not provide a block to #each_record, it will return an Enumerator
31
+ # for chaining with other methods:
32
+ #
33
+ # Person.in_batches.each_record.with_index do |person, index|
34
+ # person.award_trophy(index + 1)
35
+ # end
36
+ def each_record
37
+ return to_enum(:each_record) unless block_given?
38
+
39
+ @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
40
+ relation.records.each { |record| yield record }
41
+ end
42
+ end
43
+
44
+ # Delegates #delete_all, #update_all, #destroy_all methods to each batch.
45
+ #
46
+ # People.in_batches.delete_all
47
+ # People.where('age < 10').in_batches.destroy_all
48
+ # People.in_batches.update_all('age = age + 1')
49
+ [:delete_all, :update_all, :destroy_all].each do |method|
50
+ define_method(method) do |*args, &block|
51
+ @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
52
+ relation.send(method, *args, &block)
53
+ end
54
+ end
55
+ end
56
+
57
+ # Yields an ActiveRecord::Relation object for each batch of records.
58
+ #
59
+ # Person.in_batches.each do |relation|
60
+ # relation.update_all(awesome: true)
61
+ # end
62
+ def each
63
+ enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
64
+ return enum.each { |relation| yield relation } if block_given?
65
+ enum
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,8 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/relation/batches/batch_enumerator"
4
+
1
5
  module ActiveRecord
2
6
  module Batches
7
+ ORDER_IGNORE_MESSAGE = "Scoped order is ignored, it's forced to be batch order."
8
+
3
9
  # Looping through a collection of records from the database
4
- # (using the +all+ method, for example) is very inefficient
5
- # since it will try to instantiate all the objects at once.
10
+ # (using the Scoping::Named::ClassMethods.all method, for example)
11
+ # is very inefficient since it will try to instantiate all the objects at once.
6
12
  #
7
13
  # In that case, batch processing methods allow you to work
8
14
  # with the records in batches, thereby greatly reducing memory consumption.
@@ -26,38 +32,52 @@ module ActiveRecord
26
32
  # end
27
33
  #
28
34
  # ==== Options
29
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
30
- # * <tt>:start</tt> - Specifies the starting point for the batch processing.
31
- # This is especially useful if you want multiple workers dealing with
32
- # the same processing queue. You can make worker 1 handle all the records
33
- # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
34
- # (by setting the +:start+ option on that worker).
35
- #
36
- # # Let's process for a batch of 2000 records, skipping the first 2000 rows
37
- # Person.find_each(start: 2000, batch_size: 2000) do |person|
35
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
36
+ # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
37
+ # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
38
+ # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
39
+ # an order is present in the relation.
40
+ #
41
+ # Limits are honored, and if present there is no requirement for the batch
42
+ # size: it can be less than, equal to, or greater than the limit.
43
+ #
44
+ # The options +start+ and +finish+ are especially useful if you want
45
+ # multiple workers dealing with the same processing queue. You can make
46
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
47
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
48
+ # option on each worker.
49
+ #
50
+ # # In worker 1, let's process until 9999 records.
51
+ # Person.find_each(finish: 9_999) do |person|
52
+ # person.party_all_night!
53
+ # end
54
+ #
55
+ # # In worker 2, let's process from record 10_000 and onwards.
56
+ # Person.find_each(start: 10_000) do |person|
38
57
  # person.party_all_night!
39
58
  # end
40
59
  #
41
60
  # NOTE: It's not possible to set the order. That is automatically set to
42
61
  # ascending on the primary key ("id ASC") to make the batch ordering
43
- # work. This also means that this method only works with integer-based
44
- # primary keys.
62
+ # work. This also means that this method only works when the primary key is
63
+ # orderable (e.g. an integer or string).
45
64
  #
46
- # NOTE: You can't set the limit either, that's used to control
47
- # the batch sizes.
48
- def find_each(options = {})
65
+ # NOTE: By its nature, batch processing is subject to race conditions if
66
+ # other processes are modifying the database.
67
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
49
68
  if block_given?
50
- find_in_batches(options) do |records|
69
+ find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
51
70
  records.each { |record| yield record }
52
71
  end
53
72
  else
54
- enum_for :find_each, options do
55
- options[:start] ? where(table[primary_key].gteq(options[:start])).size : size
73
+ enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
74
+ relation = self
75
+ apply_limits(relation, start, finish).size
56
76
  end
57
77
  end
58
78
  end
59
79
 
60
- # Yields each batch of records that was found by the find +options+ as
80
+ # Yields each batch of records that was found by the find options as
61
81
  # an array.
62
82
  #
63
83
  # Person.where("age > 21").find_in_batches do |group|
@@ -76,63 +96,195 @@ module ActiveRecord
76
96
  # To be yielded each record one by one, use #find_each instead.
77
97
  #
78
98
  # ==== Options
79
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
80
- # * <tt>:start</tt> - Specifies the starting point for the batch processing.
81
- # This is especially useful if you want multiple workers dealing with
82
- # the same processing queue. You can make worker 1 handle all the records
83
- # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
84
- # (by setting the +:start+ option on that worker).
85
- #
86
- # # Let's process the next 2000 records
87
- # Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
99
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
100
+ # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
101
+ # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
102
+ # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
103
+ # an order is present in the relation.
104
+ #
105
+ # Limits are honored, and if present there is no requirement for the batch
106
+ # size: it can be less than, equal to, or greater than the limit.
107
+ #
108
+ # The options +start+ and +finish+ are especially useful if you want
109
+ # multiple workers dealing with the same processing queue. You can make
110
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
111
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
112
+ # option on each worker.
113
+ #
114
+ # # Let's process from record 10_000 on.
115
+ # Person.find_in_batches(start: 10_000) do |group|
88
116
  # group.each { |person| person.party_all_night! }
89
117
  # end
90
118
  #
91
119
  # NOTE: It's not possible to set the order. That is automatically set to
92
120
  # ascending on the primary key ("id ASC") to make the batch ordering
93
- # work. This also means that this method only works with integer-based
94
- # primary keys.
121
+ # work. This also means that this method only works when the primary key is
122
+ # orderable (e.g. an integer or string).
95
123
  #
96
- # NOTE: You can't set the limit either, that's used to control
97
- # the batch sizes.
98
- def find_in_batches(options = {})
99
- options.assert_valid_keys(:start, :batch_size)
100
-
124
+ # NOTE: By its nature, batch processing is subject to race conditions if
125
+ # other processes are modifying the database.
126
+ def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
101
127
  relation = self
102
- start = options[:start]
103
- batch_size = options[:batch_size] || 1000
104
-
105
128
  unless block_given?
106
- return to_enum(:find_in_batches, options) do
107
- total = start ? where(table[primary_key].gteq(start)).size : size
129
+ return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
130
+ total = apply_limits(relation, start, finish).size
108
131
  (total - 1).div(batch_size) + 1
109
132
  end
110
133
  end
111
134
 
112
- if logger && (arel.orders.present? || arel.taken.present?)
113
- logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
135
+ in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
136
+ yield batch.to_a
137
+ end
138
+ end
139
+
140
+ # Yields ActiveRecord::Relation objects to work with a batch of records.
141
+ #
142
+ # Person.where("age > 21").in_batches do |relation|
143
+ # relation.delete_all
144
+ # sleep(10) # Throttle the delete queries
145
+ # end
146
+ #
147
+ # If you do not provide a block to #in_batches, it will return a
148
+ # BatchEnumerator which is enumerable.
149
+ #
150
+ # Person.in_batches.each_with_index do |relation, batch_index|
151
+ # puts "Processing relation ##{batch_index}"
152
+ # relation.delete_all
153
+ # end
154
+ #
155
+ # Examples of calling methods on the returned BatchEnumerator object:
156
+ #
157
+ # Person.in_batches.delete_all
158
+ # Person.in_batches.update_all(awesome: true)
159
+ # Person.in_batches.each_record(&:party_all_night!)
160
+ #
161
+ # ==== Options
162
+ # * <tt>:of</tt> - Specifies the size of the batch. Defaults to 1000.
163
+ # * <tt>:load</tt> - Specifies if the relation should be loaded. Defaults to false.
164
+ # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
165
+ # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
166
+ # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
167
+ # an order is present in the relation.
168
+ #
169
+ # Limits are honored, and if present there is no requirement for the batch
170
+ # size, it can be less than, equal, or greater than the limit.
171
+ #
172
+ # The options +start+ and +finish+ are especially useful if you want
173
+ # multiple workers dealing with the same processing queue. You can make
174
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
175
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
176
+ # option on each worker.
177
+ #
178
+ # # Let's process from record 10_000 on.
179
+ # Person.in_batches(start: 10_000).update_all(awesome: true)
180
+ #
181
+ # An example of calling where query method on the relation:
182
+ #
183
+ # Person.in_batches.each do |relation|
184
+ # relation.update_all('age = age + 1')
185
+ # relation.where('age > 21').update_all(should_party: true)
186
+ # relation.where('age <= 21').delete_all
187
+ # end
188
+ #
189
+ # NOTE: If you are going to iterate through each record, you should call
190
+ # #each_record on the yielded BatchEnumerator:
191
+ #
192
+ # Person.in_batches.each_record(&:party_all_night!)
193
+ #
194
+ # NOTE: It's not possible to set the order. That is automatically set to
195
+ # ascending on the primary key ("id ASC") to make the batch ordering
196
+ # consistent. Therefore the primary key must be orderable, e.g. an integer
197
+ # or a string.
198
+ #
199
+ # NOTE: By its nature, batch processing is subject to race conditions if
200
+ # other processes are modifying the database.
201
+ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
202
+ relation = self
203
+ unless block_given?
204
+ return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
205
+ end
206
+
207
+ if arel.orders.present?
208
+ act_on_ignored_order(error_on_ignore)
209
+ end
210
+
211
+ batch_limit = of
212
+ if limit_value
213
+ remaining = limit_value
214
+ batch_limit = remaining if remaining < batch_limit
114
215
  end
115
216
 
116
- relation = relation.reorder(batch_order).limit(batch_size)
117
- records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
217
+ relation = relation.reorder(batch_order).limit(batch_limit)
218
+ relation = apply_limits(relation, start, finish)
219
+ relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
220
+ batch_relation = relation
221
+
222
+ loop do
223
+ if load
224
+ records = batch_relation.records
225
+ ids = records.map(&:id)
226
+ yielded_relation = where(primary_key => ids)
227
+ yielded_relation.load_records(records)
228
+ else
229
+ ids = batch_relation.pluck(primary_key)
230
+ yielded_relation = where(primary_key => ids)
231
+ end
232
+
233
+ break if ids.empty?
118
234
 
119
- while records.any?
120
- records_size = records.size
121
- primary_key_offset = records.last.id
122
- raise "Primary key not included in the custom select clause" unless primary_key_offset
235
+ primary_key_offset = ids.last
236
+ raise ArgumentError.new("Primary key not included in the custom select clause") unless primary_key_offset
123
237
 
124
- yield records
238
+ yield yielded_relation
125
239
 
126
- break if records_size < batch_size
240
+ break if ids.length < batch_limit
127
241
 
128
- records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
242
+ if limit_value
243
+ remaining -= ids.length
244
+
245
+ if remaining == 0
246
+ # Saves a useless iteration when the limit is a multiple of the
247
+ # batch size.
248
+ break
249
+ elsif remaining < batch_limit
250
+ relation = relation.limit(remaining)
251
+ end
252
+ end
253
+
254
+ batch_relation = relation.where(
255
+ bind_attribute(primary_key, primary_key_offset) { |attr, bind| attr.gt(bind) }
256
+ )
129
257
  end
130
258
  end
131
259
 
132
260
  private
133
261
 
134
- def batch_order
135
- "#{quoted_table_name}.#{quoted_primary_key} ASC"
136
- end
262
+ def apply_limits(relation, start, finish)
263
+ relation = apply_start_limit(relation, start) if start
264
+ relation = apply_finish_limit(relation, finish) if finish
265
+ relation
266
+ end
267
+
268
+ def apply_start_limit(relation, start)
269
+ relation.where(bind_attribute(primary_key, start) { |attr, bind| attr.gteq(bind) })
270
+ end
271
+
272
+ def apply_finish_limit(relation, finish)
273
+ relation.where(bind_attribute(primary_key, finish) { |attr, bind| attr.lteq(bind) })
274
+ end
275
+
276
+ def batch_order
277
+ arel_attribute(primary_key).asc
278
+ end
279
+
280
+ def act_on_ignored_order(error_on_ignore)
281
+ raise_error = (error_on_ignore.nil? ? klass.error_on_ignored_order : error_on_ignore)
282
+
283
+ if raise_error
284
+ raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
285
+ elsif logger
286
+ logger.warn(ORDER_IGNORE_MESSAGE)
287
+ end
288
+ end
137
289
  end
138
290
  end