activerecord 5.0.6 → 6.0.1

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

Potentially problematic release.


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

Files changed (358) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +638 -2023
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +8 -6
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record/aggregations.rb +249 -246
  8. data/lib/active_record/association_relation.rb +24 -13
  9. data/lib/active_record/associations/alias_tracker.rb +24 -33
  10. data/lib/active_record/associations/association.rb +119 -56
  11. data/lib/active_record/associations/association_scope.rb +94 -94
  12. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  14. data/lib/active_record/associations/builder/association.rb +18 -25
  15. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  16. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -61
  18. data/lib/active_record/associations/builder/has_many.rb +4 -0
  19. data/lib/active_record/associations/builder/has_one.rb +37 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  21. data/lib/active_record/associations/collection_association.rb +80 -252
  22. data/lib/active_record/associations/collection_proxy.rb +158 -121
  23. data/lib/active_record/associations/foreign_association.rb +9 -0
  24. data/lib/active_record/associations/has_many_association.rb +23 -29
  25. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  26. data/lib/active_record/associations/has_one_association.rb +59 -54
  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 -90
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -176
  32. data/lib/active_record/associations/preloader/association.rb +84 -125
  33. data/lib/active_record/associations/preloader/through_association.rb +82 -75
  34. data/lib/active_record/associations/preloader.rb +90 -102
  35. data/lib/active_record/associations/singular_association.rb +12 -45
  36. data/lib/active_record/associations/through_association.rb +26 -14
  37. data/lib/active_record/associations.rb +1603 -1592
  38. data/lib/active_record/attribute_assignment.rb +54 -60
  39. data/lib/active_record/attribute_decorators.rb +38 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -91
  43. data/lib/active_record/attribute_methods/query.rb +4 -3
  44. data/lib/active_record/attribute_methods/read.rb +21 -49
  45. data/lib/active_record/attribute_methods/serialization.rb +30 -7
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
  47. data/lib/active_record/attribute_methods/write.rb +35 -33
  48. data/lib/active_record/attribute_methods.rb +66 -106
  49. data/lib/active_record/attributes.rb +38 -24
  50. data/lib/active_record/autosave_association.rb +53 -32
  51. data/lib/active_record/base.rb +27 -24
  52. data/lib/active_record/callbacks.rb +63 -33
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -11
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
  68. data/lib/active_record/connection_adapters/column.rb +41 -13
  69. data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  71. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +143 -49
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -45
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -54
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -10
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  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 +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -17
  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 -5
  98. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +31 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -30
  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 +58 -54
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -4
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +35 -32
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -275
  116. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  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 +254 -262
  126. data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
  127. data/lib/active_record/connection_handling.rb +159 -40
  128. data/lib/active_record/core.rb +202 -162
  129. data/lib/active_record/counter_cache.rb +57 -28
  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 -86
  136. data/lib/active_record/enum.rb +60 -23
  137. data/lib/active_record/errors.rb +114 -18
  138. data/lib/active_record/explain.rb +4 -3
  139. data/lib/active_record/explain_registry.rb +3 -1
  140. data/lib/active_record/explain_subscriber.rb +9 -4
  141. data/lib/active_record/fixture_set/file.rb +13 -8
  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 +195 -502
  147. data/lib/active_record/gem_version.rb +4 -2
  148. data/lib/active_record/inheritance.rb +151 -97
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +116 -25
  151. data/lib/active_record/internal_metadata.rb +15 -18
  152. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  153. data/lib/active_record/locking/optimistic.rb +78 -87
  154. data/lib/active_record/locking/pessimistic.rb +18 -6
  155. data/lib/active_record/log_subscriber.rb +48 -29
  156. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  157. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/migration/command_recorder.rb +143 -97
  160. data/lib/active_record/migration/compatibility.rb +174 -56
  161. data/lib/active_record/migration/join_table.rb +8 -6
  162. data/lib/active_record/migration.rb +367 -300
  163. data/lib/active_record/model_schema.rb +145 -139
  164. data/lib/active_record/nested_attributes.rb +214 -201
  165. data/lib/active_record/no_touching.rb +10 -1
  166. data/lib/active_record/null_relation.rb +13 -34
  167. data/lib/active_record/persistence.rb +442 -72
  168. data/lib/active_record/query_cache.rb +15 -14
  169. data/lib/active_record/querying.rb +36 -23
  170. data/lib/active_record/railtie.rb +128 -36
  171. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  172. data/lib/active_record/railties/console_sandbox.rb +2 -0
  173. data/lib/active_record/railties/controller_runtime.rb +34 -33
  174. data/lib/active_record/railties/databases.rake +309 -177
  175. data/lib/active_record/readonly_attributes.rb +5 -4
  176. data/lib/active_record/reflection.rb +211 -249
  177. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  178. data/lib/active_record/relation/batches.rb +99 -52
  179. data/lib/active_record/relation/calculations.rb +211 -172
  180. data/lib/active_record/relation/delegation.rb +67 -65
  181. data/lib/active_record/relation/finder_methods.rb +208 -247
  182. data/lib/active_record/relation/from_clause.rb +2 -8
  183. data/lib/active_record/relation/merger.rb +78 -61
  184. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  185. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  186. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  187. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  188. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  189. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  190. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  191. data/lib/active_record/relation/predicate_builder.rb +86 -104
  192. data/lib/active_record/relation/query_attribute.rb +33 -2
  193. data/lib/active_record/relation/query_methods.rb +458 -329
  194. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  195. data/lib/active_record/relation/spawn_methods.rb +8 -7
  196. data/lib/active_record/relation/where_clause.rb +111 -95
  197. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  198. data/lib/active_record/relation.rb +429 -318
  199. data/lib/active_record/result.rb +69 -39
  200. data/lib/active_record/runtime_registry.rb +5 -3
  201. data/lib/active_record/sanitization.rb +83 -99
  202. data/lib/active_record/schema.rb +7 -14
  203. data/lib/active_record/schema_dumper.rb +71 -69
  204. data/lib/active_record/schema_migration.rb +15 -5
  205. data/lib/active_record/scoping/default.rb +93 -95
  206. data/lib/active_record/scoping/named.rb +45 -25
  207. data/lib/active_record/scoping.rb +20 -19
  208. data/lib/active_record/secure_token.rb +4 -2
  209. data/lib/active_record/serialization.rb +2 -0
  210. data/lib/active_record/statement_cache.rb +63 -28
  211. data/lib/active_record/store.rb +121 -41
  212. data/lib/active_record/suppressor.rb +4 -1
  213. data/lib/active_record/table_metadata.rb +26 -20
  214. data/lib/active_record/tasks/database_tasks.rb +276 -85
  215. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
  216. data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
  217. data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -16
  218. data/lib/active_record/test_databases.rb +23 -0
  219. data/lib/active_record/test_fixtures.rb +224 -0
  220. data/lib/active_record/timestamp.rb +70 -35
  221. data/lib/active_record/touch_later.rb +7 -4
  222. data/lib/active_record/transactions.rb +133 -149
  223. data/lib/active_record/translation.rb +3 -1
  224. data/lib/active_record/type/adapter_specific_registry.rb +44 -45
  225. data/lib/active_record/type/date.rb +2 -0
  226. data/lib/active_record/type/date_time.rb +2 -0
  227. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  228. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  229. data/lib/active_record/type/internal/timezone.rb +2 -0
  230. data/lib/active_record/type/json.rb +30 -0
  231. data/lib/active_record/type/serialized.rb +16 -8
  232. data/lib/active_record/type/text.rb +11 -0
  233. data/lib/active_record/type/time.rb +2 -1
  234. data/lib/active_record/type/type_map.rb +13 -15
  235. data/lib/active_record/type/unsigned_integer.rb +17 -0
  236. data/lib/active_record/type.rb +23 -17
  237. data/lib/active_record/type_caster/connection.rb +17 -12
  238. data/lib/active_record/type_caster/map.rb +5 -4
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/validations/absence.rb +2 -0
  241. data/lib/active_record/validations/associated.rb +3 -1
  242. data/lib/active_record/validations/length.rb +2 -0
  243. data/lib/active_record/validations/presence.rb +4 -2
  244. data/lib/active_record/validations/uniqueness.rb +29 -42
  245. data/lib/active_record/validations.rb +7 -4
  246. data/lib/active_record/version.rb +3 -1
  247. data/lib/active_record.rb +36 -22
  248. data/lib/arel/alias_predication.rb +9 -0
  249. data/lib/arel/attributes/attribute.rb +37 -0
  250. data/lib/arel/attributes.rb +22 -0
  251. data/lib/arel/collectors/bind.rb +24 -0
  252. data/lib/arel/collectors/composite.rb +31 -0
  253. data/lib/arel/collectors/plain_string.rb +20 -0
  254. data/lib/arel/collectors/sql_string.rb +20 -0
  255. data/lib/arel/collectors/substitute_binds.rb +28 -0
  256. data/lib/arel/crud.rb +42 -0
  257. data/lib/arel/delete_manager.rb +18 -0
  258. data/lib/arel/errors.rb +9 -0
  259. data/lib/arel/expressions.rb +29 -0
  260. data/lib/arel/factory_methods.rb +49 -0
  261. data/lib/arel/insert_manager.rb +49 -0
  262. data/lib/arel/math.rb +45 -0
  263. data/lib/arel/nodes/and.rb +32 -0
  264. data/lib/arel/nodes/ascending.rb +23 -0
  265. data/lib/arel/nodes/binary.rb +52 -0
  266. data/lib/arel/nodes/bind_param.rb +36 -0
  267. data/lib/arel/nodes/case.rb +55 -0
  268. data/lib/arel/nodes/casted.rb +50 -0
  269. data/lib/arel/nodes/comment.rb +29 -0
  270. data/lib/arel/nodes/count.rb +12 -0
  271. data/lib/arel/nodes/delete_statement.rb +45 -0
  272. data/lib/arel/nodes/descending.rb +23 -0
  273. data/lib/arel/nodes/equality.rb +18 -0
  274. data/lib/arel/nodes/extract.rb +24 -0
  275. data/lib/arel/nodes/false.rb +16 -0
  276. data/lib/arel/nodes/full_outer_join.rb +8 -0
  277. data/lib/arel/nodes/function.rb +44 -0
  278. data/lib/arel/nodes/grouping.rb +8 -0
  279. data/lib/arel/nodes/in.rb +8 -0
  280. data/lib/arel/nodes/infix_operation.rb +80 -0
  281. data/lib/arel/nodes/inner_join.rb +8 -0
  282. data/lib/arel/nodes/insert_statement.rb +37 -0
  283. data/lib/arel/nodes/join_source.rb +20 -0
  284. data/lib/arel/nodes/matches.rb +18 -0
  285. data/lib/arel/nodes/named_function.rb +23 -0
  286. data/lib/arel/nodes/node.rb +50 -0
  287. data/lib/arel/nodes/node_expression.rb +13 -0
  288. data/lib/arel/nodes/outer_join.rb +8 -0
  289. data/lib/arel/nodes/over.rb +15 -0
  290. data/lib/arel/nodes/regexp.rb +16 -0
  291. data/lib/arel/nodes/right_outer_join.rb +8 -0
  292. data/lib/arel/nodes/select_core.rb +67 -0
  293. data/lib/arel/nodes/select_statement.rb +41 -0
  294. data/lib/arel/nodes/sql_literal.rb +16 -0
  295. data/lib/arel/nodes/string_join.rb +11 -0
  296. data/lib/arel/nodes/table_alias.rb +27 -0
  297. data/lib/arel/nodes/terminal.rb +16 -0
  298. data/lib/arel/nodes/true.rb +16 -0
  299. data/lib/arel/nodes/unary.rb +45 -0
  300. data/lib/arel/nodes/unary_operation.rb +20 -0
  301. data/lib/arel/nodes/unqualified_column.rb +22 -0
  302. data/lib/arel/nodes/update_statement.rb +41 -0
  303. data/lib/arel/nodes/values_list.rb +9 -0
  304. data/lib/arel/nodes/window.rb +126 -0
  305. data/lib/arel/nodes/with.rb +11 -0
  306. data/lib/arel/nodes.rb +68 -0
  307. data/lib/arel/order_predications.rb +13 -0
  308. data/lib/arel/predications.rb +257 -0
  309. data/lib/arel/select_manager.rb +271 -0
  310. data/lib/arel/table.rb +110 -0
  311. data/lib/arel/tree_manager.rb +72 -0
  312. data/lib/arel/update_manager.rb +34 -0
  313. data/lib/arel/visitors/depth_first.rb +204 -0
  314. data/lib/arel/visitors/dot.rb +297 -0
  315. data/lib/arel/visitors/ibm_db.rb +34 -0
  316. data/lib/arel/visitors/informix.rb +62 -0
  317. data/lib/arel/visitors/mssql.rb +157 -0
  318. data/lib/arel/visitors/mysql.rb +83 -0
  319. data/lib/arel/visitors/oracle.rb +159 -0
  320. data/lib/arel/visitors/oracle12.rb +66 -0
  321. data/lib/arel/visitors/postgresql.rb +110 -0
  322. data/lib/arel/visitors/sqlite.rb +39 -0
  323. data/lib/arel/visitors/to_sql.rb +889 -0
  324. data/lib/arel/visitors/visitor.rb +46 -0
  325. data/lib/arel/visitors/where_sql.rb +23 -0
  326. data/lib/arel/visitors.rb +20 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +58 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  332. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  334. data/lib/rails/generators/active_record/migration.rb +17 -2
  335. data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
  336. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  337. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +133 -50
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  350. data/lib/active_record/attribute_set/builder.rb +0 -130
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/collection_cache_key.rb +0 -50
  353. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  354. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  355. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  356. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  357. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  358. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/relation/from_clause"
