activerecord 6.0.0 → 6.1.0

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

Potentially problematic release.


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

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +872 -582
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +1 -2
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations.rb +116 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +32 -8
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +86 -37
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +4 -9
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -76
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  102. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  103. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  104. data/lib/active_record/connection_adapters/postgresql_adapter.rb +81 -57
  105. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  106. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  107. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  108. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  109. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  110. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  111. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  112. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  113. data/lib/active_record/connection_handling.rb +211 -81
  114. data/lib/active_record/core.rb +237 -69
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations.rb +124 -85
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  118. data/lib/active_record/database_configurations/database_config.rb +52 -9
  119. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  120. data/lib/active_record/database_configurations/url_config.rb +15 -41
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +40 -16
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +54 -11
  134. data/lib/active_record/gem_version.rb +1 -1
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +39 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +22 -17
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +27 -9
  143. data/lib/active_record/middleware/database_selector.rb +4 -2
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  146. data/lib/active_record/migration.rb +114 -84
  147. data/lib/active_record/migration/command_recorder.rb +53 -45
  148. data/lib/active_record/migration/compatibility.rb +70 -20
  149. data/lib/active_record/migration/join_table.rb +0 -1
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/databases.rake +267 -93
  159. data/lib/active_record/readonly_attributes.rb +4 -0
  160. data/lib/active_record/reflection.rb +77 -63
  161. data/lib/active_record/relation.rb +108 -67
  162. data/lib/active_record/relation/batches.rb +38 -32
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  164. data/lib/active_record/relation/calculations.rb +102 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder.rb +55 -35
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  173. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  174. data/lib/active_record/relation/query_methods.rb +340 -180
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +104 -58
  178. data/lib/active_record/result.rb +41 -34
  179. data/lib/active_record/runtime_registry.rb +2 -2
  180. data/lib/active_record/sanitization.rb +6 -17
  181. data/lib/active_record/schema_dumper.rb +34 -4
  182. data/lib/active_record/schema_migration.rb +2 -8
  183. data/lib/active_record/scoping.rb +0 -1
  184. data/lib/active_record/scoping/default.rb +0 -1
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/secure_token.rb +16 -8
  187. data/lib/active_record/serialization.rb +5 -3
  188. data/lib/active_record/signed_id.rb +116 -0
  189. data/lib/active_record/statement_cache.rb +20 -4
  190. data/lib/active_record/store.rb +3 -3
  191. data/lib/active_record/suppressor.rb +2 -2
  192. data/lib/active_record/table_metadata.rb +39 -36
  193. data/lib/active_record/tasks/database_tasks.rb +139 -113
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  196. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  197. data/lib/active_record/test_databases.rb +5 -4
  198. data/lib/active_record/test_fixtures.rb +38 -16
  199. data/lib/active_record/timestamp.rb +4 -7
  200. data/lib/active_record/touch_later.rb +20 -21
  201. data/lib/active_record/transactions.rb +22 -71
  202. data/lib/active_record/type.rb +8 -2
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type_caster/connection.rb +0 -1
  210. data/lib/active_record/type_caster/map.rb +8 -5
  211. data/lib/active_record/validations.rb +3 -3
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/arel.rb +15 -12
  216. data/lib/arel/attributes/attribute.rb +4 -0
  217. data/lib/arel/collectors/bind.rb +5 -0
  218. data/lib/arel/collectors/composite.rb +8 -0
  219. data/lib/arel/collectors/sql_string.rb +7 -0
  220. data/lib/arel/collectors/substitute_binds.rb +7 -0
  221. data/lib/arel/nodes.rb +3 -1
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/predications.rb +17 -24
  237. data/lib/arel/select_manager.rb +1 -2
  238. data/lib/arel/table.rb +13 -5
  239. data/lib/arel/visitors.rb +0 -7
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration.rb +6 -2
  248. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  249. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  250. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +27 -24
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -16,10 +16,10 @@ module ActiveRecord
16
16
  def exec_queries
17
17
  QueryRegistry.reset
18
18
 
19
- super.tap do
19
+ super.tap do |records|
20
20
  if logger && warn_on_records_fetched_greater_than
