activerecord 5.0.7.2 → 6.0.3.4

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 +708 -2040
  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.rb +37 -22
  8. data/lib/active_record/advisory_lock_base.rb +18 -0
  9. data/lib/active_record/aggregations.rb +249 -247
  10. data/lib/active_record/association_relation.rb +18 -14
  11. data/lib/active_record/associations.rb +1603 -1592
  12. data/lib/active_record/associations/alias_tracker.rb +24 -34
  13. data/lib/active_record/associations/association.rb +114 -55
  14. data/lib/active_record/associations/association_scope.rb +94 -94
  15. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  16. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  17. data/lib/active_record/associations/builder/association.rb +18 -25
  18. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  19. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  20. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
  21. data/lib/active_record/associations/builder/has_many.rb +4 -0
  22. data/lib/active_record/associations/builder/has_one.rb +37 -1
  23. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  24. data/lib/active_record/associations/collection_association.rb +86 -254
  25. data/lib/active_record/associations/collection_proxy.rb +158 -122
  26. data/lib/active_record/associations/foreign_association.rb +9 -0
  27. data/lib/active_record/associations/has_many_association.rb +23 -30
  28. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  29. data/lib/active_record/associations/has_one_association.rb +59 -54
  30. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  31. data/lib/active_record/associations/join_dependency.rb +143 -176
  32. data/lib/active_record/associations/join_dependency/join_association.rb +38 -87
  33. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  34. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  35. data/lib/active_record/associations/preloader.rb +90 -103
  36. data/lib/active_record/associations/preloader/association.rb +86 -100
  37. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  38. data/lib/active_record/associations/singular_association.rb +12 -45
  39. data/lib/active_record/associations/through_association.rb +26 -14
  40. data/lib/active_record/attribute_assignment.rb +54 -61
  41. data/lib/active_record/attribute_decorators.rb +38 -17
  42. data/lib/active_record/attribute_methods.rb +66 -106
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
  44. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  45. data/lib/active_record/attribute_methods/primary_key.rb +85 -92
  46. data/lib/active_record/attribute_methods/query.rb +4 -3
  47. data/lib/active_record/attribute_methods/read.rb +20 -49
  48. data/lib/active_record/attribute_methods/serialization.rb +29 -7
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
  50. data/lib/active_record/attribute_methods/write.rb +34 -33
  51. data/lib/active_record/attributes.rb +38 -25
  52. data/lib/active_record/autosave_association.rb +54 -35
  53. data/lib/active_record/base.rb +27 -24
  54. data/lib/active_record/callbacks.rb +64 -35
  55. data/lib/active_record/coders/json.rb +2 -0
  56. data/lib/active_record/coders/yaml_column.rb +11 -12
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +552 -323
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +228 -147
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -202
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +396 -562
  70. data/lib/active_record/connection_adapters/column.rb +41 -13
  71. data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  73. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  101. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -31
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
  118. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  120. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  121. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  122. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  123. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  124. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  127. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +259 -266
  128. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  129. data/lib/active_record/connection_handling.rb +143 -40
  130. data/lib/active_record/core.rb +201 -163
  131. data/lib/active_record/counter_cache.rb +60 -28
  132. data/lib/active_record/database_configurations.rb +233 -0
  133. data/lib/active_record/database_configurations/database_config.rb +37 -0
  134. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  135. data/lib/active_record/database_configurations/url_config.rb +78 -0
  136. data/lib/active_record/define_callbacks.rb +22 -0
  137. data/lib/active_record/dynamic_matchers.rb +87 -87
  138. data/lib/active_record/enum.rb +60 -23
  139. data/lib/active_record/errors.rb +114 -18
  140. data/lib/active_record/explain.rb +4 -4
  141. data/lib/active_record/explain_registry.rb +3 -1
  142. data/lib/active_record/explain_subscriber.rb +9 -4
  143. data/lib/active_record/fixture_set/file.rb +13 -8
  144. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  145. data/lib/active_record/fixture_set/render_context.rb +17 -0
  146. data/lib/active_record/fixture_set/table_row.rb +152 -0
  147. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  148. data/lib/active_record/fixtures.rb +194 -504
  149. data/lib/active_record/gem_version.rb +5 -3
  150. data/lib/active_record/inheritance.rb +150 -99
  151. data/lib/active_record/insert_all.rb +179 -0
  152. data/lib/active_record/integration.rb +116 -25
  153. data/lib/active_record/internal_metadata.rb +16 -19
  154. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  155. data/lib/active_record/locking/optimistic.rb +77 -87
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +48 -29
  158. data/lib/active_record/middleware/database_selector.rb +74 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +369 -302
  162. data/lib/active_record/migration/command_recorder.rb +134 -100
  163. data/lib/active_record/migration/compatibility.rb +174 -56
  164. data/lib/active_record/migration/join_table.rb +8 -7
  165. data/lib/active_record/model_schema.rb +131 -127
  166. data/lib/active_record/nested_attributes.rb +213 -202
  167. data/lib/active_record/no_touching.rb +12 -3
  168. data/lib/active_record/null_relation.rb +12 -34
  169. data/lib/active_record/persistence.rb +446 -77
  170. data/lib/active_record/query_cache.rb +13 -12
  171. data/lib/active_record/querying.rb +37 -24
  172. data/lib/active_record/railtie.rb +128 -36
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +312 -177
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +214 -252
  179. data/lib/active_record/relation.rb +440 -318
  180. data/lib/active_record/relation/batches.rb +98 -52
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  182. data/lib/active_record/relation/calculations.rb +212 -173
  183. data/lib/active_record/relation/delegation.rb +72 -69
  184. data/lib/active_record/relation/finder_methods.rb +207 -247
  185. data/lib/active_record/relation/from_clause.rb +6 -8
  186. data/lib/active_record/relation/merger.rb +78 -62
  187. data/lib/active_record/relation/predicate_builder.rb +83 -105
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  195. data/lib/active_record/relation/query_attribute.rb +33 -2
  196. data/lib/active_record/relation/query_methods.rb +476 -334
  197. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  198. data/lib/active_record/relation/spawn_methods.rb +8 -8
  199. data/lib/active_record/relation/where_clause.rb +111 -96
  200. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  201. data/lib/active_record/result.rb +69 -40
  202. data/lib/active_record/runtime_registry.rb +5 -3
  203. data/lib/active_record/sanitization.rb +83 -99
  204. data/lib/active_record/schema.rb +7 -14
  205. data/lib/active_record/schema_dumper.rb +71 -69
  206. data/lib/active_record/schema_migration.rb +16 -6
  207. data/lib/active_record/scoping.rb +20 -20
  208. data/lib/active_record/scoping/default.rb +92 -95
  209. data/lib/active_record/scoping/named.rb +47 -27
  210. data/lib/active_record/secure_token.rb +4 -2
  211. data/lib/active_record/serialization.rb +2 -0
  212. data/lib/active_record/statement_cache.rb +63 -28
  213. data/lib/active_record/store.rb +121 -41
  214. data/lib/active_record/suppressor.rb +6 -3
  215. data/lib/active_record/table_metadata.rb +39 -18
  216. data/lib/active_record/tasks/database_tasks.rb +271 -81
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +70 -36
  223. data/lib/active_record/touch_later.rb +8 -6
  224. data/lib/active_record/transactions.rb +141 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +23 -18
  227. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  228. data/lib/active_record/type/date.rb +2 -0
  229. data/lib/active_record/type/date_time.rb +2 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  232. data/lib/active_record/type/internal/timezone.rb +2 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +16 -9
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +2 -1
  237. data/lib/active_record/type/type_map.rb +14 -17
  238. data/lib/active_record/type/unsigned_integer.rb +16 -0
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/type_caster/connection.rb +17 -12
  241. data/lib/active_record/type_caster/map.rb +5 -4
  242. data/lib/active_record/validations.rb +7 -5
  243. data/lib/active_record/validations/absence.rb +2 -0
  244. data/lib/active_record/validations/associated.rb +4 -3
  245. data/lib/active_record/validations/length.rb +2 -0
  246. data/lib/active_record/validations/presence.rb +4 -2
  247. data/lib/active_record/validations/uniqueness.rb +29 -42
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +62 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +203 -0
  317. data/lib/arel/visitors/dot.rb +296 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +156 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +158 -0
  323. data/lib/arel/visitors/oracle12.rb +65 -0
  324. data/lib/arel/visitors/postgresql.rb +109 -0
  325. data/lib/arel/visitors/sqlite.rb +38 -0
  326. data/lib/arel/visitors/to_sql.rb +888 -0
  327. data/lib/arel/visitors/visitor.rb +45 -0
  328. data/lib/arel/visitors/where_sql.rb +22 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  333. data/lib/rails/generators/active_record/migration.rb +17 -3
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  335. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
  338. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +137 -52
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  355. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  356. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  357. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  358. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  359. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/extract"
4
+
1
5
  module ActiveRecord