2
4
  require "active_record/relation/query_attribute"
3
5
  require "active_record/relation/where_clause"
4
6
  require "active_record/relation/where_clause_factory"
5
- require 'active_model/forbidden_attributes_protection'
6
- require 'active_support/core_ext/string/filters'
7
+ require "active_model/forbidden_attributes_protection"
7
8
 
8
9
  module ActiveRecord
9
10
  module QueryMethods
@@ -40,94 +41,56 @@ module ActiveRecord
40
41
  #
41
42
  # User.where.not(name: %w(Ko1 Nobu))
42
43
  # # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
43
- #
44
- # User.where.not(name: "Jon", role: "admin")
45
- # # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
46
44
  def not(opts, *rest)
47
45
  opts = sanitize_forbidden_attributes(opts)
48
46
 
49
47
  where_clause = @scope.send(:where_clause_factory).build(opts, rest)
50
48
 
51
49
  @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
52
- @scope.where_clause += where_clause.invert
50
+
51
+ if not_behaves_as_nor?(opts)
52
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
53
+ NOT conditions will no longer behave as NOR in Rails 6.1.
54
+ To continue using NOR conditions, NOT each conditions manually
55
+ (`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
56
+ MSG
57
+ @scope.where_clause += where_clause.invert(:nor)
58
+ else
59
+ @scope.where_clause += where_clause.invert
60
+ end
61
+
53
62
  @scope
54
63
  end
64
+
65
+ private
66
+ def not_behaves_as_nor?(opts)
67
+ opts.is_a?(Hash) && opts.size > 1
68
+ end
55
69
  end
56
70
 
57
71
  FROZEN_EMPTY_ARRAY = [].freeze
58
- Relation::MULTI_VALUE_METHODS.each do |name|
59
- class_eval <<-CODE, __FILE__, __LINE__ + 1
60
- def #{name}_values
61
- @values[:#{name}] || FROZEN_EMPTY_ARRAY
62
- end
72
+ FROZEN_EMPTY_HASH = {}.freeze
63
73
 
64
- def #{name}_values=(values)
65
- assert_mutability!
66
- @values[:#{name}] = values
74
+ Relation::VALUE_METHODS.each do |name|
75
+ method_name = \
76
+ case name
77
+ when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
78
+ when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
79
+ when *Relation::CLAUSE_METHODS then "#{name}_clause"
67
80
  end
68
- CODE
69
- end
70
-
71
- (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
72
81
  class_eval <<-CODE, __FILE__, __LINE__ + 1
73
- def #{name}_value # def readonly_value
74
- @values[:#{name}] # @values[:readonly]
82
+ def #{method_name} # def includes_values
83
+ default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
84
+ @values.fetch(:#{name}, default) # @values.fetch(:includes, default)
75
85
  end # end
76
- CODE
77
- end
78
86
 
79
- Relation::SINGLE_VALUE_METHODS.each do |name|
80
- class_eval <<-CODE, __FILE__, __LINE__ + 1
81
- def #{name}_value=(value) # def readonly_value=(value)
87
+ def #{method_name}=(value) # def includes_values=(value)
82
88
  assert_mutability! # assert_mutability!
83
- @values[:#{name}] = value # @values[:readonly] = value
89
+ @values[:#{name}] = value # @values[:includes] = value
84
90
  end # end
85
91
  CODE
86
92
  end
87
93
 
88
- Relation::CLAUSE_METHODS.each do |name|
89
- class_eval <<-CODE, __FILE__, __LINE__ + 1
90
- def #{name}_clause # def where_clause
91
- @values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
92
- end # end
93
- #
94
- def #{name}_clause=(value) # def where_clause=(value)
95
- assert_mutability! # assert_mutability!
96
- @values[:#{name}] = value # @values[:where] = value
97
- end # end
98
- CODE
99
- end
100
-
101
- def bound_attributes
102
- if limit_value && !string_containing_comma?(limit_value)
103
- limit_bind = Attribute.with_cast_value(
104
- "LIMIT".freeze,
105
- connection.sanitize_limit(limit_value),
106
- Type::Value.new,
107
- )
108
- end
109
- if offset_value
110
- offset_bind = Attribute.with_cast_value(
111
- "OFFSET".freeze,
112
- offset_value.to_i,
113
- Type::Value.new,
114
- )
115
- end
116
- connection.combine_bind_parameters(
117
- from_clause: from_clause.binds,
118
- join_clause: arel.bind_values,
119
- where_clause: where_clause.binds,
120
- having_clause: having_clause.binds,
121
- limit: limit_bind,
122
- offset: offset_bind,
123
- )
124
- end
125
-
126
- FROZEN_EMPTY_HASH = {}.freeze
127
- def create_with_value # :nodoc:
128
- @values[:create_with] || FROZEN_EMPTY_HASH
129
- end
130
-
131
94
  alias extensions extending_values
132
95
 
133
96
  # Specify relationships to be included in the result set. For
@@ -152,7 +115,7 @@ module ActiveRecord
152
115
  #
153
116
  # === conditions
154
117
  #
155
- # If you want to add conditions to your included models you'll have
118
+ # If you want to add string conditions to your included models, you'll have
156
119
  # to explicitly reference them. For example:
157
120
  #
158
121
  # User.includes(:posts).where('posts.name = ?', 'example')
@@ -163,6 +126,12 @@ module ActiveRecord
163
126
  #
164
127
  # Note that #includes works with association names while #references needs
165
128
  # the actual table name.
129
+ #
130
+ # If you pass the conditions via hash, you don't need to call #references
131
+ # explicitly, as #where references the tables for you. For example, this
132
+ # will work correctly:
133
+ #
134
+ # User.includes(:posts).where(posts: { name: 'example' })
166
135
  def includes(*args)
167
136
  check_if_method_has_arguments!(:includes, args)
168
137
  spawn.includes!(*args)
@@ -206,6 +175,19 @@ module ActiveRecord
206
175
  self
207
176
  end
208
177
 
178
+ # Extracts a named +association+ from the relation. The named association is first preloaded,
179
+ # then the individual association records are collected from the relation. Like so:
180
+ #
181
+ # account.memberships.extract_associated(:user)
182
+ # # => Returns collection of User records
183
+ #
184
+ # This is short-hand for:
185
+ #
186
+ # account.memberships.preload(:user).collect(&:user)
187
+ def extract_associated(association)
188
+ preload(association).collect(&association)
189
+ end
190
+
209
191
  # Use to indicate that the given +table_names+ are referenced by an SQL string,
210
192
  # and should therefore be JOINed in any query rather than loaded separately.
211
193
  # This method only works in conjunction with #includes.
@@ -231,12 +213,13 @@ module ActiveRecord
231
213
 
232
214
  # Works in two unique ways.
233
215
  #
234
- # First: takes a block so it can be used just like +Array#select+.
216
+ # First: takes a block so it can be used just like <tt>Array#select</tt>.
235
217
  #
236
218
  # Model.all.select { |m| m.field == value }
237
219
  #
238
220
  # This will build an array of objects from the database for the scope,
239
- # converting them into an array and iterating through them using +Array#select+.
221
+ # converting them into an array and iterating through them using
222
+ # <tt>Array#select</tt>.
240
223
  #
241
224
  # Second: Modifies the SELECT statement for the query so that only certain
242
225
  # fields are retrieved:
@@ -269,20 +252,46 @@ module ActiveRecord
269
252
  # Model.select(:field).first.other_field
270
253
  # # => ActiveModel::MissingAttributeError: missing attribute: other_field
271
254
  def select(*fields)
272
- return super if block_given?
273
- raise ArgumentError, 'Call this with at least one field' if fields.empty?
255
+ if block_given?
256
+ if fields.any?
257
+ raise ArgumentError, "`select' with block doesn't take arguments."
258
+ end
259
+
260
+ return super()
261
+ end
262
+
263
+ raise ArgumentError, "Call `select' with at least one field" if fields.empty?
274
264
  spawn._select!(*fields)
275
265
  end
276
266
 
277
267
  def _select!(*fields) # :nodoc:
268
+ fields.reject!(&:blank?)
278
269
  fields.flatten!
279
- fields.map! do |field|
280
- klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
281
- end
282
270
  self.select_values += fields
283
271
  self
284
272
  end
285
273
 
274
+ # Allows you to change a previously set select statement.
275
+ #
276
+ # Post.select(:title, :body)
277
+ # # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
278
+ #
279
+ # Post.select(:title, :body).reselect(:created_at)
280
+ # # SELECT `posts`.`created_at` FROM `posts`
281
+ #
282
+ # This is short-hand for <tt>unscope(:select).select(fields)</tt>.
283
+ # Note that we're unscoping the entire select statement.
284
+ def reselect(*args)
285
+ check_if_method_has_arguments!(:reselect, args)
286
+ spawn.reselect!(*args)
287
+ end
288
+
289
+ # Same as #reselect but operates on relation in-place instead of copying.
290
+ def reselect!(*args) # :nodoc:
291
+ self.select_values = args
292
+ self
293
+ end
294
+
286
295
  # Allows to specify a group attribute:
287
296
  #
288
297
  # User.group(:name)
@@ -339,6 +348,7 @@ module ActiveRecord
339
348
  spawn.order!(*args)
340
349
  end
341
350
 
351
+ # Same as #order but operates on relation in-place instead of copying.
342
352
  def order!(*args) # :nodoc:
343
353
  preprocess_order_args(args)
344
354
 
@@ -360,8 +370,9 @@ module ActiveRecord
360
370
  spawn.reorder!(*args)
361
371
  end
362
372
 
373
+ # Same as #reorder but operates on relation in-place instead of copying.
363
374
  def reorder!(*args) # :nodoc:
364
- preprocess_order_args(args)
375
+ preprocess_order_args(args) unless args.all?(&:blank?)
365
376
 
366
377
  self.reordering_value = true
367
378
  self.order_values = args
@@ -369,8 +380,8 @@ module ActiveRecord
369
380
  end
370
381
 
371
382
  VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
372
- :limit, :offset, :joins, :includes, :from,
373
- :readonly, :having])
383
+ :limit, :offset, :joins, :left_outer_joins, :annotate,
384
+ :includes, :from, :readonly, :having, :optimizer_hints])
374
385
 
