activerecord 4.2.0 → 6.1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1221 -796
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +267 -249
  8. data/lib/active_record/association_relation.rb +45 -7
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +172 -67
  11. data/lib/active_record/associations/association_scope.rb +105 -129
  12. data/lib/active_record/associations/belongs_to_association.rb +85 -59
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -33
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +168 -279
  22. data/lib/active_record/associations/collection_proxy.rb +263 -155
  23. data/lib/active_record/associations/foreign_association.rb +33 -0
  24. data/lib/active_record/associations/has_many_association.rb +57 -84
  25. data/lib/active_record/associations/has_many_through_association.rb +70 -82
  26. data/lib/active_record/associations/has_one_association.rb +74 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
  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 +175 -164
  32. data/lib/active_record/associations/preloader/association.rb +107 -112
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +99 -96
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +49 -24
  37. data/lib/active_record/associations.rb +1845 -1597
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -138
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -36
  46. data/lib/active_record/attribute_methods/write.rb +25 -56
  47. data/lib/active_record/attribute_methods.rb +153 -162
  48. data/lib/active_record/attributes.rb +234 -70
  49. data/lib/active_record/autosave_association.rb +157 -69
  50. data/lib/active_record/base.rb +49 -50
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +46 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +271 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +78 -161
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -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 +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +499 -293
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +595 -382
  119. data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -389
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +488 -243
  133. data/lib/active_record/counter_cache.rb +71 -50
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +273 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +212 -94
  143. data/lib/active_record/errors.rb +225 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +11 -6
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +273 -496
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +64 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +52 -0
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +103 -95
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +298 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +685 -309
  170. data/lib/active_record/model_schema.rb +420 -113
  171. data/lib/active_record/nested_attributes.rb +265 -216
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +574 -135
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +175 -54
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +533 -216
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +485 -310
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +326 -244
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +318 -256
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +99 -84
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +139 -96
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -409
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +23 -21
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -342
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -26
  209. data/lib/active_record/scoping/default.rb +96 -82
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +133 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +366 -129
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +291 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +181 -152
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -41
  231. data/lib/active_record/type/date_time.rb +4 -38
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +33 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +65 -48
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +44 -28
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -10
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +175 -65
  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_decorators.rb +0 -66
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -30
  363. data/lib/active_record/type/decimal.rb +0 -40
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -55
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -36
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -101
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  374. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,113 +1,63 @@
1
- # -*- coding: utf-8 -*-
2
- require 'arel/collectors/bind'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module ActiveRecord
5
- # = Active Record Relation
4
+ # = Active Record \Relation
6
5
  class Relation
7
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
8
- :order, :joins, :where, :having, :bind, :references,
9
- :extending, :unscope]
7
+ :order, :joins, :left_outer_joins, :references,
8
+ :extending, :unscope, :optimizer_hints, :annotate]
10
9
 
11
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
12
- :reverse_order, :distinct, :create_with, :uniq]
13
- INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
11
+ :reverse_order, :distinct, :create_with, :skip_query_cache]
14
12
 
15
- VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
13
+ CLAUSE_METHODS = [:where, :having, :from]
14
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
16
15
 
16
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
17
+
18
+ include Enumerable
17
19
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18
20
 
19
- attr_reader :table, :klass, :loaded
21
+ attr_reader :table, :klass, :loaded, :predicate_builder
22
+ attr_accessor :skip_preloading_value
20
23
  alias :model :klass
21
24
  alias :loaded? :loaded
25
+ alias :locked? :lock_value
22
26
 
23
- def initialize(klass, table, values = {})
27
+ def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
24
28
  @klass = klass
25
29
  @table = table
26
30
  @values = values
27
- @offsets = {}
28
31
  @loaded = false
32
+ @predicate_builder = predicate_builder
33
+ @delegate_to_klass = false
29
34
  end
30
35
 
31
36
  def initialize_copy(other)
32
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
33
- # https://bugs.ruby-lang.org/issues/7166
34
- @values = Hash[@values]
35
- @values[:bind] = @values[:bind].dup if @values.key? :bind
37
+ @values = @values.dup
36
38
  reset
37
39
  end
38
40
 
39
- def insert(values) # :nodoc:
40
- primary_key_value = nil
41
-
42
- if primary_key && Hash === values
43
- primary_key_value = values[values.keys.find { |k|
44
- k.name == primary_key
45
- }]
46
-
47
- if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
48
- primary_key_value = connection.next_sequence_value(klass.sequence_name)
49
- values[klass.arel_table[klass.primary_key]] = primary_key_value
50
- end
51
- end
52
-
53
- im = arel.create_insert
54
- im.into @table
55
-
56
- substitutes, binds = substitute_values values
57
-
58
- if values.empty? # empty insert
59
- im.values = Arel.sql(connection.empty_insert_statement_value)
60
- else
61
- im.insert substitutes
62
- end
63
-
64
- @klass.connection.insert(
65
- im,
66
- 'SQL',
67
- primary_key,
68
- primary_key_value,
69
- nil,
70
- binds)
41
+ def arel_attribute(name) # :nodoc:
42
+ table[name]
71
43
  end