2
6
  class PredicateBuilder
3
7
  class ArrayHandler # :nodoc:
@@ -6,18 +10,21 @@ module ActiveRecord
6
10
  end
7
11
 
8
12
  def call(attribute, value)
9
- values = value.map { |x| x.is_a?(Base) ? x.id : x }
10
- nils, values = values.partition(&:nil?)
11
-
12
- return attribute.in([]) if values.empty? && nils.empty?
13
+ return attribute.in([]) if value.empty?
13
14
 
14
- ranges, values = values.partition { |v| v.is_a?(Range) }
15
+ values = value.map { |x| x.is_a?(Base) ? x.id : x }
16
+ nils = values.extract!(&:nil?)
17
+ ranges = values.extract! { |v| v.is_a?(Range) }
15
18
 
16
19
  values_predicate =
17
20
  case values.length
18
21
  when 0 then NullPredicate
19
22
  when 1 then predicate_builder.build(attribute, values.first)
20
- else attribute.in(values)
23
+ else
24
+ values.map! do |v|
25
+ predicate_builder.build_bind_attribute(attribute.name, v)
26
+ end
27
+ values.empty? ? NullPredicate : attribute.in(values)
21
28
  end
22
29
 
23
30
  unless nils.empty?
@@ -26,18 +33,17 @@ module ActiveRecord
26
33
 