375
386
  # Removes an unwanted relation that is already defined on a chain of relations.
376
387
  # This is useful when passing around chains of relations and would like to
@@ -417,7 +428,12 @@ module ActiveRecord
417
428
  args.each do |scope|
418
429
  case scope
419
430
  when Symbol
420
- symbol_unscoping(scope)
431
+ scope = :left_outer_joins if scope == :left_joins
432
+ if !VALID_UNSCOPING_VALUES.include?(scope)
433
+ raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
434
+ end
435
+ assert_mutability!
436
+ @values[scope] = DEFAULT_VALUES[scope]
421
437
  when Hash
422
438
  scope.each do |key, target_value|
423
439
  if key != :where
@@ -482,20 +498,17 @@ module ActiveRecord
482
498
  # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
483
499
  #
484
500
  def left_outer_joins(*args)
485
- check_if_method_has_arguments!(:left_outer_joins, args)
486
-
487
- args.compact!
488
- args.flatten!
489
-
501
+ check_if_method_has_arguments!(__callee__, args)
490
502
  spawn.left_outer_joins!(*args)
491
503
  end
492
504
  alias :left_joins :left_outer_joins
493
505
 
494
506
  def left_outer_joins!(*args) # :nodoc:
507
+ args.compact!
508
+ args.flatten!
495
509
  self.left_outer_joins_values += args