44
+ deprecate :arel_attribute
72
45
 
73
- def _update_record(values, id, id_was) # :nodoc:
74
- substitutes, binds = substitute_values values
75
-
76
- scope = @klass.unscoped
77
-
78
- if @klass.finder_needs_type_condition?
79
- scope.unscope!(where: @klass.inheritance_column)
46
+ def bind_attribute(name, value) # :nodoc:
47
+ if reflection = klass._reflect_on_association(name)
48
+ name = reflection.foreign_key
49
+ value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
80
50
  end
81
51
 
82
- relation = scope.where(@klass.primary_key => (id_was || id))
83
- bvs = binds + relation.bind_values
84
- um = relation
85
- .arel
86
- .compile_update(substitutes, @klass.primary_key)
87
-
88
- @klass.connection.update(
89
- um,
90
- 'SQL',
91
- bvs,
92
- )
93
- end
94
-
95
- def substitute_values(values) # :nodoc:
96
- binds = values.map do |arel_attr, value|
97
- [@klass.columns_hash[arel_attr.name], value]
98
- end
99
-
100
- substitutes = values.each_with_index.map do |(arel_attr, _), i|
101
- [arel_attr, @klass.connection.substitute_at(binds[i][0])]
102
- end
103
-
104
- [substitutes, binds]
52
+ attr = table[name]
53
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
54
+ yield attr, bind
105
55
  end
106
56
 
107
57
  # Initializes new record from relation while maintaining the current
108
58
  # scope.
109
59
  #
110
- # Expects arguments in the same format as +Base.new+.
60
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
111
61
  #
112
62
  # users = User.where(name: 'DHH')
113
63
  # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
@@ -116,39 +66,53 @@ module ActiveRecord
116
66
  #
117
67
  # user = users.new { |user| user.name = 'Oscar' }
118
68
  # user.name # => Oscar
119
- def new(*args, &block)
120
- scoping { @klass.new(*args, &block) }
69
+ def new(attributes = nil, &block)
70
+ block = current_scope_restoring_block(&block)
71
+ scoping { _new(attributes, &block) }
121
72
  end
122
-
123
73
  alias build new
124
74
 
125
75
  # Tries to create a new record with the same scoped attributes
126
76
  # defined in the relation. Returns the initialized object if validation fails.
127
77
  #
128
- # Expects arguments in the same format as +Base.create+.
78
+ # Expects arguments in the same format as
79
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
129
80
  #
130
81
  # ==== Examples
82
+ #
131
83
  # users = User.where(name: 'Oscar')
132
- # users.create # #<User id: 3, name: "oscar", ...>
84
+ # users.create # => #<User id: 3, name: "Oscar", ...>
133
85
  #
134
86
  # users.create(name: 'fxn')
135
- # users.create # #<User id: 4, name: "fxn", ...>
87
+ # users.create # => #<User id: 4, name: "fxn", ...>
136
88
  #
137
89
  # users.create { |user| user.name = 'tenderlove' }
138
- # # #<User id: 5, name: "tenderlove", ...>
90
+ # # => #<User id: 5, name: "tenderlove", ...>
139
91
  #
140
92
  # users.create(name: nil) # validation on name
141
- # # #<User id: nil, name: nil, ...>
142
- def create(*args, &block)
143
- scoping { @klass.create(*args, &block) }
93
+ # # => #<User id: nil, name: nil, ...>
94
+ def create(attributes = nil, &block)
95
+ if attributes.is_a?(Array)
96
+ attributes.collect { |attr| create(attr, &block) }
97
+ else
98
+ block = current_scope_restoring_block(&block)
99
+ scoping { _create(attributes, &block) }
100
+ end
144
101
  end
145
102
 
146
- # Similar to #create, but calls +create!+ on the base class. Raises
147
- # an exception if a validation error occurs.
103
+ # Similar to #create, but calls
104
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
105
+ # on the base class. Raises an exception if a validation error occurs.
148
106
  #
149
- # Expects arguments in the same format as <tt>Base.create!</tt>.
150
- def create!(*args, &block)
151
- scoping { @klass.create!(*args, &block) }
107
+ # Expects arguments in the same format as
108
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
109
+ def create!(attributes = nil, &block)
110
+ if attributes.is_a?(Array)
111
+ attributes.collect { |attr| create!(attr, &block) }
112
+ else
113
+ block = current_scope_restoring_block(&block)
114
+ scoping { _create!(attributes, &block) }
115
+ end
152
116
  end
153
117
 
154
118
  def first_or_create(attributes = nil, &block) # :nodoc:
@@ -180,7 +144,7 @@ module ActiveRecord
180
144
  # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
181
145
  # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
182
146
  #
183
- # This method accepts a block, which is passed down to +create+. The last example
147
+ # This method accepts a block, which is passed down to #create. The last example
184
148
  # above can be alternatively written this way:
185
149
  #
186
150
  # # Find the first user named "Scarlett" or create a new one with a
@@ -192,34 +156,72 @@ module ActiveRecord
192
156
  #
193
157
  # This method always returns a record, but if creation was attempted and
194
158
  # failed due to validation errors it won't be persisted, you get what
