activerecord 6.0.0.beta1 → 6.0.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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +455 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record/associations/association.rb +18 -1
  5. data/lib/active_record/associations/builder/association.rb +14 -18
  6. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  7. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  9. data/lib/active_record/associations/builder/has_many.rb +2 -0
  10. data/lib/active_record/associations/builder/has_one.rb +35 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  12. data/lib/active_record/associations/collection_association.rb +5 -6
  13. data/lib/active_record/associations/collection_proxy.rb +13 -42
  14. data/lib/active_record/associations/has_many_association.rb +1 -9
  15. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  16. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  17. data/lib/active_record/associations/join_dependency.rb +10 -9
  18. data/lib/active_record/associations/preloader/association.rb +37 -34
  19. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  20. data/lib/active_record/associations/preloader.rb +11 -6
  21. data/lib/active_record/associations.rb +3 -2
  22. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  23. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  24. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  25. data/lib/active_record/attribute_methods/query.rb +2 -3
  26. data/lib/active_record/attribute_methods/read.rb +3 -9
  27. data/lib/active_record/attribute_methods/write.rb +6 -12
  28. data/lib/active_record/attribute_methods.rb +3 -53
  29. data/lib/active_record/attributes.rb +13 -0
  30. data/lib/active_record/autosave_association.rb +15 -5
  31. data/lib/active_record/base.rb +0 -1
  32. data/lib/active_record/callbacks.rb +3 -3
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  40. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  42. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
  45. data/lib/active_record/connection_adapters/column.rb +17 -13
  46. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  47. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  48. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  49. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  50. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  51. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  52. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  53. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  55. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  56. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  58. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  59. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  60. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  61. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  62. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  63. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  64. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  65. data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
  66. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  67. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  68. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  69. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
  72. data/lib/active_record/connection_handling.rb +32 -16
  73. data/lib/active_record/core.rb +27 -20
  74. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  75. data/lib/active_record/database_configurations/url_config.rb +21 -16
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/dynamic_matchers.rb +1 -1
  78. data/lib/active_record/enum.rb +15 -0
  79. data/lib/active_record/errors.rb +18 -13
  80. data/lib/active_record/fixtures.rb +11 -6
  81. data/lib/active_record/gem_version.rb +1 -1
  82. data/lib/active_record/inheritance.rb +1 -1
  83. data/lib/active_record/insert_all.rb +179 -0
  84. data/lib/active_record/integration.rb +13 -1
  85. data/lib/active_record/internal_metadata.rb +5 -1
  86. data/lib/active_record/locking/optimistic.rb +3 -4
  87. data/lib/active_record/log_subscriber.rb +1 -1
  88. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  89. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/migration/command_recorder.rb +28 -14
  92. data/lib/active_record/migration/compatibility.rb +72 -63
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/persistence.rb +212 -19
  95. data/lib/active_record/querying.rb +18 -14
  96. data/lib/active_record/railtie.rb +9 -1
  97. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  98. data/lib/active_record/railties/databases.rake +124 -25
  99. data/lib/active_record/reflection.rb +18 -32
  100. data/lib/active_record/relation/calculations.rb +40 -44
  101. data/lib/active_record/relation/delegation.rb +23 -31
  102. data/lib/active_record/relation/finder_methods.rb +13 -13
  103. data/lib/active_record/relation/merger.rb +11 -16
  104. data/lib/active_record/relation/query_attribute.rb +5 -3
  105. data/lib/active_record/relation/query_methods.rb +217 -68
  106. data/lib/active_record/relation/spawn_methods.rb +1 -1
  107. data/lib/active_record/relation/where_clause.rb +10 -10
  108. data/lib/active_record/relation.rb +184 -35
  109. data/lib/active_record/sanitization.rb +33 -4
  110. data/lib/active_record/schema.rb +1 -1
  111. data/lib/active_record/schema_dumper.rb +10 -1
  112. data/lib/active_record/schema_migration.rb +1 -1
  113. data/lib/active_record/scoping/default.rb +7 -15
  114. data/lib/active_record/scoping/named.rb +10 -2
  115. data/lib/active_record/scoping.rb +6 -7
  116. data/lib/active_record/statement_cache.rb +2 -2
  117. data/lib/active_record/store.rb +48 -0
  118. data/lib/active_record/table_metadata.rb +9 -13
  119. data/lib/active_record/tasks/database_tasks.rb +109 -6
  120. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  121. data/lib/active_record/test_databases.rb +1 -16
  122. data/lib/active_record/test_fixtures.rb +2 -2
  123. data/lib/active_record/timestamp.rb +35 -19
  124. data/lib/active_record/touch_later.rb +4 -2
  125. data/lib/active_record/transactions.rb +55 -45
  126. data/lib/active_record/type_caster/connection.rb +16 -10
  127. data/lib/active_record/validations/uniqueness.rb +4 -4
  128. data/lib/active_record/validations.rb +1 -0
  129. data/lib/active_record.rb +7 -1
  130. data/lib/arel/insert_manager.rb +3 -3
  131. data/lib/arel/nodes/and.rb +1 -1
  132. data/lib/arel/nodes/case.rb +1 -1
  133. data/lib/arel/nodes/comment.rb +29 -0
  134. data/lib/arel/nodes/select_core.rb +16 -12
  135. data/lib/arel/nodes/unary.rb +1 -0
  136. data/lib/arel/nodes/values_list.rb +2 -17
  137. data/lib/arel/nodes.rb +2 -1
  138. data/lib/arel/select_manager.rb +10 -10
  139. data/lib/arel/visitors/depth_first.rb +7 -2
  140. data/lib/arel/visitors/dot.rb +7 -2
  141. data/lib/arel/visitors/ibm_db.rb +13 -0
  142. data/lib/arel/visitors/informix.rb +6 -0
  143. data/lib/arel/visitors/mssql.rb +15 -1
  144. data/lib/arel/visitors/oracle12.rb +4 -5
  145. data/lib/arel/visitors/postgresql.rb +4 -10
  146. data/lib/arel/visitors/to_sql.rb +107 -131
  147. data/lib/arel/visitors/visitor.rb +9 -5
  148. data/lib/arel.rb +7 -0
  149. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  150. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  151. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  152. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  153. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  154. metadata +17 -13
  155. data/lib/active_record/collection_cache_key.rb +0 -53
  156. data/lib/arel/nodes/values.rb +0 -16