496
510
  self
497
511
  end
498
- alias :left_joins! :left_outer_joins!
499
512
 
500
513
  # Returns a new relation, which is the result of filtering the current relation
501
514
  # according to the conditions in the arguments.
@@ -658,7 +671,7 @@ module ActiveRecord
658
671
  # present). Neither relation may have a #limit, #offset, or #distinct set.
659
672
  #
660
673
  # Post.where("id = 1").or(Post.where("author_id = 3"))
661
- # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3'))
674
+ # # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
662
675
  #
663
676
  def or(other)
664
677
  unless other.is_a? Relation
@@ -676,7 +689,8 @@ module ActiveRecord
676
689
  end
677
690
 
678
691
  self.where_clause = self.where_clause.or(other.where_clause)
679
- self.having_clause = self.having_clause.or(other.having_clause)
692
+ self.having_clause = having_clause.or(other.having_clause)
693
+ self.references_values += other.references_values
680
694
 
681
695
  self
682
696
  end
@@ -707,13 +721,6 @@ module ActiveRecord
707
721
  end
708
722
 
709
723
  def limit!(value) # :nodoc:
710
- if string_containing_comma?(value)
711
- # Remove `string_containing_comma?` when removing this deprecation
712
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
713
- Passing a string to limit in the form "1,2" is deprecated and will be
714
- removed in Rails 5.1. Please call `offset` explicitly instead.
715
- WARNING
716
- end
717
724
  self.limit_value = value