195
- # +create+ returns in such situation.
159
+ # #create returns in such situation.
196
160
  #
197
- # Please note *this method is not atomic*, it runs first a SELECT, and if
161
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
198
162
  # there are no results an INSERT is attempted. If there are other threads
199
163
  # or processes there is a race condition between both calls and it could
200
164
  # be the case that you end up with two similar records.
201
165
  #
202
- # Whether that is a problem or not depends on the logic of the
203
- # application, but in the particular case in which rows have a UNIQUE
204
- # constraint an exception may be raised, just retry:
205
- #
206
- # begin
207
- # CreditAccount.find_or_create_by(user_id: user.id)
208
- # rescue ActiveRecord::RecordNotUnique
209
- # retry
210
- # end
211
- #
166
+ # If this might be a problem for your application, please see #create_or_find_by.
212
167
  def find_or_create_by(attributes, &block)
213
168
  find_by(attributes) || create(attributes, &block)
214
169
  end
215
170
 
216
- # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
171
+ # Like #find_or_create_by, but calls
172
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
173
  # is raised if the created record is invalid.
218
174
  def find_or_create_by!(attributes, &block)
219
175
  find_by(attributes) || create!(attributes, &block)
220
176
  end
221
177
 
222
- # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
178
+ # Attempts to create a record with the given attributes in a table that has a unique constraint
179
+ # on one or several of its columns. If a row already exists with one or several of these
180
+ # unique constraints, the exception such an insertion would normally raise is caught,
181
+ # and the existing record with those attributes is found using #find_by!.
182
+ #
183
+ # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
184
+ # and the INSERT, as that method needs to first query the table, then attempt to insert a row
185
+ # if none is found.
186
+ #
187
+ # There are several drawbacks to #create_or_find_by, though:
188
+ #
189
+ # * The underlying table must have the relevant columns defined with unique constraints.
190
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
191
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
+ # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
193
+ # rather than a record with the given attributes.
194
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
195
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
196
+ # if a DELETE between those two statements is run by another client. But for most applications,
197
+ # that's a significantly less likely condition to hit.
198
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
199
+ # * The primary key may auto-increment on each create, even if it fails. This can accelerate
200
+ # the problem of running out of integers, if the underlying table is still stuck on a primary
201
+ # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
202
+ # to this problem).
203
+ #
204
+ # This method will return a record if all given attributes are covered by unique constraints
205
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
206
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
207
+ # such situation.
208
+ def create_or_find_by(attributes, &block)
209
+ transaction(requires_new: true) { create(attributes, &block) }
210
+ rescue ActiveRecord::RecordNotUnique
211
+ find_by!(attributes)
212
+ end
213
+
214
+ # Like #create_or_find_by, but calls
215
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
216
+ # is raised if the created record is invalid.
217
+ def create_or_find_by!(attributes, &block)
218
+ transaction(requires_new: true) { create!(attributes, &block) }
219
+ rescue ActiveRecord::RecordNotUnique
220
+ find_by!(attributes)
221
+ end
222
+
223
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
224
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
223
225
  def find_or_initialize_by(attributes, &block)
224
226
  find_by(attributes) || new(attributes, &block)
225
227
  end
@@ -232,25 +234,25 @@ module ActiveRecord
232
234
  # are needed by the next ones when eager loading is going on.
233
235
  #
234
236
  # Please see further details in the
235
- # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
237
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
236
238
  def explain
237
- #TODO: Fix for binds.
238
239
  exec_explain(collecting_queries_for_explain { exec_queries })
239
240
  end
240
241
 
241
242
  # Converts relation objects to Array.
242
- def to_a
243
+ def to_ary
244
+ records.dup
245
+ end
246
+ alias to_a to_ary
247
+
248
+ def records # :nodoc:
243
249
  load
244
250
  @records
245
251
  end
246
252
 
247
253
  # Serializes the relation objects Array.
248
254
  def encode_with(coder)
249
- coder.represent_seq(nil, to_a)
250
- end
251
-
252
- def as_json(options = nil) #:nodoc:
253
- to_a.as_json(options)
255
+ coder.represent_seq(nil, records)
254
256
  end
255
257
 
256
258
  # Returns size of the records.
@@ -261,30 +263,133 @@ module ActiveRecord
261
263
  # Returns true if there are no records.
262
264
  def empty?
263
265
  return @records.empty? if loaded?
266
+ !exists?
267
+ end
264
268
 
265
- if limit_value == 0
266
- true
267
- else
268
- c = count(:all)
269
- c.respond_to?(:zero?) ? c.zero? : c.empty?
270
- end
269
+ # Returns true if there are no records.
270
+ def none?
271
+ return super if block_given?
272
+ empty?
271
273
  end
272
274
 
273
275
  # Returns true if there are any records.
274
276
  def any?
275
- if block_given?
276
- to_a.any? { |*block_args| yield(*block_args) }
277
- else
278
- !empty?
279
- end
277
+ return super if block_given?
278
+ !empty?
279
+ end
280
+
281
+ # Returns true if there is exactly one record.
282
+ def one?
283
+ return super if block_given?
284
+ limit_value ? records.one? : size == 1
280
285
  end