27
34
  array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
28
35
  array_predicates.unshift(values_predicate)
29
- array_predicates.inject { |composite, predicate| composite.or(predicate) }
36
+ array_predicates.inject(&:or)
30
37
  end
31
38
 
32
- protected
33
-
34
- attr_reader :predicate_builder
39
+ private
40
+ attr_reader :predicate_builder
35
41
 
36
- module NullPredicate # :nodoc:
37
- def self.or(other)
38
- other
42
+ module NullPredicate # :nodoc:
43
+ def self.or(other)
44
+ other
45
+ end
39
46
  end
40
- end
41
47
  end
42
48
  end
43
49
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class PredicateBuilder
5
+ class AssociationQueryValue # :nodoc:
6
+ def initialize(associated_table, value)
7
+ @associated_table = associated_table
8
+ @value = value
9
+ end
10
+
11
+ def queries
12
+ [associated_table.association_join_foreign_key.to_s => ids]
13
+ end
14
+
15
+ private
16
+ attr_reader :associated_table, :value
17
+
18
+ def ids
19
+ case value
20
+ when Relation
21
+ value.select_values.empty? ? value.select(primary_key) : value
22
+ when Array
23
+ value.map { |v| convert_to_id(v) }
24
+ else
25
+ convert_to_id(value)
26
+ end
27
+ end
28
+
29
+ def primary_key
30
+ associated_table.association_join_primary_key
31
+ end
32
+
33
+ def convert_to_id(value)
34
+ case value
35
+ when Base
36
+ value._read_attribute(primary_key)
37
+ else
38
+ value
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class BaseHandler # :nodoc:
@@ -9,9 +11,8 @@ module ActiveRecord
9
11
  predicate_builder.build(attribute, value.id)
10
12
  end
11
13
 
12
- protected
13
-
14
- attr_reader :predicate_builder
14
+ private
15
+ attr_reader :predicate_builder
15
16
  end
16
17
  end
17
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class BasicObjectHandler # :nodoc:
@@ -6,12 +8,12 @@ module ActiveRecord
6
8
  end
7
9
 
8
10
  def call(attribute, value)