718
725
  self
719
726
  end
@@ -780,7 +787,7 @@ module ActiveRecord
780
787
  # end
781
788
  #
782
789
  def none
783
- where("1=0").extending!(NullRelation)
790
+ spawn.none!
784
791
  end
785
792
 
786
793
  def none! # :nodoc:
@@ -824,7 +831,7 @@ module ActiveRecord
824
831
  value = sanitize_forbidden_attributes(value)
825
832
  self.create_with_value = create_with_value.merge(value)
826
833
  else
827
- self.create_with_value = {}
834
+ self.create_with_value = FROZEN_EMPTY_HASH
828
835
  end
829
836
 
830
837
  self
@@ -865,16 +872,12 @@ module ActiveRecord
865
872
  def distinct(value = true)
866
873
  spawn.distinct!(value)
867
874
  end
868
- alias uniq distinct
869
- deprecate uniq: :distinct
870
875
 
871
876
  # Like #distinct, but modifies relation in place.
872
877
  def distinct!(value = true) # :nodoc:
873
878
  self.distinct_value = value
874
879
  self
875
880
  end
876
- alias uniq! distinct!
877
- deprecate uniq!: :distinct!
878
881
 
879
882
  # Used to extend a scope with additional methods, either through
880
883
  # a module or through a block provided.
@@ -930,6 +933,29 @@ module ActiveRecord
930
933
  self
931
934
  end
932
935
 
936
+ # Specify optimizer hints to be used in the SELECT statement.
937
+ #
938
+ # Example (for MySQL):
939
+ #
940
+ # Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
941
+ # # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
942
+ #
943
+ # Example (for PostgreSQL with pg_hint_plan):
944
+ #
945
+ # Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
946
+ # # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
947
+ def optimizer_hints(*args)
948
+ check_if_method_has_arguments!(:optimizer_hints, args)
949
+ spawn.optimizer_hints!(*args)
950
+ end
951
+
952
+ def optimizer_hints!(*args) # :nodoc:
953
+ args.flatten!
954
+
955
+ self.optimizer_hints_values |= args
956
+ self
957
+ end
958
+
933
959
  # Reverse the existing order clause on the relation.
934
960
  #
935
961
  # User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
@@ -944,299 +970,402 @@ module ActiveRecord
944
970
  self
945
971
  end
946
972
 
947
- # Returns the Arel object associated with the relation.
948
- def arel # :nodoc:
949
- @arel ||= build_arel
973
+ def skip_query_cache!(value = true) # :nodoc:
974
+ self.skip_query_cache_value = value
975
+ self
950
976
  end
951
977
 
952
- private
978
+ def skip_preloading! # :nodoc:
979
+ self.skip_preloading_value = true
980
+ self
981
+ end
953
982
 
954
- def assert_mutability!
955
- raise ImmutableRelation if @loaded
956
- raise ImmutableRelation if defined?(@arel) && @arel
983
+ # Adds an SQL comment to queries generated from this relation. For example:
984
+ #
985
+ # User.annotate("selecting user names").select(:name)
986
+ # # SELECT "users"."name" FROM "users" /* selecting user names */
987
+ #
988
+ # User.annotate("selecting", "user", "names").select(:name)
989
+ # # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
990
+ #
991
+ # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
992
+ def annotate(*args)
993
+ check_if_method_has_arguments!(:annotate, args)
994
+ spawn.annotate!(*args)
957
995
  end
958
996
 
959
- def build_arel
960
- arel = Arel::SelectManager.new(table)
997
+ # Like #annotate, but modifies relation in place.
998
+ def annotate!(*args) # :nodoc:
999
+ self.annotate_values += args
1000
+ self
1001
+ end
961
1002
 
962
- build_joins(arel, joins_values.flatten) unless joins_values.empty?
963
- build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
1003
+ # Returns the Arel object associated with the relation.
1004
+ def arel(aliases = nil) # :nodoc:
1005
+ @arel ||= build_arel(aliases)
1006
+ end
964
1007
 
965
- arel.where(where_clause.ast) unless where_clause.empty?
966
- arel.having(having_clause.ast) unless having_clause.empty?
967
- if limit_value
968
- if string_containing_comma?(limit_value)
969
- arel.take(connection.sanitize_limit(limit_value))
970
- else
971
- arel.take(Arel::Nodes::BindParam.new)
972
- end
973
- end
974
- arel.skip(Arel::Nodes::BindParam.new) if offset_value
975
- arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
1008
+ def construct_join_dependency(associations, join_type) # :nodoc:
1009
+ ActiveRecord::Associations::JoinDependency.new(
1010
+ klass, table, associations, join_type
1011
+ )
1012
+ end
976
1013
 
977
- build_order(arel)
1014
+ protected
1015
+ def build_subquery(subquery_alias, select_value) # :nodoc:
1016
+ subquery = except(:optimizer_hints).arel.as(subquery_alias)
978
1017
 