281
286
 
282
287
  # Returns true if there is more than one record.
283
288
  def many?
284
- if block_given?
285
- to_a.many? { |*block_args| yield(*block_args) }
289
+ return super if block_given?
290
+ limit_value ? records.many? : size > 1
291
+ end
292
+
293
+ # Returns a stable cache key that can be used to identify this query.
294
+ # The cache key is built with a fingerprint of the SQL query.
295
+ #
296
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
297
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
298
+ #
299
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
300
+ # in Rails 6.0 and earlier, the cache key will also include a version.
301
+ #
302
+ # ActiveRecord::Base.collection_cache_versioning = false
303
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
304
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
305
+ #
306
+ # You can also pass a custom timestamp column to fetch the timestamp of the
307
+ # last updated record.
308
+ #
309
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
310
+ def cache_key(timestamp_column = "updated_at")
311
+ @cache_keys ||= {}
312
+ @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
313
+ end
314
+
315
+ def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
316
+ query_signature = ActiveSupport::Digest.hexdigest(to_sql)
317
+ key = "#{klass.model_name.cache_key}/query-#{query_signature}"
318
+
319
+ if collection_cache_versioning
320
+ key
286
321
  else
287
- limit_value ? to_a.many? : size > 1
322
+ "#{key}-#{compute_cache_version(timestamp_column)}"
323
+ end
324
+ end
325
+ private :compute_cache_key
326
+
327
+ # Returns a cache version that can be used together with the cache key to form
328
+ # a recyclable caching scheme. The cache version is built with the number of records
329
+ # matching the query, and the timestamp of the last updated record. When a new record
330
+ # comes to match the query, or any of the existing records is updated or deleted,
331
+ # the cache version changes.
332
+ #
333
+ # If the collection is loaded, the method will iterate through the records
334
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
335
+ #
336
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
337
+ def cache_version(timestamp_column = :updated_at)
338
+ if collection_cache_versioning
339
+ @cache_versions ||= {}
340
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
341
+ end
342
+ end
343
+
344
+ def compute_cache_version(timestamp_column) # :nodoc:
345
+ timestamp_column = timestamp_column.to_s
346
+
347
+ if loaded? || distinct_value
348
+ size = records.size
349
+ if size > 0
350
+ timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
351
+ end
352
+ else
353
+ collection = eager_loading? ? apply_join_dependency : self
354
+
355
+ column = connection.visitor.compile(table[timestamp_column])
356
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
357
+
358
+ if collection.has_limit_or_offset?
359
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
360
+ subquery_alias = "subquery_for_cache_key"
361
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
363
+ else
364
+ query = collection.unscope(:order)
365
+ query.select_values = [select_values % column]
366
+ arel = query.arel
367
+ end
368
+
369
+ size, timestamp = connection.select_rows(arel, nil).first
370
+
371
+ if size
372
+ column_type = klass.type_for_attribute(timestamp_column)
373
+ timestamp = column_type.deserialize(timestamp)
374
+ else
375
+ size = 0
376
+ end
377
+ end
378
+
379
+ if timestamp
380
+ "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
381
+ else
382
+ "#{size}"
383
+ end
384
+ end
385
+ private :compute_cache_version
386
+
387
+ # Returns a cache key along with the version.
388
+ def cache_key_with_version
389
+ if version = cache_version
390
+ "#{cache_key}-#{version}"
391
+ else
392
+ cache_key
288
393
  end
289
394
  end
290
395
 
@@ -298,17 +403,22 @@ module ActiveRecord
298
403
  # Please check unscoped if you want to remove all previous scopes (including
299
404
  # the default_scope) during the execution of a block.
300
405
  def scoping
301
- previous, klass.current_scope = klass.current_scope, self
302
- yield
406
+ already_in_scope? ? yield : _scoping(self) { yield }
407
+ end
408
+
409
+ def _exec_scope(*args, &block) # :nodoc:
410
+ @delegate_to_klass = true
411
+ _scoping(nil) { instance_exec(*args, &block) || self }
303
412
  ensure
304
- klass.current_scope = previous
413
+ @delegate_to_klass = false
305
414
  end
306
415
 
307
416
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
308
417
  # statement and sends it straight to the database. It does not instantiate the involved models and it does not
309
- # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
310
- # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
311
- # database.
418
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
419
+ # Active Record's normal type casting and serialization. Returns the number of rows affected.
420
+ #
421
+ # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
312
422
  #
313
423
  # ==== Parameters
314
424
  #
@@ -324,184 +434,190 @@ module ActiveRecord
324
434
  #
325
435
  # # Update all books that match conditions, but limit it to 5 ordered by date
326
436
  # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
437
+ #
438
+ # # Update all invoices and set the number column to its id value.
439
+ # Invoice.update_all('number = id')
327
440
  def update_all(updates)
328
441
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
329
442
 
330
- stmt = Arel::UpdateManager.new(arel.engine)
443
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
444
+ arel.source.left = table
331
445
 
332
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
333
- stmt.table(table)
446
+ stmt = Arel::UpdateManager.new
447
+ stmt.table(arel.source)
334
448
  stmt.key = table[primary_key]