9
- attribute.eq(value)
11
+ bind = predicate_builder.build_bind_attribute(attribute.name, value)
12
+ attribute.eq(bind)
10
13
  end
11
14
 
12
- protected
13
-
14
- attr_reader :predicate_builder
15
+ private
16
+ attr_reader :predicate_builder
15
17
  end
16
18
  end
17
19
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class PredicateBuilder
5
+ class PolymorphicArrayValue # :nodoc:
6
+ def initialize(associated_table, values)
7
+ @associated_table = associated_table
8
+ @values = values
9
+ end
10
+
11
+ def queries
12
+ type_to_ids_mapping.map do |type, ids|
13
+ {
14
+ associated_table.association_foreign_type.to_s => type,
15
+ associated_table.association_foreign_key.to_s => ids
16
+ }
17
+ end
18
+ end
19
+
20
+ private
21
+ attr_reader :associated_table, :values
22
+
23
+ def type_to_ids_mapping
24
+ default_hash = Hash.new { |hsh, key| hsh[key] = [] }
25
+ values.each_with_object(default_hash) do |value, hash|
26
+ hash[klass(value).polymorphic_name] << convert_to_id(value)
27
+ end
28
+ end
29
+
30
+ def primary_key(value)
31
+ associated_table.association_join_primary_key(klass(value))
32
+ end
33
+
34
+ def klass(value)
35
+ case value
36
+ when Base
37
+ value.class
38
+ when Relation
39
+ value.klass
40
+ end
41
+ end
42
+
43
+ def convert_to_id(value)
44
+ case value
45
+ when Base
46
+ value._read_attribute(primary_key(value))
47
+ when Relation
48
+ value.select(primary_key(value))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class RangeHandler # :nodoc:
@@ -8,26 +10,13 @@ module ActiveRecord
8
10
  end
9
11
 
10
12
  def call(attribute, value)
11
- if value.begin.respond_to?(:infinite?) && value.begin.infinite?
12
- if value.end.respond_to?(:infinite?) && value.end.infinite?
13
- attribute.not_in([])
14
- elsif value.exclude_end?
15
- attribute.lt(value.end)
16
- else
17
- attribute.lteq(value.end)
18
- end
19
- elsif value.end.respond_to?(:infinite?) && value.end.infinite?
20
- attribute.gteq(value.begin)
21
- elsif value.exclude_end?
22
- attribute.gteq(value.begin).and(attribute.lt(value.end))
23
- else
24
- attribute.between(value)
25
- end
13
+ begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
14
+ end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
15
+ attribute.between(RangeWithBinds.new(begin_bind, end_bind, value.exclude_end?))
26
16
  end
27
17
 
28
- protected
29
-
30
- attr_reader :predicate_builder
18
+ private
19
+ attr_reader :predicate_builder
31
20
  end
32
21
  end
33
22
  end
@@ -1,7 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class RelationHandler # :nodoc:
4
6
  def call(attribute, value)
7
+ if value.eager_loading?
8
+ value = value.send(:apply_join_dependency)
9
+ end
10
+
5
11
  if value.select_values.empty?
6
12
  value = value.select(value.arel_attribute(value.klass.primary_key))
7
13
  end
@@ -1,8 +1,10 @@
1
- require 'active_record/attribute'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/attribute"
2
4
 
3
5
  module ActiveRecord
4
6
  class Relation
5
- class QueryAttribute < Attribute # :nodoc:
7
+ class QueryAttribute < ActiveModel::Attribute # :nodoc:
6
8
  def type_cast(value)
7
9
  value
8
10
  end
@@ -14,6 +16,35 @@ module ActiveRecord
14
16
  def with_cast_value(value)
15
17
  QueryAttribute.new(name, value, type)
16
18
  end