979
- build_select(arel)
1018
+ Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
1019
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1020
+ end
1021
+ end
980
1022
 
981
- arel.distinct(distinct_value)
982
- arel.from(build_from) unless from_clause.empty?
983
- arel.lock(lock_value) if lock_value
1023
+ private
1024
+ def assert_mutability!
1025
+ raise ImmutableRelation if @loaded
1026
+ raise ImmutableRelation if defined?(@arel) && @arel
1027
+ end
984
1028
 
985
- arel
986
- end
1029
+ def build_arel(aliases)
1030
+ arel = Arel::SelectManager.new(table)
987
1031
 
988
- def symbol_unscoping(scope)
989
- if !VALID_UNSCOPING_VALUES.include?(scope)
990
- raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
991
- end
1032
+ if !joins_values.empty?
1033
+ build_joins(arel, joins_values.flatten, aliases)
1034
+ elsif !left_outer_joins_values.empty?
1035
+ build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
1036
+ end
992
1037
 
993
- clause_method = Relation::CLAUSE_METHODS.include?(scope)
994
- multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope)
995
- if clause_method
996
- unscope_code = "#{scope}_clause="
997
- else
998
- unscope_code = "#{scope}_value#{'s' if multi_val_method}="
999
- end
1038
+ arel.where(where_clause.ast) unless where_clause.empty?
1039
+ arel.having(having_clause.ast) unless having_clause.empty?
1040
+ if limit_value
1041
+ limit_attribute = ActiveModel::Attribute.with_cast_value(
1042
+ "LIMIT",
1043
+ connection.sanitize_limit(limit_value),
1044
+ Type.default_value,
1045
+ )
1046
+ arel.take(Arel::Nodes::BindParam.new(limit_attribute))
1047
+ end
1048
+ if offset_value
1049
+ offset_attribute = ActiveModel::Attribute.with_cast_value(
1050
+ "OFFSET",
1051
+ offset_value.to_i,
1052
+ Type.default_value,
1053
+ )
1054
+ arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
1055
+ end
1056
+ arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
1000
1057
 
1001
- case scope
1002
- when :order
1003
- result = []
1004
- else
1005
- result = [] if multi_val_method
1006
- end
1058
+ build_order(arel)
1007
1059
 
1008
- self.send(unscope_code, result)
1009
- end
1060
+ build_select(arel)
1010
1061
 
1011
- def association_for_table(table_name)
1012
- table_name = table_name.to_s
1013
- @klass._reflect_on_association(table_name) ||
1014
- @klass._reflect_on_association(table_name.singularize)
1015
- end
1062
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1063
+ arel.distinct(distinct_value)
1064
+ arel.from(build_from) unless from_clause.empty?
1065
+ arel.lock(lock_value) if lock_value
1066
+ arel.comment(*annotate_values) unless annotate_values.empty?
1016
1067
 
1017
- def build_from
1018
- opts = from_clause.value
1019
- name = from_clause.name
1020
- case opts
1021
- when Relation
1022
- name ||= 'subquery'
1023
- opts.arel.as(name.to_s)
1024
- else
1025
- opts
1068
+ arel
1026
1069
  end
1027
- end
1028
1070
 
1029
- def build_left_outer_joins(manager, outer_joins)
1030
- buckets = outer_joins.group_by do |join|
1031
- case join
1032
- when Hash, Symbol, Array
1033
- :association_join
1071
+ def build_from
1072
+ opts = from_clause.value
1073
+ name = from_clause.name
1074
+ case opts
1075
+ when Relation
1076
+ if opts.eager_loading?
1077
+ opts = opts.send(:apply_join_dependency)
1078
+ end
1079
+ name ||= "subquery"
1080
+ opts.arel.as(name.to_s)
1034
1081
  else
1035
- raise ArgumentError, 'only Hash, Symbol and Array are allowed'
1082
+ opts
1036
1083
  end
1037
1084
  end
1038
1085
 
1039
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
1040
- end
1086
+ def select_association_list(associations)
1087
+ result = []
1088
+ associations.each do |association|
1089
+ case association
1090
+ when Hash, Symbol, Array
1091
+ result << association
1092
+ else
1093
+ yield if block_given?
1094
+ end
1095
+ end
1096
+ result
1097
+ end
1041
1098
 
1042
- def build_joins(manager, joins)
1043
- buckets = joins.group_by do |join|
1044
- case join
1045
- when String
1046
- :string_join
1047
- when Hash, Symbol, Array
1048
- :association_join
1049
- when ActiveRecord::Associations::JoinDependency
1050
- :stashed_join
1051
- when Arel::Nodes::Join
1052
- :join_node
1053
- else
1054
- raise 'unknown class: %s' % join.class.name
1099
+ def valid_association_list(associations)
1100
+ select_association_list(associations) do
1101
+ raise ArgumentError, "only Hash, Symbol and Array are allowed"
1055
1102
  end
1056
1103
  end
1057
1104
 
1058
- build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
1059
- end
1105
+ def build_left_outer_joins(manager, outer_joins, aliases)
1106
+ buckets = Hash.new { |h, k| h[k] = [] }
1107
+ buckets[:association_join] = valid_association_list(outer_joins)
1108
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
1109
+ end
1060
1110
 
1061
- def build_join_query(manager, buckets, join_type)
1062
- buckets.default = []
1111
+ def build_joins(manager, joins, aliases)
1112
+ buckets = Hash.new { |h, k| h[k] = [] }
1063
1113
 
1064
- association_joins = buckets[:association_join]
1065
- stashed_association_joins = buckets[:stashed_join]
1066
- join_nodes = buckets[:join_node].uniq
1067
- string_joins = buckets[:string_join].map(&:strip).uniq
1114
+ unless left_outer_joins_values.empty?
1115
+ left_joins = valid_association_list(left_outer_joins_values.flatten)
1116
+ buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1117
+ end
1068
1118
 
1069
- join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
1119
+ if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
1120
+ buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
1121
+ end
1070
1122
 
1071
- join_dependency = ActiveRecord::Associations::JoinDependency.new(
1072
- @klass,
1073
- association_joins,
1074
- join_list
1075
- )
1123
+ joins.map! do |join|
1124
+ if join.is_a?(String)
1125
+ table.create_string_join(Arel.sql(join.strip)) unless join.blank?
1126
+ else
1127
+ join
1128
+ end
1129
+ end.delete_if(&:blank?).uniq!
1130
+
1131
+ while joins.first.is_a?(Arel::Nodes::Join)
1132
+ join_node = joins.shift
1133
+ if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
1134
+ buckets[:join_node] << join_node
1135
+ else
1136
+ buckets[:leading_join] << join_node
1137
+ end
1138
+ end
1076
1139
 