335
-
336
- if joins_values.any?
337
- @klass.connection.join_to_update(stmt, arel)
449
+ stmt.take(arel.limit)
450
+ stmt.offset(arel.offset)
451
+ stmt.order(*arel.orders)
452
+ stmt.wheres = arel.constraints
453
+
454
+ if updates.is_a?(Hash)
455
+ if klass.locking_enabled? &&
456
+ !updates.key?(klass.locking_column) &&
457
+ !updates.key?(klass.locking_column.to_sym)
458
+ attr = table[klass.locking_column]
459
+ updates[attr.name] = _increment_attribute(attr)
460
+ end
461
+ stmt.set _substitute_values(updates)
338
462
  else
339
- stmt.take(arel.limit)
340
- stmt.order(*arel.orders)
341
- stmt.wheres = arel.constraints
463
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
342
464
  end
343
465
 
344
- bvs = arel.bind_values + bind_values
345
- @klass.connection.update stmt, 'SQL', bvs
466
+ klass.connection.update(stmt, "#{klass} Update All").tap { reset }
346
467
  end
347
468
 
348
- # Updates an object (or multiple objects) and saves it to the database, if validations pass.
349
- # The resulting object is returned whether the object was saved successfully to the database or not.
469
+ def update(id = :all, attributes) # :nodoc:
470
+ if id == :all
471
+ each { |record| record.update(attributes) }
472
+ else
473
+ klass.update(id, attributes)
474
+ end
475
+ end
476
+
477
+ # Updates the counters of the records in the current relation.
350
478
  #
351
479
  # ==== Parameters
352
480
  #
353
- # * +id+ - This should be the id or an array of ids to be updated.
354
- # * +attributes+ - This should be a hash of attributes or an array of hashes.
481
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
482
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
483
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
355
484
  #
356
485
  # ==== Examples
357
486
  #
358
- # # Updates one record
359
- # Person.update(15, user_name: 'Samuel', group: 'expert')
360
- #
361
- # # Updates multiple records
362
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
363
- # Person.update(people.keys, people.values)
364
- def update(id, attributes)
365
- if id.is_a?(Array)
366
- id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
367
- else
368
- object = find(id)
369
- object.update(attributes)
370
- object
487
+ # # For Posts by a given author increment the comment_count by 1.
488
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
489
+ def update_counters(counters)
490
+ touch = counters.delete(:touch)
491
+
492
+ updates = {}
493
+ counters.each do |counter_name, value|
494
+ attr = table[counter_name]
495
+ updates[attr.name] = _increment_attribute(attr, value)
496
+ end
497
+
498
+ if touch
499
+ names = touch if touch != true
500
+ names = Array.wrap(names)
501
+ options = names.extract_options!
502
+ touch_updates = klass.touch_attributes_with_time(*names, **options)
503
+ updates.merge!(touch_updates) unless touch_updates.empty?
371
504
  end
505
+
506
+ update_all updates
372
507
  end
373
508
 
374
- # Destroys the records matching +conditions+ by instantiating each
375
- # record and calling its +destroy+ method. Each object's callbacks are
376
- # executed (including <tt>:dependent</tt> association options). Returns the
377
- # collection of objects that were destroyed; each will be frozen, to
378
- # reflect that no changes should be made (since they can't be persisted).
509
+ # Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
510
+ # It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
511
+ # This method can be passed attribute names and an optional time argument.
512
+ # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
513
+ # If no time argument is passed, the current time is used as default.
379
514
  #
380
- # Note: Instantiation, callback execution, and deletion of each
381
- # record can be time consuming when you're removing many records at
382
- # once. It generates at least one SQL +DELETE+ query per record (or
383
- # possibly more, to enforce your callbacks). If you want to delete many
384
- # rows quickly, without concern for their associations or callbacks, use
385
- # +delete_all+ instead.
515
+ # === Examples
386
516
  #
387
- # ==== Parameters
517
+ # # Touch all records
518
+ # Person.all.touch_all
519
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
388
520
  #
389
- # * +conditions+ - A string, array, or hash that specifies which records
390
- # to destroy. If omitted, all records are destroyed. See the
391
- # Conditions section in the introduction to ActiveRecord::Base for
392
- # more information.
521
+ # # Touch multiple records with a custom attribute
522
+ # Person.all.touch_all(:created_at)
523
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
393
524
  #
394
- # ==== Examples
525
+ # # Touch multiple records with a specified time
526
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
527
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
395
528
  #
396
- # Person.destroy_all("last_login < '2004-04-04'")
397
- # Person.destroy_all(status: "inactive")
398
- # Person.where(age: 0..18).destroy_all
399
- def destroy_all(conditions = nil)
400
- if conditions
401
- where(conditions).destroy_all
402
- else
403
- to_a.each {|object| object.destroy }.tap { reset }
404
- end
529
+ # # Touch records with scope
530
+ # Person.where(name: 'David').touch_all
531
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
532
+ def touch_all(*names, time: nil)
533
+ update_all klass.touch_attributes_with_time(*names, time: time)
405
534
  end
406
535
 