19
+
20
+ def nil?
21
+ unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
+ value_before_type_cast.nil? ||
23
+ type.respond_to?(:subtype, true) && value_for_database.nil?
24
+ end
25
+ rescue ::RangeError
26
+ end
27
+
28
+ def infinite?
29
+ infinity?(value_before_type_cast) || infinity?(value_for_database)
30
+ rescue ::RangeError
31
+ end
32
+
33
+ def unboundable?
34
+ if defined?(@_unboundable)
35
+ @_unboundable
36
+ else
37
+ value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
38
+ @_unboundable = nil
39
+ end
40
+ rescue ::RangeError
41
+ @_unboundable = type.cast(value_before_type_cast) <=> 0
42
+ end
43
+
44
+ private
45
+ def infinity?(value)
46
+ value.respond_to?(:infinite?) && value.infinite?
47
+ end
17
48
  end
18
49
  end
19
50
  end
@@ -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)
@@ -311,7 +331,7 @@ module ActiveRecord
311
331
  def group!(*args) # :nodoc:
312
332
  args.flatten!
313
333
 
314
- self.group_values += args
334
+ self.group_values |= args
315
335
  self
316
336
  end
317
337
 
@@ -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,404 @@ 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
+ def annotate(*args)
1004
+ check_if_method_has_arguments!(:annotate, args)
1005
+ spawn.annotate!(*args)
1006
+ end
953
1007
 
954
- def assert_mutability!
955
- raise ImmutableRelation if @loaded
956
- raise ImmutableRelation if defined?(@arel) && @arel
1008
+ # Like #annotate, but modifies relation in place.
1009
+ def annotate!(*args) # :nodoc:
1010
+ self.annotate_values += args
1011
+ self
957
1012
  end
958
1013
 
959
- def build_arel
960
- arel = Arel::SelectManager.new(table)
1014
+ # Returns the Arel object associated with the relation.
1015
+ def arel(aliases = nil) # :nodoc:
1016
+ @arel ||= build_arel(aliases)
1017
+ end
961
1018
 
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?
1019
+ def construct_join_dependency(associations, join_type) # :nodoc:
1020
+ ActiveRecord::Associations::JoinDependency.new(
1021
+ klass, table, associations, join_type
1022
+ )
1023
+ end
964
1024
 
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)
1025
+ protected
1026
+ def build_subquery(subquery_alias, select_value) # :nodoc:
1027
+ subquery = except(:optimizer_hints).arel.as(subquery_alias)
1028
+
1029
+ Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
1030
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
972
1031
  end
973
1032
  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
-
977
- build_order(arel)
978
-
979
- build_select(arel)
980
1033
 
981
- arel.distinct(distinct_value)
982
- arel.from(build_from) unless from_clause.empty?
983
- arel.lock(lock_value) if lock_value
1034
+ private
1035
+ def assert_mutability!
1036
+ raise ImmutableRelation if @loaded
1037
+ raise ImmutableRelation if defined?(@arel) && @arel
1038
+ end
984
1039
 
985
- arel
986
- end
1040
+ def build_arel(aliases)
1041
+ arel = Arel::SelectManager.new(table)
987
1042
 
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
1043
+ if !joins_values.empty?
1044
+ build_joins(arel, joins_values.flatten, aliases)
1045
+ elsif !left_outer_joins_values.empty?
1046
+ build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
1047
+ end
992
1048
 
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
1049
+ arel.where(where_clause.ast) unless where_clause.empty?
1050
+ arel.having(having_clause.ast) unless having_clause.empty?
1051
+ if limit_value
1052
+ limit_attribute = ActiveModel::Attribute.with_cast_value(
1053
+ "LIMIT",
1054
+ connection.sanitize_limit(limit_value),
1055
+ Type.default_value,
1056
+ )
1057
+ arel.take(Arel::Nodes::BindParam.new(limit_attribute))
1058
+ end
1059
+ if offset_value
1060
+ offset_attribute = ActiveModel::Attribute.with_cast_value(
1061
+ "OFFSET",
1062
+ offset_value.to_i,
1063
+ Type.default_value,
1064
+ )
1065
+ arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
1066
+ end
1067
+ arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
1000
1068
 
1001
- case scope
1002
- when :order
1003
- result = []
1004
- else
1005
- result = [] if multi_val_method
1006
- end
1069
+ build_order(arel)
1007
1070
 
1008
- self.send(unscope_code, result)
1009
- end
1071
+ build_select(arel)
1010
1072
 
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
1073
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1074
+ arel.distinct(distinct_value)
1075
+ arel.from(build_from) unless from_clause.empty?
1076
+ arel.lock(lock_value) if lock_value
1077
+ arel.comment(*annotate_values) unless annotate_values.empty?
1016
1078
 
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
1079
+ arel
1026
1080
  end