1077
- join_infos = join_dependency.join_constraints stashed_association_joins, join_type
1140
+ joins.each do |join|
1141
+ case join
1142
+ when Hash, Symbol, Array
1143
+ buckets[:association_join] << join
1144
+ when ActiveRecord::Associations::JoinDependency
1145
+ buckets[:stashed_join] << join
1146
+ when Arel::Nodes::Join
1147
+ buckets[:join_node] << join
1148
+ else
1149
+ raise "unknown class: %s" % join.class.name
1150
+ end
1151
+ end
1078
1152
 
1079
- join_infos.each do |info|
1080
- info.joins.each { |join| manager.from(join) }
1081
- manager.bind_values.concat info.binds
1153
+ build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
1082
1154
  end
1083
1155
 
1084
- manager.join_sources.concat(join_list)
1156
+ def build_join_query(manager, buckets, join_type, aliases)
1157
+ association_joins = buckets[:association_join]
1158
+ stashed_joins = buckets[:stashed_join]
1159
+ leading_joins = buckets[:leading_join]
1160
+ join_nodes = buckets[:join_node]
1085
1161
 
1086
- manager
1087
- end
1162
+ join_sources = manager.join_sources
1163
+ join_sources.concat(leading_joins) unless leading_joins.empty?
1088
1164
 
1089
- def convert_join_strings_to_ast(table, joins)
1090
- joins
1091
- .flatten
1092
- .reject(&:blank?)
1093
- .map { |join| table.create_string_join(Arel.sql(join)) }
1094
- end
1165
+ unless association_joins.empty? && stashed_joins.empty?
1166
+ alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
1167
+ join_dependency = construct_join_dependency(association_joins, join_type)
1168
+ join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
1169
+ end
1095
1170
 
1096
- def build_select(arel)
1097
- if select_values.any?
1098
- arel.project(*arel_columns(select_values.uniq))
1099
- else
1100
- arel.project(@klass.arel_table[Arel.star])
1171
+ join_sources.concat(join_nodes) unless join_nodes.empty?
1101
1172
  end
1102
- end
1103
1173
 
1104
- def arel_columns(columns)
1105
- columns.map do |field|
1106
- if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
1107
- arel_attribute(field)
1108
- elsif Symbol === field
1109
- connection.quote_table_name(field.to_s)
1174
+ def build_select(arel)
1175
+ if select_values.any?
1176
+ arel.project(*arel_columns(select_values.uniq))
1177
+ elsif klass.ignored_columns.any?
1178
+ arel.project(*klass.column_names.map { |field| arel_attribute(field) })
1110
1179
  else
1111
- field
1180
+ arel.project(table[Arel.star])
1112
1181
  end
1113
1182
  end
1114
- end
1115
1183
 
1116
- def reverse_sql_order(order_query)
1117
- if order_query.empty?
1118
- return [arel_attribute(primary_key).desc] if primary_key
1119
- raise IrreversibleOrderError,
1120
- "Relation has no current order and table has no primary key to be used as default order"
1184
+ def arel_columns(columns)
1185
+ columns.flat_map do |field|
1186
+ case field
1187
+ when Symbol
1188
+ arel_column(field.to_s) do |attr_name|
1189
+ connection.quote_table_name(attr_name)
1190
+ end
1191
+ when String
1192
+ arel_column(field, &:itself)
1193
+ when Proc
1194
+ field.call
1195
+ else
1196
+ field
1197
+ end
1198
+ end
1121
1199
  end
1122
1200
 
1123
- order_query.flat_map do |o|
1124
- case o
1125
- when Arel::Attribute
1126
- o.desc
1127
- when Arel::Nodes::Ordering
1128
- o.reverse
1129
- when String
1130
- if does_not_support_reverse?(o)
1131
- raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
1132
- end
1133
- o.split(',').map! do |s|
1134
- s.strip!
1135
- s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
1136
- end
1201
+ def arel_column(field)
1202
+ field = klass.attribute_aliases[field] || field
1203
+ from = from_clause.name || from_clause.value
1204
+
1205
+ if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1206
+ arel_attribute(field)
1137
1207
  else
1138
- o
1208
+ yield field
1139
1209
  end
1140
1210
  end
1141
- end
1142
1211
 