21
- if @records.length > warn_on_records_fetched_greater_than
22
- logger.warn "Query fetched #{@records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
21
+ if records.length > warn_on_records_fetched_greater_than
22
+ logger.warn "Query fetched #{records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
23
23
  end
24
24
  end
25
25
  end
@@ -28,21 +28,22 @@ module ActiveRecord
28
28
  # # => Post.where(published: true).joins(:comments)
29
29
  #
30
30
  # This is mainly intended for sharing common conditions between multiple associations.
31
- def merge(other)
31
+ def merge(other, *rest)
32
32
  if other.is_a?(Array)
33
33
  records & other
34
34
  elsif other
35
- spawn.merge!(other)
35
+ spawn.merge!(other, *rest)
36
36
  else
37
37
  raise ArgumentError, "invalid argument: #{other.inspect}."
38
38
  end
39
39
  end
40
40
 
41
- def merge!(other) # :nodoc:
41
+ def merge!(other, *rest) # :nodoc:
42
+ options = rest.extract_options!
42
43
  if other.is_a?(Hash)
43
- Relation::HashMerger.new(self, other).merge
44
+ Relation::HashMerger.new(self, other, options[:rewhere]).merge
44
45
  elsif other.is_a?(Relation)
45
- Relation::Merger.new(self, other).merge
46
+ Relation::Merger.new(self, other, options[:rewhere]).merge
46
47
  elsif other.respond_to?(:to_proc)
47
48
  instance_exec(&other)
48
49
  else
@@ -67,10 +68,9 @@ module ActiveRecord
67
68
  end
68
69
 
69
70
  private
70
-
71
71
  def relation_with(values)
72
- result = Relation.create(klass, values: values)
73
- result.extend(*extending_values) if extending_values.any?
72
+ result = spawn
73
+ result.instance_variable_set(:@values, values)
74
74
  result
75
75
  end
76
76
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/array/extract"
4
+
3
5
  module ActiveRecord
4
6
  class Relation
5
7
  class WhereClause # :nodoc:
@@ -10,21 +12,25 @@ module ActiveRecord
10
12
  end
11
13
 
12
14
  def +(other)
13
- WhereClause.new(
14
- predicates + other.predicates,
15
- )
15
+ WhereClause.new(predicates + other.predicates)
16
16
  end
17
17
 
18
18
  def -(other)
19
- WhereClause.new(
20
- predicates - other.predicates,
21
- )
19
+ WhereClause.new(predicates - other.predicates)
20
+ end
21
+
22
+ def |(other)
23
+ WhereClause.new(predicates | other.predicates)
22
24
  end
23
25
 
24
- def merge(other)
25
- WhereClause.new(
26
- predicates_unreferenced_by(other) + other.predicates,
27
- )
26
+ def merge(other, rewhere = nil)
27
+ predicates = if rewhere
28
+ except_predicates(other.extract_attributes)
29
+ else
30
+ predicates_unreferenced_by(other)
31
+ end
32
+
33
+ WhereClause.new(predicates | other.predicates)
28
34
  end
29
35
 
30
36
  def except(*columns)
@@ -39,30 +45,31 @@ module ActiveRecord
39
45
  if left.empty? || right.empty?
40
46
  common
41
47
  else
42
- or_clause = WhereClause.new(
43
- [left.ast.or(right.ast)],
44
- )
45
- common + or_clause
48
+ left = left.ast
49
+ left = left.expr if left.is_a?(Arel::Nodes::Grouping)
50
+
51
+ right = right.ast
52
+ right = right.expr if right.is_a?(Arel::Nodes::Grouping)
53
+
54
+ or_clause = Arel::Nodes::Or.new(left, right)
55
+
56
+ common.predicates << Arel::Nodes::Grouping.new(or_clause)
57
+ common
46
58
  end
47
59
  end
48
60
 
49
61
  def to_h(table_name = nil)
50
- equalities = equalities(predicates)
51
- if table_name
52
- equalities = equalities.select do |node|
53
- node.left.relation.name == table_name
54
- end
55
- end
56
-
57
- equalities.map { |node|
62
+ equalities(predicates).each_with_object({}) do |node, hash|
63
+ next if table_name&.!= node.left.relation.name
58
64
  name = node.left.name.to_s
59
65
  value = extract_node_value(node.right)
60
- [name, value]
61
- }.to_h
66
+ hash[name] = value
67
+ end
62
68
  end