1027
- end
1028
1081
 
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
1082
+ def build_from
1083
+ opts = from_clause.value
1084
+ name = from_clause.name
1085
+ case opts
1086
+ when Relation
1087
+ if opts.eager_loading?
1088
+ opts = opts.send(:apply_join_dependency)
1089
+ end
1090
+ name ||= "subquery"
1091
+ opts.arel.as(name.to_s)
1034
1092
  else
1035
- raise ArgumentError, 'only Hash, Symbol and Array are allowed'
1093
+ opts
1036
1094
  end
1037
1095
  end
1038
1096
 
1039
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
1040
- end
1097
+ def select_association_list(associations)
1098
+ result = []
1099
+ associations.each do |association|
1100
+ case association
1101
+ when Hash, Symbol, Array
1102
+ result << association
1103
+ else
1104
+ yield if block_given?
1105
+ end
1106
+ end
1107
+ result
1108
+ end
1041
1109
 
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
1110
+ def valid_association_list(associations)
1111
+ select_association_list(associations) do
1112
+ raise ArgumentError, "only Hash, Symbol and Array are allowed"
1055
1113
  end
1056
1114
  end
1057
1115
 
1058
- build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
1059
- end
1116
+ def build_left_outer_joins(manager, outer_joins, aliases)
1117
+ buckets = Hash.new { |h, k| h[k] = [] }
1118
+ buckets[:association_join] = valid_association_list(outer_joins)
1119
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
1120
+ end
1060
1121
 
1061
- def build_join_query(manager, buckets, join_type)
1062
- buckets.default = []
1122
+ def build_joins(manager, joins, aliases)
1123
+ buckets = Hash.new { |h, k| h[k] = [] }
1063
1124
 
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
1125
+ unless left_outer_joins_values.empty?
1126
+ left_joins = valid_association_list(left_outer_joins_values.flatten)
1127
+ buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1128
+ end
1068
1129
 
1069
- join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
1130
+ if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
1131
+ buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
1132
+ end
1070
1133
 
1071
- join_dependency = ActiveRecord::Associations::JoinDependency.new(
1072
- @klass,
1073
- association_joins,
1074
- join_list
1075
- )
1134
+ joins.map! do |join|
1135
+ if join.is_a?(String)
1136
+ table.create_string_join(Arel.sql(join.strip)) unless join.blank?
1137
+ else
1138
+ join
1139
+ end
1140
+ end.delete_if(&:blank?).uniq!
1141
+
1142
+ while joins.first.is_a?(Arel::Nodes::Join)
1143
+ join_node = joins.shift
1144
+ if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
1145
+ buckets[:join_node] << join_node
1146
+ else
1147
+ buckets[:leading_join] << join_node
1148
+ end
1149
+ end
1076
1150
 
1077
- join_infos = join_dependency.join_constraints stashed_association_joins, join_type
1151
+ joins.each do |join|
1152
+ case join
1153
+ when Hash, Symbol, Array
1154
+ buckets[:association_join] << join
1155
+ when ActiveRecord::Associations::JoinDependency
1156
+ buckets[:stashed_join] << join
1157
+ when Arel::Nodes::Join
1158
+ buckets[:join_node] << join
1159
+ else
1160
+ raise "unknown class: %s" % join.class.name
1161
+ end
1162
+ end
1078
1163
 
1079
- join_infos.each do |info|
1080
- info.joins.each { |join| manager.from(join) }
1081
- manager.bind_values.concat info.binds
1164
+ build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
1082
1165
  end
1083
1166
 
1084
- manager.join_sources.concat(join_list)
1167
+ def build_join_query(manager, buckets, join_type, aliases)
1168
+ association_joins = buckets[:association_join]
1169
+ stashed_joins = buckets[:stashed_join]
1170
+ leading_joins = buckets[:leading_join]
1171
+ join_nodes = buckets[:join_node]
1085
1172
 
