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