407
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
408
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
409
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
410
- #
411
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
412
- # from the attributes, and then calls destroy on it.
413
- #
414
- # ==== Parameters
536
+ # Destroys the records by instantiating each
537
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
538
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
539
+ # Returns the collection of objects that were destroyed; each will be frozen, to
540
+ # reflect that no changes should be made (since they can't be persisted).
415
541
  #
416
- # * +id+ - Can be either an Integer or an Array of Integers.
542
+ # Note: Instantiation, callback execution, and deletion of each
543
+ # record can be time consuming when you're removing many records at
544
+ # once. It generates at least one SQL +DELETE+ query per record (or
545
+ # possibly more, to enforce your callbacks). If you want to delete many
546
+ # rows quickly, without concern for their associations or callbacks, use
547
+ # #delete_all instead.
417
548
  #
418
549
  # ==== Examples
419
550
  #
420
- # # Destroy a single object
421
- # Todo.destroy(1)
422
- #
423
- # # Destroy multiple objects
424
- # todos = [1,2,3]
425
- # Todo.destroy(todos)
426
- def destroy(id)
427
- if id.is_a?(Array)
428
- id.map { |one_id| destroy(one_id) }
429
- else
430
- find(id).destroy
431
- end
551
+ # Person.where(age: 0..18).destroy_all
552
+ def destroy_all
553
+ records.each(&:destroy).tap { reset }
432
554
  end
433
555
 
434
- # Deletes the records matching +conditions+ without instantiating the records
435
- # first, and hence not calling the +destroy+ method nor invoking callbacks. This
436
- # is a single SQL DELETE statement that goes straight to the database, much more
437
- # efficient than +destroy_all+. Be careful with relations though, in particular
556
+ # Deletes the records without instantiating the records
557
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
558
+ # method nor invoking callbacks.
559
+ # This is a single SQL DELETE statement that goes straight to the database, much more
560
+ # efficient than #destroy_all. Be careful with relations though, in particular
438
561
  # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
439
562
  # number of rows affected.
440
563
  #
441
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
442
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
443
564
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
444
565
  #
445
566
  # Both calls delete the affected posts all at once with a single DELETE statement.
446
567
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
447
- # +after_destroy+ callbacks, use the +destroy_all+ method instead.
568
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
448
569
  #
449
- # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
570
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
450
571
  #
451
- # Post.limit(100).delete_all
452
- # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
453
- def delete_all(conditions = nil)
454
- invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
455
- if MULTI_VALUE_METHODS.include?(method)
456
- send("#{method}_values").any?
457
- else
458
- send("#{method}_value")
459
- end
460
- }
572
+ # Post.distinct.delete_all
573
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
574
+ def delete_all
575
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
576
+ value = @values[method]
577
+ method == :distinct ? value : value&.any?
578
+ end
461
579
  if invalid_methods.any?
462
580
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
463
581
  end
464
582
 
465
- if conditions
466
- where(conditions).delete_all
467
- else
468
- stmt = Arel::DeleteManager.new(arel.engine)
469
- stmt.from(table)
470
-
471
- if joins_values.any?
472
- @klass.connection.join_to_delete(stmt, arel, table[primary_key])
473
- else
474
- stmt.wheres = arel.constraints
475
- end
583
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
584
+ arel.source.left = table
476
585
 
477
- affected = @klass.connection.delete(stmt, 'SQL', bind_values)
586
+ stmt = Arel::DeleteManager.new
587
+ stmt.from(arel.source)
588
+ stmt.key = table[primary_key]
589
+ stmt.take(arel.limit)
590
+ stmt.offset(arel.offset)
591
+ stmt.order(*arel.orders)
592
+ stmt.wheres = arel.constraints
478
593
 
479
- reset
480
- affected
481
- end
594
+ klass.connection.delete(stmt, "#{klass} Destroy").tap { reset }
482
595
  end
483
596
 
484
- # Deletes the row with a primary key matching the +id+ argument, using a
485
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
486
- # Record objects are not instantiated, so the object's callbacks are not
487
- # executed, including any <tt>:dependent</tt> association options.
597
+ # Finds and destroys all records matching the specified conditions.
598
+ # This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
599
+ # Returns the collection of objects that were destroyed.
488
600
  #
489
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
601
+ # If no record is found, returns empty array.
490
602
  #
491
- # Note: Although it is often much faster than the alternative,
492
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
493
- # your application that ensures referential integrity or performs other
494
- # essential jobs.
495
- #
496
- # ==== Examples
603
+ # Person.destroy_by(id: 13)
604
+ # Person.destroy_by(name: 'Spartacus', rating: 4)
605
+ # Person.destroy_by("published_at < ?", 2.weeks.ago)
606
+ def destroy_by(*args)
607
+ where(*args).destroy_all
608
+ end
609
+
610
+ # Finds and deletes all records matching the specified conditions.
611
+ # This is short-hand for <tt>relation.where(condition).delete_all</tt>.
612
+ # Returns the number of rows affected.
497
613
  #
498
- # # Delete a single row
499
- # Todo.delete(1)
614
+ # If no record is found, returns <tt>0</tt> as zero rows were affected.
500
615
  #