63
69
 
64
70
  def ast
65
- Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
71
+ predicates = predicates_with_wrapped_sql_literals
72
+ predicates.one? ? predicates.first : Arel::Nodes::And.new(predicates)
66
73
  end
67
74
 
68
75
  def ==(other)
@@ -70,11 +77,9 @@ module ActiveRecord
70
77
  predicates == other.predicates
71
78
  end
72
79
 
73
- def invert(as = :nand)
80
+ def invert
74
81
  if predicates.size == 1
75
82
  inverted_predicates = [ invert_predicate(predicates.first) ]
76
- elsif as == :nor
77
- inverted_predicates = predicates.map { |node| invert_predicate(node) }
78
83
  else
79
84
  inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
80
85
  end
@@ -83,29 +88,59 @@ module ActiveRecord
83
88
  end
84
89
 
85
90
  def self.empty
86
- @empty ||= new([])
91
+ @empty ||= new([]).freeze
87
92
  end
88
93
 
89
- protected
94
+ def contradiction?
95
+ predicates.any? do |x|
96
+ case x
97
+ when Arel::Nodes::In
98
+ Array === x.right && x.right.empty?
99
+ when Arel::Nodes::Equality
100
+ x.right.respond_to?(:unboundable?) && x.right.unboundable?
101
+ end
102
+ end
103
+ end
104
+
105
+ def extract_attributes
106
+ predicates.each_with_object([]) do |node, attrs|
107
+ attr = extract_attribute(node) || begin
108
+ node.left if node.equality? && node.left.is_a?(Arel::Predications)
109
+ end
110
+ attrs << attr if attr
111
+ end
112
+ end
90
113
 
114
+ protected
91
115
  attr_reader :predicates
92
116
 
93
117
  def referenced_columns
94
- @referenced_columns ||= begin
95
- equality_nodes = predicates.select { |n| equality_node?(n) }
96
- Set.new(equality_nodes, &:left)
118
+ predicates.each_with_object({}) do |node, hash|
119
+ attr = extract_attribute(node) || begin
120
+ node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
121
+ end
122
+
123
+ hash[attr] = node if attr
97
124
  end
98
125
  end
99
126
 
100
127
  private
128
+ def extract_attribute(node)
129
+ attr_node = nil
130
+ Arel.fetch_attribute(node) do |attr|
131
+ return if attr_node&.!= attr # all attr nodes should be the same
132
+ attr_node = attr
133
+ end
134
+ attr_node
135
+ end
136
+
101
137
  def equalities(predicates)
102
138
  equalities = []
103
139
 
104
140
  predicates.each do |node|
105
- case node
106
- when Arel::Nodes::Equality
141
+ if equality_node?(node)
107
142
  equalities << node
108
- when Arel::Nodes::And
143
+ elsif node.is_a?(Arel::Nodes::And)
109
144
  equalities.concat equalities(node.children)
110
145
  end
111
146
  end
@@ -114,37 +149,55 @@ module ActiveRecord
114
149
  end
115
150
 
116
151
  def predicates_unreferenced_by(other)
117
- predicates.reject do |n|
118
- equality_node?(n) && other.referenced_columns.include?(n.left)
152
+ referenced_columns = other.referenced_columns
153
+
154
+ predicates.reject do |node|
155
+ attr = extract_attribute(node) || begin
156
+ node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
157
+ end
158
+ next false unless attr
159
+
160
+ ref = referenced_columns[attr]
161
+ next false unless ref
162
+
163
+ if equality_node?(node) && equality_node?(ref) || node == ref
164
+ true
165
+ else
166
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
167
+ Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
168
+ both conditions, and will be replaced by the latter in Rails 6.2.
169
+ To migrate to Rails 6.2's behavior, use `relation.merge(other, rewhere: true)`.
170
+ MSG
171
+ false
172
+ end
119
173
  end
120
174
  end
121
175
 
122
176
  def equality_node?(node)
123
- node.respond_to?(:operator) && node.operator == :==
177
+ !node.is_a?(String) && node.equality?
124
178
  end
125
179
 
126
180
  def invert_predicate(node)
127
181
  case node
128
182
  when NilClass
129
183
  raise ArgumentError, "Invalid argument for .where.not(), got nil."