@@ -41,18 +41,31 @@ module ActiveRecord
41
41
  #
42
42
  # User.where.not(name: %w(Ko1 Nobu))
43
43
  # # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
44
- #
45
- # User.where.not(name: "Jon", role: "admin")
46
- # # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
47
44
  def not(opts, *rest)
48
45
  opts = sanitize_forbidden_attributes(opts)
49
46
 
50
47
  where_clause = @scope.send(:where_clause_factory).build(opts, rest)
51
48
 
52
49
  @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
53
- @scope.where_clause += where_clause.invert
50
+
51
+ if not_behaves_as_nor?(opts)
52
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
53
+ NOT conditions will no longer behave as NOR in Rails 6.1.
54
+ To continue using NOR conditions, NOT each conditions manually
55
+ (`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
56
+ MSG
57
+ @scope.where_clause += where_clause.invert(:nor)
58
+ else
59
+ @scope.where_clause += where_clause.invert
60
+ end
61
+
54
62
  @scope
55
63
  end
64
+
65
+ private
66
+ def not_behaves_as_nor?(opts)
67
+ opts.is_a?(Hash) && opts.size > 1
68
+ end
56
69
  end
57
70
 
58
71
  FROZEN_EMPTY_ARRAY = [].freeze
@@ -67,11 +80,13 @@ module ActiveRecord
67
80
  end
68
81
  class_eval <<-CODE, __FILE__, __LINE__ + 1
69
82
  def #{method_name} # def includes_values
70
- get_value(#{name.inspect}) # get_value(:includes)
83
+ default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
84
+ @values.fetch(:#{name}, default) # @values.fetch(:includes, default)
71
85
  end # end
72
86
 
73
87
  def #{method_name}=(value) # def includes_values=(value)
74
- set_value(#{name.inspect}, value) # set_value(:includes, value)
88
+ assert_mutability! # assert_mutability!
89
+ @values[:#{name}] = value # @values[:includes] = value
75
90
  end # end
76
91
  CODE
77
92
  end
@@ -100,7 +115,7 @@ module ActiveRecord
100
115
  #
101
116
  # === conditions
102
117
  #
103
- # If you want to add conditions to your included models you'll have
118
+ # If you want to add string conditions to your included models, you'll have
104
119
  # to explicitly reference them. For example:
105
120
  #
106
121
  # User.includes(:posts).where('posts.name = ?', 'example')
@@ -111,6 +126,12 @@ module ActiveRecord
111
126
  #
112
127
  # Note that #includes works with association names while #references needs
113
128
  # the actual table name.
129
+ #
130
+ # If you pass the conditions via hash, you don't need to call #references
131
+ # explicitly, as #where references the tables for you. For example, this
132
+ # will work correctly:
133
+ #
134
+ # User.includes(:posts).where(posts: { name: 'example' })
114
135
  def includes(*args)
115
136
  check_if_method_has_arguments!(:includes, args)
116
137
  spawn.includes!(*args)
@@ -154,6 +175,19 @@ module ActiveRecord
154
175
  self
155
176
  end
156
177
 
178
+ # Extracts a named +association+ from the relation. The named association is first preloaded,
179
+ # then the individual association records are collected from the relation. Like so:
180
+ #
181
+ # account.memberships.extract_associated(:user)
182
+ # # => Returns collection of User records
183
+ #
184
+ # This is short-hand for:
185
+ #
186
+ # account.memberships.preload(:user).collect(&:user)
187
+ def extract_associated(association)
188
+ preload(association).collect(&association)
189
+ end
190
+
157
191
  # Use to indicate that the given +table_names+ are referenced by an SQL string,
158
192
  # and should therefore be JOINed in any query rather than loaded separately.
159
193
  # This method only works in conjunction with #includes.
@@ -233,13 +267,31 @@ module ActiveRecord
233
267
  def _select!(*fields) # :nodoc:
234
268
  fields.reject!(&:blank?)
235
269
  fields.flatten!
236
- fields.map! do |field|
237
- klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
238
- end
239
270
  self.select_values += fields
240
271
  self
241
272
  end
242
273
 
274
+ # Allows you to change a previously set select statement.
275
+ #
276
+ # Post.select(:title, :body)
277
+ # # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
278
+ #
279
+ # Post.select(:title, :body).reselect(:created_at)
280
+ # # SELECT `posts`.`created_at` FROM `posts`
281
+ #
282
+ # This is short-hand for <tt>unscope(:select).select(fields)</tt>.
283
+ # Note that we're unscoping the entire select statement.
284
+ def reselect(*args)
285
+ check_if_method_has_arguments!(:reselect, args)
286
+ spawn.reselect!(*args)
287
+ end
288
+
289
+ # Same as #reselect but operates on relation in-place instead of copying.
290
+ def reselect!(*args) # :nodoc:
291
+ self.select_values = args
292
+ self
293
+ end
294
+
243
295
  # Allows to specify a group attribute:
244
296
  #
245
297
  # User.group(:name)
@@ -320,7 +372,7 @@ module ActiveRecord
320
372
 
321
373
  # Same as #reorder but operates on relation in-place instead of copying.
322
374
  def reorder!(*args) # :nodoc:
323
- preprocess_order_args(args)
375
+ preprocess_order_args(args) unless args.all?(&:blank?)
324
376
 
325
377
  self.reordering_value = true
326
378
  self.order_values = args
@@ -328,8 +380,8 @@ module ActiveRecord
328
380
  end
329
381
 
330
382
  VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
331
- :limit, :offset, :joins, :left_outer_joins,
332
- :includes, :from, :readonly, :having])
383
+ :limit, :offset, :joins, :left_outer_joins, :annotate,
384
+ :includes, :from, :readonly, :having, :optimizer_hints])
333
385
 
334
386
  # Removes an unwanted relation that is already defined on a chain of relations.
335
387
  # This is useful when passing around chains of relations and would like to
@@ -380,7 +432,8 @@ module ActiveRecord
380
432
  if !VALID_UNSCOPING_VALUES.include?(scope)
381
433
  raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
382
434
  end
383
- set_value(scope, DEFAULT_VALUES[scope])
435
+ assert_mutability!
436
+ @values[scope] = DEFAULT_VALUES[scope]
384
437
  when Hash
385
438
  scope.each do |key, target_value|
386
439
  if key != :where
@@ -880,6 +933,29 @@ module ActiveRecord
880
933
  self
881
934
  end
882
935
 
936
+ # Specify optimizer hints to be used in the SELECT statement.
937
+ #
938
+ # Example (for MySQL):
939
+ #
940
+ # Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
941
+ # # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
942
+ #
943
+ # Example (for PostgreSQL with pg_hint_plan):
944
+ #
945
+ # Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
946
+ # # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
947
+ def optimizer_hints(*args)
948
+ check_if_method_has_arguments!(:optimizer_hints, args)
949
+ spawn.optimizer_hints!(*args)
950
+ end
951
+
952
+ def optimizer_hints!(*args) # :nodoc:
953
+ args.flatten!
954
+
955
+ self.optimizer_hints_values |= args
956
+ self
957
+ end
958
+
883
959
  # Reverse the existing order clause on the relation.
884
960
  #
885
961
  # User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
@@ -904,23 +980,47 @@ module ActiveRecord
904
980
  self
905
981
  end
906
982
 
983
+ # Adds an SQL comment to queries generated from this relation. For example:
984
+ #
985
+ # User.annotate("selecting user names").select(:name)
986
+ # # SELECT "users"."name" FROM "users" /* selecting user names */
987
+ #
988
+ # User.annotate("selecting", "user", "names").select(:name)
989
+ # # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
990
+ #
991
+ # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
992
+ def annotate(*args)
993
+ check_if_method_has_arguments!(:annotate, args)
994
+ spawn.annotate!(*args)
995
+ end
996
+
997
+ # Like #annotate, but modifies relation in place.
998
+ def annotate!(*args) # :nodoc:
999
+ self.annotate_values += args
1000
+ self
1001
+ end
1002
+
907
1003
  # Returns the Arel object associated with the relation.
908
1004
  def arel(aliases = nil) # :nodoc:
909
1005
  @arel ||= build_arel(aliases)
910
1006
  end
911
1007
 
912
- private
913
- # Returns a relation value with a given name
914
- def get_value(name)
915
- @values.fetch(name, DEFAULT_VALUES[name])
916
- end
1008
+ def construct_join_dependency(associations, join_type) # :nodoc:
1009
+ ActiveRecord::Associations::JoinDependency.new(
1010
+ klass, table, associations, join_type
1011
+ )
1012
+ end
1013
+
1014
+ protected
1015
+ def build_subquery(subquery_alias, select_value) # :nodoc:
1016
+ subquery = except(:optimizer_hints).arel.as(subquery_alias)
917
1017
 
918
- # Sets the relation value with the given name
919
- def set_value(name, value)
920
- assert_mutability!
921
- @values[name] = value
1018
+ Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
1019
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1020
+ end
922
1021
  end
923
1022
 
1023
+ private
924
1024
  def assert_mutability!
925
1025
  raise ImmutableRelation if @loaded
926
1026
  raise ImmutableRelation if defined?(@arel) && @arel
@@ -929,8 +1029,11 @@ module ActiveRecord
929
1029
  def build_arel(aliases)
930
1030
  arel = Arel::SelectManager.new(table)
931
1031
 
932
- aliases = build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty?
933
- build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty?
1032
+ if !joins_values.empty?
1033
+ build_joins(arel, joins_values.flatten, aliases)
1034
+ elsif !left_outer_joins_values.empty?
1035
+ build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
1036
+ end
934
1037
 
935
1038
  arel.where(where_clause.ast) unless where_clause.empty?
936
1039
  arel.having(having_clause.ast) unless having_clause.empty?
@@ -956,9 +1059,11 @@ module ActiveRecord
956
1059
 
957
1060
  build_select(arel)
958
1061
 
1062
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
959
1063
  arel.distinct(distinct_value)
960
1064
  arel.from(build_from) unless from_clause.empty?
961
1065
  arel.lock(lock_value) if lock_value
1066
+ arel.comment(*annotate_values) unless annotate_values.empty?
962
1067
 
963
1068
  arel
964
1069
  end
@@ -978,32 +1083,56 @@ module ActiveRecord
978
1083
  end
979
1084
  end
980
1085
 
981
- def build_left_outer_joins(manager, outer_joins, aliases)
982
- buckets = outer_joins.group_by do |join|
983
- case join
1086
+ def valid_association_list(associations)
1087
+ associations.each do |association|
1088
+ case association
984
1089
  when Hash, Symbol, Array
985
- :association_join
986
- when ActiveRecord::Associations::JoinDependency
987
- :stashed_join
1090
+ # valid
988
1091
  else
989
1092
  raise ArgumentError, "only Hash, Symbol and Array are allowed"
990
1093
  end
991
1094
  end
1095
+ end
992
1096
 
1097
+ def build_left_outer_joins(manager, outer_joins, aliases)
1098
+ buckets = Hash.new { |h, k| h[k] = [] }
1099
+ buckets[:association_join] = valid_association_list(outer_joins)
993
1100
  build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
994
1101
  end
995
1102
 
996
1103
  def build_joins(manager, joins, aliases)
997
- buckets = joins.group_by do |join|
1104
+ buckets = Hash.new { |h, k| h[k] = [] }
1105
+
1106
+ unless left_outer_joins_values.empty?
1107
+ left_joins = valid_association_list(left_outer_joins_values.flatten)
1108
+ buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1109
+ end
1110
+
1111
+ joins.map! do |join|
1112
+ if join.is_a?(String)
1113
+ table.create_string_join(Arel.sql(join.strip)) unless join.blank?
1114
+ else
1115
+ join
1116
+ end
1117
+ end.delete_if(&:blank?).uniq!
1118
+
1119
+ while joins.first.is_a?(Arel::Nodes::Join)
1120
+ join_node = joins.shift
1121
+ if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
1122
+ buckets[:join_node] << join_node
1123
+ else
1124
+ buckets[:leading_join] << join_node
1125
+ end
1126
+ end
1127
+
1128
+ joins.each do |join|
998
1129
  case join
999
- when String
1000
- :string_join
1001
1130
  when Hash, Symbol, Array
1002
- :association_join
1131
+ buckets[:association_join] << join
1003
1132
  when ActiveRecord::Associations::JoinDependency
1004
- :stashed_join
1133
+ buckets[:stashed_join] << join
1005
1134
  when Arel::Nodes::Join
1006
- :join_node
1135
+ buckets[:join_node] << join
1007
1136
  else
1008
1137
  raise "unknown class: %s" % join.class.name
1009
1138
  end
@@ -1013,31 +1142,21 @@ module ActiveRecord
1013
1142
  end
1014
1143
 
1015
1144
  def build_join_query(manager, buckets, join_type, aliases)
1016
- buckets.default = []
1017
-
1018
1145
  association_joins = buckets[:association_join]
1019
1146
  stashed_joins = buckets[:stashed_join]
1020
- join_nodes = buckets[:join_node].uniq
1021
- string_joins = buckets[:string_join].map(&:strip).uniq
1022
-
1023
- join_list = join_nodes + convert_join_strings_to_ast(string_joins)
1024
- alias_tracker = alias_tracker(join_list, aliases)
1025
-
1026
- join_dependency = construct_join_dependency(association_joins)
1147
+ leading_joins = buckets[:leading_join]
1148
+ join_nodes = buckets[:join_node]
1027
1149
 
1028
- joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
1029
- joins.each { |join| manager.from(join) }
1150
+ join_sources = manager.join_sources
1151
+ join_sources.concat(leading_joins) unless leading_joins.empty?
1030
1152
 
1031
- manager.join_sources.concat(join_list)
1032
-
1033
- alias_tracker.aliases
1034
- end
1153
+ unless association_joins.empty? && stashed_joins.empty?
1154
+ alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
1155
+ join_dependency = construct_join_dependency(association_joins, join_type)
1156
+ join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
1157
+ end
1035
1158
 
1036
- def convert_join_strings_to_ast(joins)
1037
- joins
1038
- .flatten
1039
- .reject(&:blank?)
1040
- .map { |join| table.create_string_join(Arel.sql(join)) }
1159
+ join_sources.concat(join_nodes) unless join_nodes.empty?
1041
1160
  end
1042
1161
 
1043
1162
  def build_select(arel)
@@ -1052,11 +1171,14 @@ module ActiveRecord
1052
1171
 
1053
1172
  def arel_columns(columns)
1054
1173
  columns.flat_map do |field|
1055
- if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
1056
- arel_attribute(field)
1057
- elsif Symbol === field
1058
- connection.quote_table_name(field.to_s)
1059
- elsif Proc === field
1174
+ case field
1175
+ when Symbol
1176
+ arel_column(field.to_s) do |attr_name|
1177
+ connection.quote_table_name(attr_name)
1178
+ end
1179
+ when String
1180
+ arel_column(field, &:itself)
1181
+ when Proc
1060
1182
  field.call
1061
1183
  else
1062
1184
  field
@@ -1064,6 +1186,21 @@ module ActiveRecord
1064
1186
  end
1065
1187
  end
1066
1188
 
1189
+ def arel_column(field)
1190
+ field = klass.attribute_aliases[field] || field
1191
+ from = from_clause.name || from_clause.value
1192
+
1193
+ if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1194
+ arel_attribute(field)
1195
+ else
1196
+ yield field
1197
+ end
1198
+ end
1199
+
1200
+ def table_name_matches?(from)
1201
+ /(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
1202
+ end
1203
+
1067
1204
  def reverse_sql_order(order_query)
1068
1205
  if order_query.empty?
1069
1206
  return [arel_attribute(primary_key).desc] if primary_key
@@ -1079,7 +1216,7 @@ module ActiveRecord
1079
1216
  o.reverse
1080
1217
  when String
1081
1218
  if does_not_support_reverse?(o)
1082
- raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
1219
+ raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
1083
1220
  end
1084
1221
  o.split(",").map! do |s|
1085
1222
  s.strip!
@@ -1099,7 +1236,7 @@ module ActiveRecord
1099
1236
  # Uses SQL function with multiple arguments.
1100
1237
  (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
1101
1238
  # Uses "nulls first" like construction.
1102
- /nulls (first|last)\Z/i.match?(order)
1239
+ /\bnulls\s+(?:first|last)\b/i.match?(order)
1103
1240
  end
1104
1241
 
1105
1242
  def build_order(arel)
@@ -1125,6 +1262,7 @@ module ActiveRecord
1125
1262
  end
1126
1263
 
1127
1264
  def preprocess_order_args(order_args)
1265
+ order_args.reject!(&:blank?)
1128
1266
  order_args.map! do |arg|
1129
1267
  klass.sanitize_sql_for_order(arg)
1130
1268
  end
@@ -1132,7 +1270,7 @@ module ActiveRecord
1132
1270
 
1133
1271
  @klass.disallow_raw_sql!(
1134
1272
  order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1135
- permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
1273
+ permit: connection.column_name_with_order_matcher
1136
1274
  )
1137
1275
 
1138
1276
  validate_order_args(order_args)
@@ -1145,14 +1283,14 @@ module ActiveRecord
1145
1283
  order_args.map! do |arg|
1146
1284
  case arg
1147
1285
  when Symbol
1148
- arel_attribute(arg).asc
1286
+ order_column(arg.to_s).asc
1149
1287
  when Hash
1150
1288
  arg.map { |field, dir|
1151
1289
  case field
1152
1290
  when Arel::Nodes::SqlLiteral
1153
1291
  field.send(dir.downcase)
1154
1292
  else
1155
- arel_attribute(field).send(dir.downcase)
1293
+ order_column(field.to_s).send(dir.downcase)
1156
1294
  end
1157
1295
  }
1158
1296
  else
@@ -1161,6 +1299,16 @@ module ActiveRecord
1161
1299
  end.flatten!
1162
1300
  end
1163
1301
 
1302
+ def order_column(field)
1303
+ arel_column(field) do |attr_name|
1304
+ if attr_name == "count" && !group_values.empty?
1305
+ arel_attribute(attr_name)
1306
+ else
1307
+ Arel.sql(connection.quote_table_name(attr_name))
1308
+ end
1309
+ end
1310
+ end
1311
+
1164
1312
  # Checks to make sure that the arguments are not blank. Note that if some
1165
1313
  # blank-like object were initially passed into the query method, then this
1166
1314
  # method will not raise an error.
@@ -1187,7 +1335,8 @@ module ActiveRecord
1187
1335
  def structurally_incompatible_values_for_or(other)
1188
1336
  values = other.values
1189
1337
  STRUCTURAL_OR_METHODS.reject do |method|
1190
- get_value(method) == values.fetch(method, DEFAULT_VALUES[method])
1338
+ default = DEFAULT_VALUES[method]
1339
+ @values.fetch(method, default) == values.fetch(method, default)
1191
1340
  end
1192
1341
  end
1193
1342
 
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  module SpawnMethods
9
9
  # This is overridden by Associations::CollectionProxy
10
10
  def spawn #:nodoc:
11
- @delegate_to_klass ? klass.all : clone
11
+ already_in_scope? ? klass.all : clone
12
12
  end
13
13
 
14
14
  # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
@@ -70,7 +70,15 @@ module ActiveRecord
70
70
  predicates == other.predicates
71
71
  end
72
72
 
73
- def invert
73
+ def invert(as = :nand)
74
+ if predicates.size == 1
75
+ inverted_predicates = [ invert_predicate(predicates.first) ]
76
+ elsif as == :nor
77
+ inverted_predicates = predicates.map { |node| invert_predicate(node) }
78
+ else
79
+ inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
80
+ end
81
+
74
82
  WhereClause.new(inverted_predicates)
75
83
  end
76
84
 
@@ -115,10 +123,6 @@ module ActiveRecord
115
123
  node.respond_to?(:operator) && node.operator == :==
116
124
  end
117
125
 
118
- def inverted_predicates
119
- predicates.map { |node| invert_predicate(node) }
120
- end
121
-
122
126
  def invert_predicate(node)
123
127
  case node
124
128
  when NilClass
@@ -140,11 +144,7 @@ module ActiveRecord
140
144
 
141
145
  def except_predicates(columns)
142
146
  predicates.reject do |node|
143
- case node
144
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
145
- subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
146
- columns.include?(subrelation.name.to_s)
147
- end
147
+ Arel.fetch_attribute(node) { |attr| columns.include?(attr.name.to_s) }
148
148
  end
149
149
  end
150
150