1143
- def does_not_support_reverse?(order)
1144
- # Uses SQL function with multiple arguments.
1145
- (order.include?(',') && order.split(',').find { |section| section.count('(') != section.count(')')}) ||
1146
- # Uses "nulls first" like construction.
1147
- order =~ /nulls (first|last)\Z/i
1148
- end
1212
+ def table_name_matches?(from)
1213
+ /(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
1214
+ end
1149
1215
 
1150
- def build_order(arel)
1151
- orders = order_values.uniq
1152
- orders.reject!(&:blank?)
1216
+ def reverse_sql_order(order_query)
1217
+ if order_query.empty?
1218
+ return [arel_attribute(primary_key).desc] if primary_key
1219
+ raise IrreversibleOrderError,
1220
+ "Relation has no current order and table has no primary key to be used as default order"
1221
+ end
1153
1222
 
1154
- arel.order(*orders) unless orders.empty?
1155
- end
1223
+ order_query.flat_map do |o|
1224
+ case o
1225
+ when Arel::Attribute
1226
+ o.desc
1227
+ when Arel::Nodes::Ordering
1228
+ o.reverse
1229
+ when String
1230
+ if does_not_support_reverse?(o)
1231
+ raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
1232
+ end
1233
+ o.split(",").map! do |s|
1234
+ s.strip!
1235
+ s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
1236
+ end
1237
+ else
1238
+ o
1239
+ end
1240
+ end
1241
+ end
1156
1242
 
1157
- VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1158
- 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1243
+ def does_not_support_reverse?(order)
1244
+ # Account for String subclasses like Arel::Nodes::SqlLiteral that
1245
+ # override methods like #count.
1246
+ order = String.new(order) unless order.instance_of?(String)
1159
1247
 
1160
- def validate_order_args(args)
1161
- args.each do |arg|
1162
- next unless arg.is_a?(Hash)
1163
- arg.each do |_key, value|
1164
- raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
1165
- "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1166
- end
1248
+ # Uses SQL function with multiple arguments.
1249
+ (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
1250
+ # Uses "nulls first" like construction.
1251
+ /\bnulls\s+(?:first|last)\b/i.match?(order)
1167
1252
  end
1168
- end
1169
1253
 
1170
- def preprocess_order_args(order_args)
1171
- order_args.map! do |arg|
1172
- klass.send(:sanitize_sql_for_order, arg)
1254
+ def build_order(arel)
1255
+ orders = order_values.uniq
1256
+ orders.reject!(&:blank?)
1257
+
1258
+ arel.order(*orders) unless orders.empty?
1173
1259
  end
1174
- order_args.flatten!
1175
- validate_order_args(order_args)
1176
1260
 
1177
- references = order_args.grep(String)
1178
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
1179
- references!(references) if references.any?
1261
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1262
+ "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
1180
1263
 
1181
- # if a symbol is given we prepend the quoted table name
1182
- order_args.map! do |arg|
1183
- case arg
1184
- when Symbol
1185
- arel_attribute(arg).asc
1186
- when Hash
1187
- arg.map { |field, dir|
1188
- arel_attribute(field).send(dir.downcase)
1189
- }
1190
- else
1191
- arg
1264
+ def validate_order_args(args)
1265
+ args.each do |arg|
1266
+ next unless arg.is_a?(Hash)
1267
+ arg.each do |_key, value|
1268
+ unless VALID_DIRECTIONS.include?(value)
1269
+ raise ArgumentError,
1270
+ "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
1271
+ end
1272
+ end
1192
1273
  end
1193
- end.flatten!
1194
- end
1274
+ end
1195
1275
 
1196
- # Checks to make sure that the arguments are not blank. Note that if some
1197
- # blank-like object were initially passed into the query method, then this
1198
- # method will not raise an error.
1199
- #
1200
- # Example:
1201
- #
1202
- # Post.references() # raises an error
1203
- # Post.references([]) # does not raise an error
1204
- #
1205
- # This particular method should be called with a method_name and the args
1206
- # passed into that method as an input. For example:
1207
- #
1208
- # def references(*args)
1209
- # check_if_method_has_arguments!("references", args)
1210
- # ...
1211
- # end
1212
- def check_if_method_has_arguments!(method_name, args)
1213
- if args.blank?
1214
- raise ArgumentError, "The method .#{method_name}() must contain arguments."
1276
+ def preprocess_order_args(order_args)
1277
+ order_args.reject!(&:blank?)
1278
+ order_args.map! do |arg|
1279
+ klass.sanitize_sql_for_order(arg)
1280
+ end
1281
+ order_args.flatten!
1282
+
1283
+ @klass.disallow_raw_sql!(
1284
+ order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1285
+ permit: connection.column_name_with_order_matcher
1286
+ )
1287
+
1288
+ validate_order_args(order_args)
1289
+
1290
+ references = order_args.grep(String)
1291
+ references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1292
+ references!(references) if references.any?
1293
+
1294
+ # if a symbol is given we prepend the quoted table name
1295
+ order_args.map! do |arg|
1296
+ case arg
1297
+ when Symbol
1298
+ order_column(arg.to_s).asc
1299
+ when Hash
1300
+ arg.map { |field, dir|
1301
+ case field
1302
+ when Arel::Nodes::SqlLiteral
1303
+ field.send(dir.downcase)
1304
+ else
1305
+ order_column(field.to_s).send(dir.downcase)
1306
+ end
1307
+ }
1308
+ else
1309
+ arg
1310
+ end
1311
+ end.flatten!
1215
1312
  end
1216
- end
1217
1313
 
1218
- def structurally_incompatible_values_for_or(other)
1219
- Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
1220
- (Relation::MULTI_VALUE_METHODS - [:extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
1221
- (Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
1222
- end
1314
+ def order_column(field)
1315
+ arel_column(field) do |attr_name|
1316
+ if attr_name == "count" && !group_values.empty?
1317
+ arel_attribute(attr_name)
1318
+ else
1319
+ Arel.sql(connection.quote_table_name(attr_name))
1320
+ end
1321
+ end
1322
+ end
1223
1323
 
1224
- def new_where_clause
1225
- Relation::WhereClause.empty
1226
- end
1227
- alias new_having_clause new_where_clause
1324
+ # Checks to make sure that the arguments are not blank. Note that if some
1325
+ # blank-like object were initially passed into the query method, then this
1326
+ # method will not raise an error.
1327
+ #
1328
+ # Example:
1329
+ #
1330
+ # Post.references() # raises an error
1331
+ # Post.references([]) # does not raise an error
1332
+ #
1333
+ # This particular method should be called with a method_name and the args
1334
+ # passed into that method as an input. For example:
1335
+ #
1336
+ # def references(*args)
1337
+ # check_if_method_has_arguments!("references", args)
1338
+ # ...
1339
+ # end
1340
+ def check_if_method_has_arguments!(method_name, args)
1341
+ if args.blank?
1342
+ raise ArgumentError, "The method .#{method_name}() must contain arguments."
1343
+ end
1344
+ end
1228
1345
 
1229
- def where_clause_factory
1230
- @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
1231
- end
1232
- alias having_clause_factory where_clause_factory
1346
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
1347
+ def structurally_incompatible_values_for_or(other)
1348
+ values = other.values
1349
+ STRUCTURAL_OR_METHODS.reject do |method|
1350
+ default = DEFAULT_VALUES[method]
1351
+ @values.fetch(method, default) == values.fetch(method, default)
1352
+ end
1353
+ end
1233
1354
 
1234
- def new_from_clause
1235
- Relation::FromClause.empty
1236
- end
1355
+ def where_clause_factory
1356
+ @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
1357
+ end
1358
+ alias having_clause_factory where_clause_factory
1237
1359
 
1238
- def string_containing_comma?(value)
1239
- ::String === value && value.include?(",")
1240
- end
1360
+ DEFAULT_VALUES = {
1361
+ create_with: FROZEN_EMPTY_HASH,
1362
+ where: Relation::WhereClause.empty,
1363
+ having: Relation::WhereClause.empty,
1364
+ from: Relation::FromClause.empty
1365
+ }
1366
+
1367
+ Relation::MULTI_VALUE_METHODS.each do |value|
1368
+ DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
1369
+ end
1241
1370
  end
1242
1371
  end