1086
- manager
1087
- end
1173
+ join_sources = manager.join_sources
1174
+ join_sources.concat(leading_joins) unless leading_joins.empty?
1088
1175
 
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
1176
+ unless association_joins.empty? && stashed_joins.empty?
1177
+ alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
1178
+ join_dependency = construct_join_dependency(association_joins, join_type)
1179
+ join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
1180
+ end
1095
1181
 
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])
1182
+ join_sources.concat(join_nodes) unless join_nodes.empty?
1101
1183
  end
1102
- end
1103
1184
 
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)
1185
+ def build_select(arel)
1186
+ if select_values.any?
1187
+ arel.project(*arel_columns(select_values.uniq))
1188
+ elsif klass.ignored_columns.any?
1189
+ arel.project(*klass.column_names.map { |field| arel_attribute(field) })
1110
1190
  else
1111
- field
1191
+ arel.project(table[Arel.star])
1112
1192
  end
1113
1193
  end
1114
- end
1115
1194
 
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"
1195
+ def arel_columns(columns)
1196
+ columns.flat_map do |field|
1197
+ case field
1198
+ when Symbol
1199
+ arel_column(field.to_s) do |attr_name|
1200
+ connection.quote_table_name(attr_name)
1201
+ end
1202
+ when String
1203
+ arel_column(field, &:itself)
1204
+ when Proc
1205
+ field.call
1206
+ else
1207
+ field
1208
+ end
1209
+ end
1121
1210
  end
1122
1211
 
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
1212
+ def arel_column(field)
1213
+ field = klass.attribute_aliases[field] || field
1214
+ from = from_clause.name || from_clause.value
1215
+
1216
+ if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1217
+ arel_attribute(field)
1137
1218
  else
1138
- o
1219
+ yield field
1139
1220
  end
1140
1221
  end