130
- when Arel::Nodes::In
131
- Arel::Nodes::NotIn.new(node.left, node.right)
132
- when Arel::Nodes::IsNotDistinctFrom
133
- Arel::Nodes::IsDistinctFrom.new(node.left, node.right)
134
- when Arel::Nodes::IsDistinctFrom
135
- Arel::Nodes::IsNotDistinctFrom.new(node.left, node.right)
136
- when Arel::Nodes::Equality
137
- Arel::Nodes::NotEqual.new(node.left, node.right)
138
184
  when String
139
185
  Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
140
186
  else
141
- Arel::Nodes::Not.new(node)
187
+ node.invert
142
188
  end
143
189
  end
144
190
 
145
191
  def except_predicates(columns)
192
+ attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
193
+ non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
194
+
146
195
  predicates.reject do |node|
147
- Arel.fetch_attribute(node) { |attr| columns.include?(attr.name.to_s) }
196
+ if !non_attrs.empty? && node.equality? && node.left.is_a?(Arel::Predications)
197
+ non_attrs.include?(node.left)
198
+ end || Arel.fetch_attribute(node) do |attr|
199
+ attrs.include?(attr) || columns.include?(attr.name.to_s)
200
+ end
148
201
  end
149
202
  end
150
203
 
@@ -174,15 +227,8 @@ module ActiveRecord
174
227
  case node
175
228
  when Array
176
229
  node.map { |v| extract_node_value(v) }
177
- when Arel::Nodes::Casted, Arel::Nodes::Quoted
178
- node.val
179
- when Arel::Nodes::BindParam
180
- value = node.value
181
- if value.respond_to?(:value_before_type_cast)
182
- value.value_before_type_cast
183
- else
184
- value
185
- end
230
+ when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
231
+ node.value_before_type_cast
186
232
  end
187
233
  end
188
234
  end
@@ -65,16 +65,10 @@ module ActiveRecord
65
65
  end
66
66
  end
67
67
 
68
- def to_hash
69
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
70
- `ActiveRecord::Result#to_hash` has been renamed to `to_a`.
71
- `to_hash` is deprecated and will be removed in Rails 6.1.
72
- MSG
73
- to_a
74
- end
75
-
76
68
  alias :map! :map
77
69
  alias :collect! :map
70
+ deprecate "map!": :map
71
+ deprecate "collect!": :map
78
72
 
79
73
  # Returns true if there are no records, otherwise false.
80
74
  def empty?
@@ -92,31 +86,30 @@ module ActiveRecord
92
86
  hash_rows[idx]
93
87
  end
94
88
 
95
- # Returns the first record from the rows collection.
96
- # If the rows collection is empty, returns +nil+.
97
- def first
98
- return nil if @rows.empty?
99
- Hash[@columns.zip(@rows.first)]
100
- end
101
-
102
89
  # Returns the last record from the rows collection.
103
- # If the rows collection is empty, returns +nil+.
104
- def last
105
- return nil if @rows.empty?
106
- Hash[@columns.zip(@rows.last)]
90
+ def last(n = nil)
91
+ n ? hash_rows.last(n) : hash_rows.last
107
92
  end
108
93
 
109
94
  def cast_values(type_overrides = {}) # :nodoc:
110
95
  if columns.one?
111
96
  # Separated to avoid allocating an array per row
112
97
 
113
- type = column_type(columns.first, type_overrides)
98
+ type = if type_overrides.is_a?(Array)
99
+ type_overrides.first
100
+ else
101
+ column_type(columns.first, type_overrides)
102
+ end
114
103
 
115
104
  rows.map do |(value)|
116
105
  type.deserialize(value)
117
106
  end
118
107
  else
119
- types = columns.map { |name| column_type(name, type_overrides) }
108
+ types = if type_overrides.is_a?(Array)
109
+ type_overrides
110
+ else
111
+ columns.map { |name| column_type(name, type_overrides) }
112
+ end
120
113
 
121
114
  rows.map do |values|
122
115
  Array.new(values.size) { |i| types[i].deserialize(values[i]) }
@@ -132,7 +125,6 @@ module ActiveRecord
132
125
  end
133
126
 
134
127
  private
135
-
136
128
  def column_type(name, type_overrides = {})