501
- # # Delete multiple rows
502
- # Todo.delete([2,3,4])
503
- def delete(id_or_array)
504
- where(primary_key => id_or_array).delete_all
616
+ # Person.delete_by(id: 13)
617
+ # Person.delete_by(name: 'Spartacus', rating: 4)
618
+ # Person.delete_by("published_at < ?", 2.weeks.ago)
619
+ def delete_by(*args)
620
+ where(*args).delete_all
505
621
  end
506
622
 
507
623
  # Causes the records to be loaded from the database if they have not
@@ -510,8 +626,11 @@ module ActiveRecord
510
626
  # return value is the relation itself, not the records.
511
627
  #
512
628
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
513
- def load
514
- exec_queries unless loaded?
629
+ def load(&block)
630
+ unless loaded?
631
+ @records = exec_queries(&block)
632
+ @loaded = true
633
+ end
515
634
 
516
635
  self
517
636
  end
@@ -523,10 +642,11 @@ module ActiveRecord
523
642
  end
524
643
 
525
644
  def reset
526
- @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
527
- @should_eager_load = @join_dependency = nil
528
- @records = []
529
- @offsets = {}
645
+ @delegate_to_klass = false
646
+ @to_sql = @arel = @loaded = @should_eager_load = nil
647
+ @offsets = @take = nil
648
+ @cache_keys = nil
649
+ @records = [].freeze
530
650
  self
531
651
  end
532
652
 
@@ -536,47 +656,30 @@ module ActiveRecord
536
656
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
537
657
  def to_sql
538
658
  @to_sql ||= begin
539
- relation = self
540
- connection = klass.connection
541
- visitor = connection.visitor
542
-
543
- if eager_loading?
544
- find_with_associations { |rel| relation = rel }
545
- end
546
-
547
- arel = relation.arel
548
- binds = (arel.bind_values + relation.bind_values).dup
549
- binds.map! { |bv| connection.quote(*bv.reverse) }
550
- collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
551
- collect.substitute_binds(binds).join
552
- end
659
+ if eager_loading?
660
+ apply_join_dependency do |relation, join_dependency|
661
+ relation = join_dependency.apply_column_aliases(relation)
662
+ relation.to_sql
663
+ end
664
+ else
665
+ conn = klass.connection
666
+ conn.unprepared_statement { conn.to_sql(arel) }
667
+ end
668
+ end
553
669
  end
554
670
 
555
671
  # Returns a hash of where conditions.
556
672
  #
557
673
  # User.where(name: 'Oscar').where_values_hash
558
674
  # # => {name: "Oscar"}
559
- def where_values_hash(relation_table_name = table_name)
560
- equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
561
- node.left.relation.name == relation_table_name
562
- }
563
-
564
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
565
-
566
- Hash[equalities.map { |where|
567
- name = where.left.name
568
- [name, binds.fetch(name.to_s) {
569
- case where.right
570
- when Array then where.right.map(&:val)
571
- else
572
- where.right.val
573
- end
574
- }]
575
- }]
675
+ def where_values_hash(relation_table_name = klass.table_name)
676
+ where_clause.to_h(relation_table_name)
576
677
  end
577
678
 
578
679
  def scope_for_create
579
- @scope_for_create ||= where_values_hash.merge(create_with_value)
680
+ hash = where_clause.to_h(klass.table_name, equality_only: true)
681
+ create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
682
+ hash
580
683
  end
581
684
 
582
685
  # Returns true if relation needs eager loading.
@@ -594,88 +697,197 @@ module ActiveRecord
594
697
  includes_values & joins_values
595
698
  end
596
699
 
597
- # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
598
- # to maintain backwards compatibility. Use +distinct_value+ instead.
599
- def uniq_value
600
- distinct_value
601
- end
602
-
603
700
  # Compares two relations for equality.
604
701
  def ==(other)
605
702
  case other
606
703
  when Associations::CollectionProxy, AssociationRelation
607
- self == other.to_a
704
+ self == other.records
608
705
  when Relation
609
706
  other.to_sql == to_sql
610
707
  when Array
611
- to_a == other
708
+ records == other
612
709
  end
613
710
  end
614
711
 
615
712
  def pretty_print(q)
616
- q.pp(self.to_a)
713
+ q.pp(records)
617
714
  end
618
715
 
619
716
  # Returns true if relation is blank.
620
717
  def blank?
621
- to_a.blank?
718
+ records.blank?
622
719
  end
623
720
 
624
721
  def values
625
- Hash[@values]
722
+ @values.dup
626
723
  end
627
724
 
628
725
  def inspect
629
- entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
630
- entries[10] = '...' if entries.size == 11
726
+ subject = loaded? ? records : annotate("loading for inspect")
727
+ entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
728
+
729
+ entries[10] = "..." if entries.size == 11
631
730
 
632
731
  "#<#{self.class.name} [#{entries.join(', ')}]>"
633
732
  end
634
733
 
635
- private
734
+ def empty_scope? # :nodoc:
735
+ @values == klass.unscoped.values
736
+ end
636
737
 
637
- def exec_queries
638
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
738
+ def has_limit_or_offset? # :nodoc:
739
+ limit_value || offset_value
740
+ end
741
+
742
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
743
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
744
+ end
639
745
 