1141
- end
1142
1222
 
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
1223
+ def table_name_matches?(from)
1224
+ table_name = Regexp.escape(table.name)
1225
+ quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
1226
+ /(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
1227
+ end
1149
1228
 
1150
- def build_order(arel)
1151
- orders = order_values.uniq
1152
- orders.reject!(&:blank?)
1229
+ def reverse_sql_order(order_query)
1230
+ if order_query.empty?
1231
+ return [arel_attribute(primary_key).desc] if primary_key
1232
+ raise IrreversibleOrderError,
1233
+ "Relation has no current order and table has no primary key to be used as default order"
1234
+ end
1153
1235
 
1154
- arel.order(*orders) unless orders.empty?
1155
- end
1236
+ order_query.flat_map do |o|
1237
+ case o
1238
+ when Arel::Attribute
1239
+ o.desc
1240
+ when Arel::Nodes::Ordering
1241
+ o.reverse
1242
+ when String
1243
+ if does_not_support_reverse?(o)
1244
+ raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
1245
+ end
1246
+ o.split(",").map! do |s|
1247
+ s.strip!
1248
+ s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
1249
+ end
1250
+ else
1251
+ o
1252
+ end
1253
+ end
1254
+ end
1156
1255
 
1157
- VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1158
- 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1256
+ def does_not_support_reverse?(order)
1257
+ # Account for String subclasses like Arel::Nodes::SqlLiteral that
1258
+ # override methods like #count.
1259
+ order = String.new(order) unless order.instance_of?(String)
1159
1260
 
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
1261
+ # Uses SQL function with multiple arguments.
1262
+ (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
1263
+ # Uses "nulls first" like construction.
1264
+ /\bnulls\s+(?:first|last)\b/i.match?(order)
1167
1265
  end
1168
- end
1169
1266
 
1170
- def preprocess_order_args(order_args)
1171
- order_args.map! do |arg|
1172
- klass.send(:sanitize_sql_for_order, arg)
1267
+ def build_order(arel)
1268
+ orders = order_values.uniq
1269
+ orders.reject!(&:blank?)
1270
+
1271
+ arel.order(*orders) unless orders.empty?
1173
1272
  end
1174
- order_args.flatten!
1175
- validate_order_args(order_args)
1176
1273
 
1177
- references = order_args.grep(String)
1178
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
1179
- references!(references) if references.any?
1274
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1275
+ "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
1180
1276
 
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
1277
+ def validate_order_args(args)
1278
+ args.each do |arg|
1279
+ next unless arg.is_a?(Hash)
1280
+ arg.each do |_key, value|
1281
+ unless VALID_DIRECTIONS.include?(value)
1282
+ raise ArgumentError,
1283
+ "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
1284
+ end
1285
+ end
1192
1286
  end
1193
- end.flatten!
1194
- end
1287
+ end
1195
1288
 
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."
1289
+ def preprocess_order_args(order_args)
1290
+ order_args.reject!(&:blank?)
1291
+ order_args.map! do |arg|
1292
+ klass.sanitize_sql_for_order(arg)
1293
+ end
1294
+ order_args.flatten!
1295
+
1296
+ @klass.disallow_raw_sql!(
1297
+ order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1298
+ permit: connection.column_name_with_order_matcher
1299
+ )
1300
+
1301
+ validate_order_args(order_args)
1302
+
1303
+ references = order_args.grep(String)
1304
+ references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1305
+ references!(references) if references.any?
1306
+
1307
+ # if a symbol is given we prepend the quoted table name
1308
+ order_args.map! do |arg|
1309
+ case arg
1310
+ when Symbol
1311
+ order_column(arg.to_s).asc
1312
+ when Hash
1313
+ arg.map { |field, dir|
1314
+ case field
1315
+ when Arel::Nodes::SqlLiteral
1316
+ field.send(dir.downcase)
1317
+ else
1318
+ order_column(field.to_s).send(dir.downcase)
1319
+ end
1320
+ }
1321
+ else
1322
+ arg
1323
+ end
1324
+ end.flatten!
1215
1325
  end
1216
- end
1217
1326
 
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
1327
+ def order_column(field)
1328
+ arel_column(field) do |attr_name|
1329
+ if attr_name == "count" && !group_values.empty?
1330
+ arel_attribute(attr_name)
1331
+ else
1332
+ Arel.sql(connection.quote_table_name(attr_name))
1333
+ end
1334
+ end
1335
+ end
1223
1336
 
1224
- def new_where_clause
1225
- Relation::WhereClause.empty
1226
- end
1227
- alias new_having_clause new_where_clause
1337
+ # Checks to make sure that the arguments are not blank. Note that if some
1338
+ # blank-like object were initially passed into the query method, then this
1339
+ # method will not raise an error.
1340
+ #
1341
+ # Example:
1342
+ #
1343
+ # Post.references() # raises an error
1344
+ # Post.references([]) # does not raise an error
1345
+ #
1346
+ # This particular method should be called with a method_name and the args
1347
+ # passed into that method as an input. For example:
1348
+ #
1349
+ # def references(*args)
1350
+ # check_if_method_has_arguments!("references", args)
1351
+ # ...
1352
+ # end
1353
+ def check_if_method_has_arguments!(method_name, args)
1354
+ if args.blank?
1355
+ raise ArgumentError, "The method .#{method_name}() must contain arguments."
1356
+ end
1357
+ end
1228
1358
 
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
1359
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
1360
+ def structurally_incompatible_values_for_or(other)
1361
+ values = other.values
1362
+ STRUCTURAL_OR_METHODS.reject do |method|
1363
+ default = DEFAULT_VALUES[method]
1364
+ @values.fetch(method, default) == values.fetch(method, default)
1365
+ end
1366
+ end
1233
1367
 
1234
- def new_from_clause
1235
- Relation::FromClause.empty
1236
- end
1368
+ def where_clause_factory
1369
+ @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
1370
+ end
1371
+ alias having_clause_factory where_clause_factory
1237
1372
 
1238
- def string_containing_comma?(value)
1239
- ::String === value && value.include?(",")
1240
- end
1373
+ DEFAULT_VALUES = {
1374
+ create_with: FROZEN_EMPTY_HASH,
1375
+ where: Relation::WhereClause.empty,
1376
+ having: Relation::WhereClause.empty,
1377
+ from: Relation::FromClause.empty
1378
+ }
1379
+
1380
+ Relation::MULTI_VALUE_METHODS.each do |value|
1381
+ DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
1382
+ end
1241
1383
  end
1242
1384
  end