137
129
  type_overrides.fetch(name) do
138
130
  column_types.fetch(name, Type.default_value)
@@ -146,21 +138,36 @@ module ActiveRecord
146
138
  # used as keys in ActiveRecord::Base's @attributes hash
147
139
  columns = @columns.map(&:-@)
148
140
  length = columns.length
141
+ template = nil
149
142
 
150
143
  @rows.map { |row|
151
- # In the past we used Hash[columns.zip(row)]
152
- # though elegant, the verbose way is much more efficient
153
- # both time and memory wise cause it avoids a big array allocation
154
- # this method is called a lot and needs to be micro optimised
155
- hash = {}
156
-
157
- index = 0
158
- while index < length
159
- hash[columns[index]] = row[index]
160
- index += 1
144
+ if template
145
+ # We use transform_values to build subsequent rows from the
146
+ # hash of the first row. This is faster because we avoid any
147
+ # reallocs and in Ruby 2.7+ avoid hashing entirely.
148
+ index = -1
149
+ template.transform_values do
150
+ row[index += 1]
151
+ end
152
+ else
153
+ # In the past we used Hash[columns.zip(row)]
154
+ # though elegant, the verbose way is much more efficient
155
+ # both time and memory wise cause it avoids a big array allocation
156
+ # this method is called a lot and needs to be micro optimised
157
+ hash = {}
158
+
159
+ index = 0
160
+ while index < length
161
+ hash[columns[index]] = row[index]
162
+ index += 1
163
+ end
164
+
165
+ # It's possible to select the same column twice, in which case
166
+ # we can't use a template
167
+ template = hash if hash.length == length
168
+
169
+ hash
161
170
  end
162
-
163
- hash
164
171
  }
165
172
  end
166
173
  end
@@ -14,9 +14,9 @@ module ActiveRecord
14
14
  class RuntimeRegistry # :nodoc:
15
15
  extend ActiveSupport::PerThreadRegistry
16
16
 
17
- attr_accessor :connection_handler, :sql_runtime
17
+ attr_accessor :sql_runtime
18
18
 
19
- [:connection_handler, :sql_runtime].each do |val|
19
+ [:sql_runtime].each do |val|
20
20
  class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
21
  class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
22
22
  end
@@ -67,7 +67,7 @@ module ActiveRecord
67
67
  )
68
68
 
69
69
  # Ensure we aren't dealing with a subclass of String that might
70
- # override methods we use (eg. Arel::Nodes::SqlLiteral).
70
+ # override methods we use (e.g. Arel::Nodes::SqlLiteral).
71
71
  if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
72
72
  condition = [String.new(condition.first), *condition[1..-1]]
73
73
  end
@@ -141,19 +141,7 @@ module ActiveRecord
141
141
  (unexpected ||= []) << arg
142
142
  end
143
143
 
144
- return unless unexpected
145
-
146
- if allow_unsafe_raw_sql == :deprecated
147
- ActiveSupport::Deprecation.warn(
148
- "Dangerous query method (method whose arguments are used as raw " \
149
- "SQL) called with non-attribute argument(s): " \
150
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
151
- "arguments will be disallowed in Rails 6.1. This method should " \
152
- "not be called with user-provided values, such as request " \
153
- "parameters or model attributes. Known-safe values can be passed " \
154
- "by wrapping them in Arel.sql()."
155
- )
156
- else
144
+ if unexpected
157
145
  raise(ActiveRecord::UnknownAttributeReference,
158
146
  "Query method called with non-attribute argument(s): " +
159
147
  unexpected.map(&:inspect).join(", ")
@@ -193,13 +181,14 @@ module ActiveRecord
193
181
 
194
182
  def quote_bound_value(value, c = connection)
195
183
  if value.respond_to?(:map) && !value.acts_like?(:string)
196
- quoted = value.map { |v| c.quote(v) }
197
- if quoted.empty?
184
+ values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
185
+ if values.empty?
198
186
  c.quote(nil)
199
187
  else
200
- quoted.join(",")
188
+ values.map! { |v| c.quote(v) }.join(",")
201
189
  end
202
190
  else
191
+ value = value.id_for_database if value.respond_to?(:id_for_database)
203
192
  c.quote(value)
204
193
  end
205
194
  end