746
+ class StrictLoadingScope # :nodoc:
747
+ def self.empty_scope?
748
+ true
749
+ end
750
+
751
+ def self.strict_loading_value
752
+ true
753
+ end
754
+ end
755
+
756
+ def preload_associations(records) # :nodoc:
640
757
  preload = preload_values
641
- preload += includes_values unless eager_loading?
642
- preloader = build_preloader
758
+ preload += includes_values unless eager_loading?
759
+ preloader = nil
760
+ scope = strict_loading_value ? StrictLoadingScope : nil
643
761
  preload.each do |associations|
644
- preloader.preload @records, associations
762
+ preloader ||= build_preloader
763
+ preloader.preload records, associations, scope
645
764
  end
765
+ end
646
766
 
647
- @records.each { |record| record.readonly! } if readonly_value
767
+ protected
768
+ def load_records(records)
769
+ @records = records.freeze
770
+ @loaded = true
771
+ end
648
772
 
649
- @loaded = true
650
- @records
651
- end
773
+ def null_relation? # :nodoc:
774
+ is_a?(NullRelation)
775
+ end
652
776
 
653
- def build_preloader
654
- ActiveRecord::Associations::Preloader.new
655
- end
777
+ private
778
+ def already_in_scope?
779
+ @delegate_to_klass && klass.current_scope(true)
780
+ end
781
+
782
+ def current_scope_restoring_block(&block)
783
+ current_scope = klass.current_scope(true)
784
+ -> record do
785
+ klass.current_scope = current_scope
786
+ yield record if block_given?
787
+ end
788
+ end
789
+
790
+ def _new(attributes, &block)
791
+ klass.new(attributes, &block)
792
+ end
793
+
794
+ def _create(attributes, &block)
795
+ klass.create(attributes, &block)
796
+ end
656
797
 
657
- def references_eager_loaded_tables?
658
- joined_tables = arel.join_sources.map do |join|
659
- if join.is_a?(Arel::Nodes::StringJoin)
660
- tables_in_string(join.left)
798
+ def _create!(attributes, &block)
799
+ klass.create!(attributes, &block)
800
+ end
801
+
802
+ def _scoping(scope)
803
+ previous, klass.current_scope = klass.current_scope(true), scope
804
+ yield
805
+ ensure
806
+ klass.current_scope = previous
807
+ end
808
+
809
+ def _substitute_values(values)
810
+ values.map do |name, value|
811
+ attr = table[name]
812
+ unless Arel.arel_node?(value)
813
+ type = klass.type_for_attribute(attr.name)
814
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
815
+ end
816
+ [attr, value]
817
+ end
818
+ end
819
+
820
+ def _increment_attribute(attribute, value = 1)
821
+ bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
822
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
823
+ expr = value < 0 ? expr - bind : expr + bind
824
+ expr.expr
825
+ end
826
+
827
+ def exec_queries(&block)
828
+ skip_query_cache_if_necessary do
829
+ records =
830
+ if where_clause.contradiction?
831
+ []
832
+ elsif eager_loading?
833
+ apply_join_dependency do |relation, join_dependency|
834
+ if relation.null_relation?
835
+ []
836
+ else
837
+ relation = join_dependency.apply_column_aliases(relation)
838
+ rows = connection.select_all(relation.arel, "SQL")
839
+ join_dependency.instantiate(rows, strict_loading_value, &block)
840
+ end.freeze
841
+ end
842
+ else
843
+ klass.find_by_sql(arel, &block).freeze
844
+ end
845
+
846
+ preload_associations(records) unless skip_preloading_value
847
+
848
+ records.each(&:readonly!) if readonly_value
849
+ records.each(&:strict_loading!) if strict_loading_value
850
+
851
+ records
852
+ end
853
+ end
854
+
855
+ def skip_query_cache_if_necessary
856
+ if skip_query_cache_value
857
+ uncached do
858
+ yield
859
+ end
661
860
  else
662
- [join.left.table_name, join.left.table_alias]
861
+ yield
663
862
  end
664
863
  end
665
864
 
666
- joined_tables += [table.name, table.table_alias]
865
+ def build_preloader
866
+ ActiveRecord::Associations::Preloader.new
867
+ end
868
+
869
+ def references_eager_loaded_tables?
870
+ joined_tables = build_joins([]).flat_map do |join|
871
+ if join.is_a?(Arel::Nodes::StringJoin)
872
+ tables_in_string(join.left)
873
+ else
874
+ join.left.name
875
+ end
876
+ end
877
+
878
+ joined_tables << table.name
667
879
 
668
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
669
- joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
880
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
881
+ joined_tables.map!(&:downcase)
670
882
 
671
- (references_values - joined_tables).any?
672
- end
883
+ !(references_values.map(&:to_s) - joined_tables).empty?
884
+ end
673
885
 
674
- def tables_in_string(string)
675
- return [] if string.blank?
676
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
677
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
678
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
679
- end
886
+ def tables_in_string(string)
887
+ return [] if string.blank?
888
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
889
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
+ end
680
892
  end
681
893
  end