query_methods.rb 47.0 KB
Newer Older
1 2
# frozen_string_literal: true

3 4 5 6
require "active_record/relation/from_clause"
require "active_record/relation/query_attribute"
require "active_record/relation/where_clause"
require "active_record/relation/where_clause_factory"
7
require "active_model/forbidden_attributes_protection"
8

9 10
module ActiveRecord
  module QueryMethods
11 12
    extend ActiveSupport::Concern

13 14
    include ActiveModel::ForbiddenAttributesProtection

15
    # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
16
    # In this case, #where must be chained with #not to return a new relation.
17
    class WhereChain
18 19
      include ActiveModel::ForbiddenAttributesProtection

20 21 22 23
      def initialize(scope)
        @scope = scope
      end

24 25
      # Returns a new relation expressing WHERE + NOT condition according to
      # the conditions in the arguments.
26
      #
27
      # #not accepts conditions as a string, array, or hash. See QueryMethods#where for
28
      # more details on each format.
29 30
      #
      #    User.where.not("name = 'Jon'")
31
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
32 33
      #
      #    User.where.not(["name = ?", "Jon"])
34
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
35
      #
36
      #    User.where.not(name: "Jon")
37
      #    # SELECT * FROM users WHERE name != 'Jon'
38 39 40 41
      #
      #    User.where.not(name: nil)
      #    # SELECT * FROM users WHERE name IS NOT NULL
      #
42
      #    User.where.not(name: %w(Ko1 Nobu))
43 44
      #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
      def not(opts, *rest)
45 46
        opts = sanitize_forbidden_attributes(opts)

47
        where_clause = @scope.send(:where_clause_factory).build(opts, rest)
48 49

        @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
50 51 52 53 54

        if not_behaves_as_nor?(opts)
          ActiveSupport::Deprecation.warn(<<~MSG.squish)
            NOT conditions will no longer behave as NOR in Rails 6.1.
            To continue using NOR conditions, NOT each conditions manually
55 56 57 58 59 60 61 62 63
            (`#{
              opts.flat_map { |key, value|
                if value.is_a?(Hash) && value.size > 1
                  value.map { |k, v| ".where.not(#{key.inspect} => { #{k.inspect} => ... })" }
                else
                  ".where.not(#{key.inspect} => ...)"
                end
              }.join
            }`).
64 65 66 67 68 69
          MSG
          @scope.where_clause += where_clause.invert(:nor)
        else
          @scope.where_clause += where_clause.invert
        end

70 71
        @scope
      end
72

73 74 75 76 77 78
      # Returns a new relation with left outer joins and where clause to idenitfy
      # missing relations.
      #
      # For example, posts that are missing a related author:
      #
      #    Post.where.missing(:author)
79 80 81
      #    # SELECT "posts".* FROM "posts"
      #    # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
      #    # WHERE "authors"."id" IS NULL
82
      #
83 84
      # Additionally, multiple relations can be combined. This will return posts
      # that are missing both an author and any` comments:
85 86
      #
      #    Post.where.missing(:author, :comments)
87 88 89 90
      #    # SELECT "posts".* FROM "posts"
      #    # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
      #    # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
      #    # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
91 92 93 94 95 96 97 98 99 100 101
      def missing(*args)
        args.each do |arg|
          reflection = @scope.klass._reflect_on_association(arg)
          opts = { reflection.table_name => { reflection.association_primary_key => nil } }
          @scope.left_outer_joins!(arg)
          @scope.where!(opts)
        end

        @scope
      end

102 103
      private
        def not_behaves_as_nor?(opts)
104 105 106 107
          return false unless opts.is_a?(Hash)

          opts.any? { |k, v| v.is_a?(Hash) && v.size > 1 } ||
            opts.size > 1
108
        end
109 110
    end

111
    FROZEN_EMPTY_ARRAY = [].freeze
112
    FROZEN_EMPTY_HASH = {}.freeze
113

114
    Relation::VALUE_METHODS.each do |name|
R
Ryuta Kamizono 已提交
115
      method_name, default =
116
        case name
R
Ryuta Kamizono 已提交
117 118 119 120 121
        when *Relation::MULTI_VALUE_METHODS
          ["#{name}_values", "FROZEN_EMPTY_ARRAY"]
        when *Relation::SINGLE_VALUE_METHODS
          ["#{name}_value", name == :create_with ? "FROZEN_EMPTY_HASH" : "nil"]
        when *Relation::CLAUSE_METHODS
122
          ["#{name}_clause", name == :from ? "Relation::FromClause.empty" : "Relation::WhereClause.empty"]
123
        end
R
Ryuta Kamizono 已提交
124

125
      class_eval <<-CODE, __FILE__, __LINE__ + 1
R
Ryuta Kamizono 已提交
126 127 128 129 130 131 132 133
        def #{method_name}                     # def includes_values
          @values.fetch(:#{name}, #{default})  #   @values.fetch(:includes, FROZEN_EMPTY_ARRAY)
        end                                    # end

        def #{method_name}=(value)             # def includes_values=(value)
          assert_mutability!                   #   assert_mutability!
          @values[:#{name}] = value            #   @values[:includes] = value
        end                                    # end
134
      CODE
135 136
    end

137
    alias extensions extending_values
138

O
Oscar Del Ben 已提交
139 140 141 142 143 144 145 146 147 148
    # Specify relationships to be included in the result set. For
    # example:
    #
    #   users = User.includes(:address)
    #   users.each do |user|
    #     user.address.city
    #   end
    #
    # allows you to access the +address+ attribute of the +User+ model without
    # firing an additional query. This will often result in a
149
    # performance improvement over a simple join.
150
    #
151 152 153 154 155 156 157 158
    # You can also specify multiple relationships, like this:
    #
    #   users = User.includes(:address, :friends)
    #
    # Loading nested relationships is possible using a Hash:
    #
    #   users = User.includes(:address, friends: [:address, :followers])
    #
159 160
    # === conditions
    #
161
    # If you want to add string conditions to your included models, you'll have
162 163 164 165 166 167 168
    # to explicitly reference them. For example:
    #
    #   User.includes(:posts).where('posts.name = ?', 'example')
    #
    # Will throw an error, but this will work:
    #
    #   User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
169
    #
170
    # Note that #includes works with association names while #references needs
171
    # the actual table name.
172 173 174 175 176
    #
    # If you pass the conditions via hash, you don't need to call #references
    # explicitly, as #where references the tables for you. For example, this
    # will work correctly:
    #
177
    #   User.includes(:posts).where(posts: { name: 'example' })
178
    def includes(*args)
179
      check_if_method_has_arguments!(:includes, args)
180
      spawn.includes!(*args)
181
    end
182

J
Jon Leighton 已提交
183
    def includes!(*args) # :nodoc:
184
      args.compact_blank!
185
      args.flatten!
A
Aaron Patterson 已提交
186

187
      self.includes_values |= args
188
      self
189
    end
190

191 192 193
    # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
    #
    #   User.eager_load(:posts)
194 195 196
    #   # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
    #   # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
    #   # "users"."id"
197
    def eager_load(*args)
198
      check_if_method_has_arguments!(:eager_load, args)
199
      spawn.eager_load!(*args)
200
    end
201

J
Jon Leighton 已提交
202
    def eager_load!(*args) # :nodoc:
203
      self.eager_load_values |= args
204
      self
205 206
    end

207
    # Allows preloading of +args+, in the same way that #includes does:
208 209
    #
    #   User.preload(:posts)
210
    #   # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
211
    def preload(*args)
212
      check_if_method_has_arguments!(:preload, args)
213
      spawn.preload!(*args)
214
    end
215

J
Jon Leighton 已提交
216
    def preload!(*args) # :nodoc:
217
      self.preload_values |= args
218
      self
219
    end
220

221 222 223 224 225 226 227 228 229 230 231 232 233
    # Extracts a named +association+ from the relation. The named association is first preloaded,
    # then the individual association records are collected from the relation. Like so:
    #
    #   account.memberships.extract_associated(:user)
    #   # => Returns collection of User records
    #
    # This is short-hand for:
    #
    #   account.memberships.preload(:user).collect(&:user)
    def extract_associated(association)
      preload(association).collect(&association)
    end

234 235
    # Use to indicate that the given +table_names+ are referenced by an SQL string,
    # and should therefore be JOINed in any query rather than loaded separately.
236
    # This method only works in conjunction with #includes.
237
    # See #includes for more details.
238 239
    #
    #   User.includes(:posts).where("posts.name = 'foo'")
240
    #   # Doesn't JOIN the posts table, resulting in an error.
241 242
    #
    #   User.includes(:posts).where("posts.name = 'foo'").references(:posts)
243
    #   # Query now knows the string references posts, so adds a JOIN
244 245 246
    def references(*table_names)
      check_if_method_has_arguments!(:references, table_names)
      spawn.references!(*table_names)
247
    end
248

249 250 251
    def references!(*table_names) # :nodoc:
      table_names.flatten!
      table_names.map!(&:to_s)
252

253
      self.references_values |= table_names
254
      self
255 256
    end

257
    # Works in two unique ways.
258
    #
259
    # First: takes a block so it can be used just like <tt>Array#select</tt>.
260
    #
261
    #   Model.all.select { |m| m.field == value }
262 263
    #
    # This will build an array of objects from the database for the scope,
264 265
    # converting them into an array and iterating through them using
    # <tt>Array#select</tt>.
266 267
    #
    # Second: Modifies the SELECT statement for the query so that only certain
V
Vijay Dev 已提交
268
    # fields are retrieved:
269
    #
270
    #   Model.select(:field)
271
    #   # => [#<Model id: nil, field: "value">]
272 273
    #
    # Although in the above example it looks as though this method returns an
V
Vijay Dev 已提交
274
    # array, it actually returns a relation object and can have other query
275 276
    # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
    #
277
    # The argument to the method can also be an array of fields.
278
    #
279
    #   Model.select(:field, :other_field, :and_one_more)
280
    #   # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
281
    #
282 283 284
    # You can also use one or more strings, which will be used unchanged as SELECT fields.
    #
    #   Model.select('field AS field_one', 'other_field AS field_two')
285
    #   # => [#<Model id: nil, field: "value", other_field: "value">]
286 287 288 289 290 291
    #
    # If an alias was specified, it will be accessible from the resulting objects:
    #
    #   Model.select('field AS field_one').first.field_one
    #   # => "value"
    #
292
    # Accessing attributes of an object that do not have fields retrieved by a select
293
    # except +id+ will throw ActiveModel::MissingAttributeError:
294
    #
295 296 297
    #   Model.select(:field).first.other_field
    #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
    def select(*fields)
298 299
      if block_given?
        if fields.any?
300
          raise ArgumentError, "`select' with block doesn't take arguments."
301 302 303 304 305
        end

        return super()
      end

306
      raise ArgumentError, "Call `select' with at least one field" if fields.empty?
307
      spawn._select!(*fields)
308 309
    end

310
    def _select!(*fields) # :nodoc:
311
      fields.compact_blank!
312 313
      fields.flatten!
      self.select_values += fields
314
      self
315
    end
S
Santiago Pastorino 已提交
316

W
Willian Gustavo Veiga 已提交
317 318 319
    # Allows you to change a previously set select statement.
    #
    #   Post.select(:title, :body)
320
    #   # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
W
Willian Gustavo Veiga 已提交
321 322
    #
    #   Post.select(:title, :body).reselect(:created_at)
323
    #   # SELECT `posts`.`created_at` FROM `posts`
W
Willian Gustavo Veiga 已提交
324 325 326
    #
    # This is short-hand for <tt>unscope(:select).select(fields)</tt>.
    # Note that we're unscoping the entire select statement.
327 328 329 330 331 332 333 334 335
    def reselect(*args)
      check_if_method_has_arguments!(:reselect, args)
      spawn.reselect!(*args)
    end

    # Same as #reselect but operates on relation in-place instead of copying.
    def reselect!(*args) # :nodoc:
      self.select_values = args
      self
W
Willian Gustavo Veiga 已提交
336
    end
S
Santiago Pastorino 已提交
337

O
Oscar Del Ben 已提交
338 339 340
    # Allows to specify a group attribute:
    #
    #   User.group(:name)
341
    #   # SELECT "users".* FROM "users" GROUP BY name
O
Oscar Del Ben 已提交
342
    #
343
    # Returns an array with distinct records based on the +group+ attribute:
O
Oscar Del Ben 已提交
344 345
    #
    #   User.select([:id, :name])
346
    #   # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
O
Oscar Del Ben 已提交
347 348
    #
    #   User.group(:name)
349
    #   # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
350 351
    #
    #   User.group('name AS grouped_name, age')
352
    #   # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
353 354
    #
    # Passing in an array of attributes to group by is also supported.
355
    #
356
    #   User.select([:id, :first_name]).group(:id, :first_name).first(3)
357
    #   # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
358
    def group(*args)
359
      check_if_method_has_arguments!(:group, args)
360
      spawn.group!(*args)
361
    end
362

J
Jon Leighton 已提交
363
    def group!(*args) # :nodoc:
364 365
      args.flatten!

366
      self.group_values |= args
367
      self
368
    end
369

O
Oscar Del Ben 已提交
370 371
    # Allows to specify an order attribute:
    #
372
    #   User.order(:name)
373
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
374
    #
375
    #   User.order(email: :desc)
376
    #   # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
377
    #
378
    #   User.order(:name, email: :desc)
379
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
380 381
    #
    #   User.order('name')
382
    #   # SELECT "users".* FROM "users" ORDER BY name
383 384
    #
    #   User.order('name DESC')
385
    #   # SELECT "users".* FROM "users" ORDER BY name DESC
386 387
    #
    #   User.order('name DESC, email')
388
    #   # SELECT "users".* FROM "users" ORDER BY name DESC, email
389
    def order(*args)
390
      check_if_method_has_arguments!(:order, args)
391
      spawn.order!(*args)
392
    end
393

394
    # Same as #order but operates on relation in-place instead of copying.
J
Jon Leighton 已提交
395
    def order!(*args) # :nodoc:
396
      preprocess_order_args(args)
397

398
      self.order_values += args
399
      self
400
    end
401

402 403 404 405 406 407 408 409
    # Replaces any existing order defined on the relation with the specified order.
    #
    #   User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
    #
    # Subsequent calls to order on the same relation will be appended. For example:
    #
    #   User.order('email DESC').reorder('id ASC').order('name ASC')
    #
410
    # generates a query with 'ORDER BY id ASC, name ASC'.
S
Sebastian Martinez 已提交
411
    def reorder(*args)
412
      check_if_method_has_arguments!(:reorder, args)
413
      spawn.reorder!(*args)
414
    end
415

416
    # Same as #reorder but operates on relation in-place instead of copying.
J
Jon Leighton 已提交
417
    def reorder!(*args) # :nodoc:
418
      preprocess_order_args(args) unless args.all?(&:blank?)
419

420
      self.reordering_value = true
421
      self.order_values = args
422
      self
S
Sebastian Martinez 已提交
423 424
    end

425
    VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
426
                                     :limit, :offset, :joins, :left_outer_joins, :annotate,
427
                                     :includes, :from, :readonly, :having, :optimizer_hints])
428 429 430 431 432

    # Removes an unwanted relation that is already defined on a chain of relations.
    # This is useful when passing around chains of relations and would like to
    # modify the relations without reconstructing the entire chain.
    #
433
    #   User.order('email DESC').unscope(:order) == User.all
434 435 436 437 438
    #
    # The method arguments are symbols which correspond to the names of the methods
    # which should be unscoped. The valid arguments are given in VALID_UNSCOPING_VALUES.
    # The method can also be called with multiple arguments. For example:
    #
439
    #   User.order('email DESC').select('id').where(name: "John")
440 441
    #       .unscope(:order, :select, :where) == User.all
    #
442
    # One can additionally pass a hash as an argument to unscope specific +:where+ values.
443
    # This is done by passing a hash with a single key-value pair. The key should be
444
    # +:where+ and the value should be the where value to unscope. For example:
445
    #
446 447
    #   User.where(name: "John", active: true).unscope(where: :name)
    #       == User.where(active: true)
448
    #
449 450
    # This method is similar to #except, but unlike
    # #except, it persists across merges:
451
    #
J
Jon Leighton 已提交
452 453
    #   User.order('email').merge(User.except(:order))
    #       == User.order('email')
454
    #
J
Jon Leighton 已提交
455 456 457 458 459
    #   User.order('email').merge(User.unscope(:order))
    #       == User.all
    #
    # This means it can be used in association definitions:
    #
R
Ryuta Kamizono 已提交
460
    #   has_many :comments, -> { unscope(where: :trashed) }
461 462
    #
    def unscope(*args)
463
      check_if_method_has_arguments!(:unscope, args)
464 465 466
      spawn.unscope!(*args)
    end

467
    def unscope!(*args) # :nodoc:
468
      args.flatten!
J
Jon Leighton 已提交
469
      self.unscope_values += args
470 471 472 473

      args.each do |scope|
        case scope
        when Symbol
474
          scope = :left_outer_joins if scope == :left_joins
475 476 477
          if !VALID_UNSCOPING_VALUES.include?(scope)
            raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
          end
478
          assert_mutability!
479
          @values.delete(scope)
480 481 482 483 484 485
        when Hash
          scope.each do |key, target_value|
            if key != :where
              raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
            end

486 487
            target_values = Array(target_value).map(&:to_s)
            self.where_clause = where_clause.except(*target_values)
488 489 490 491 492 493 494 495 496
          end
        else
          raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
        end
      end

      self
    end

497 498
    # Performs a joins on +args+. The given symbol(s) should match the name of
    # the association(s).
499 500
    #
    #   User.joins(:posts)
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
    #   # SELECT "users".*
    #   # FROM "users"
    #   # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
    #
    # Multiple joins:
    #
    #   User.joins(:posts, :account)
    #   # SELECT "users".*
    #   # FROM "users"
    #   # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
    #   # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
    #
    # Nested joins:
    #
    #   User.joins(posts: [:comments])
    #   # SELECT "users".*
    #   # FROM "users"
    #   # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
    #   # INNER JOIN "comments" "comments_posts"
    #   #   ON "comments_posts"."post_id" = "posts"."id"
521 522 523 524
    #
    # You can use strings in order to customize your joins:
    #
    #   User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
525
    #   # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
526
    def joins(*args)
527 528
      check_if_method_has_arguments!(:joins, args)
      spawn.joins!(*args)
529
    end
530

J
Jon Leighton 已提交
531
    def joins!(*args) # :nodoc:
532 533
      args.compact!
      args.flatten!
534
      self.joins_values |= args
535
      self
P
Pratik Naik 已提交
536 537
    end

538 539 540 541 542 543
    # Performs a left outer joins on +args+:
    #
    #   User.left_outer_joins(:posts)
    #   => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
    #
    def left_outer_joins(*args)
544
      check_if_method_has_arguments!(__callee__, args)
545 546
      spawn.left_outer_joins!(*args)
    end
547
    alias :left_joins :left_outer_joins
548 549

    def left_outer_joins!(*args) # :nodoc:
550 551
      args.compact!
      args.flatten!
552
      self.left_outer_joins_values |= args
553 554 555
      self
    end

556 557 558 559 560 561 562 563 564 565
    # Returns a new relation, which is the result of filtering the current relation
    # according to the conditions in the arguments.
    #
    # #where accepts conditions in one of several formats. In the examples below, the resulting
    # SQL is given as an illustration; the actual query generated may be different depending
    # on the database adapter.
    #
    # === string
    #
    # A single string, without additional arguments, is passed to the query
566
    # constructor as an SQL fragment, and used in the where clause of the query.
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
    #
    #    Client.where("orders_count = '2'")
    #    # SELECT * from clients where orders_count = '2';
    #
    # Note that building your own string from user input may expose your application
    # to injection attacks if not done properly. As an alternative, it is recommended
    # to use one of the following methods.
    #
    # === array
    #
    # If an array is passed, then the first element of the array is treated as a template, and
    # the remaining elements are inserted into the template to generate the condition.
    # Active Record takes care of building the query to avoid injection attacks, and will
    # convert from the ruby type to the database type where needed. Elements are inserted
    # into the string in the order in which they appear.
    #
    #   User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # Alternatively, you can use named placeholders in the template, and pass a hash as the
    # second element of the array. The names in the template are replaced with the corresponding
    # values from the hash.
    #
    #   User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # This can make for more readable code in complex queries.
    #
    # Lastly, you can use sprintf-style % escapes in the template. This works slightly differently
    # than the previous methods; you are responsible for ensuring that the values in the template
    # are properly quoted. The values are passed to the connector for quoting, but the caller
    # is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
599
    # the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    #
    #   User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # If #where is called with multiple arguments, these are treated as if they were passed as
    # the elements of a single array.
    #
    #   User.where("name = :name and email = :email", { name: "Joe", email: "joe@example.com" })
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # When using strings to specify conditions, you can use any operator available from
    # the database. While this provides the most flexibility, you can also unintentionally introduce
    # dependencies on the underlying database. If your code is intended for general consumption,
    # test with multiple database backends.
    #
    # === hash
    #
    # #where will also accept a hash condition, in which the keys are fields and the values
    # are values to be searched for.
    #
    # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
    #
    #    User.where({ name: "Joe", email: "joe@example.com" })
    #    # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
    #
    #    User.where({ name: ["Alice", "Bob"]})
    #    # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
    #
    #    User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
    #    # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
    #
631 632 633 634 635 636
    # In the case of a belongs_to relationship, an association key can be used
    # to specify the model if an ActiveRecord object is used as the value.
    #
    #    author = Author.find(1)
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
637 638
    #    Post.where(author: author)
    #    Post.where(author_id: author)
639 640 641
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
642 643
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
644 645
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
646 647
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
648
    #
649 650 651 652 653 654 655 656 657 658
    # === Joins
    #
    # If the relation is the result of a join, you may create a condition which uses any of the
    # tables in the join. For string and array conditions, use the table name in the condition.
    #
    #    User.joins(:posts).where("posts.created_at < ?", Time.now)
    #
    # For hash conditions, you can either use the table name in the key, or use a sub-hash.
    #
    #    User.joins(:posts).where({ "posts.published" => true })
A
AvnerCohen 已提交
659
    #    User.joins(:posts).where({ posts: { published: true } })
660
    #
661
    # === no argument
662
    #
663 664
    # If no argument is passed, #where returns a new instance of WhereChain, that
    # can be chained with #not to return a new relation that negates the where clause.
665 666
    #
    #    User.where.not(name: "Jon")
667
    #    # SELECT * FROM users WHERE name != 'Jon'
668
    #
669
    # See WhereChain for more details on #not.
670
    #
671
    # === blank condition
672
    #
673
    # If the condition is any blank-ish object, then #where is a no-op and returns
674
    # the current relation.
675
    def where(opts = :chain, *rest)
676
      if :chain == opts
677 678 679 680 681 682
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
683 684
    end

685
    def where!(opts, *rest) # :nodoc:
686 687
      opts = sanitize_forbidden_attributes(opts)
      references!(PredicateBuilder.references(opts)) if Hash === opts
688
      self.where_clause += where_clause_factory.build(opts, rest)
689
      self
690
    end
P
Pratik Naik 已提交
691

692 693
    # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
    #
694 695 696 697 698 699 700 701
    #   Post.where(trashed: true).where(trashed: false)
    #   # WHERE `trashed` = 1 AND `trashed` = 0
    #
    #   Post.where(trashed: true).rewhere(trashed: false)
    #   # WHERE `trashed` = 0
    #
    #   Post.where(active: true).where(trashed: true).rewhere(trashed: false)
    #   # WHERE `active` = 1 AND `trashed` = 0
702
    #
703 704
    # This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
    # Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
705 706 707 708
    def rewhere(conditions)
      unscope(where: conditions.keys).where(conditions)
    end

709 710 711 712
    # Returns a new relation, which is the logical union of this relation and the one passed as an
    # argument.
    #
    # The two relations must be structurally compatible: they must be scoping the same model, and
713 714
    # they must differ only by #where (if no #group has been defined) or #having (if a #group is
    # present). Neither relation may have a #limit, #offset, or #distinct set.
715
    #
716
    #    Post.where("id = 1").or(Post.where("author_id = 3"))
R
Ryuta Kamizono 已提交
717
    #    # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
718 719
    #
    def or(other)
720 721 722 723
      unless other.is_a? Relation
        raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
      end

724 725 726
      spawn.or!(other)
    end

727
    def or!(other) # :nodoc:
728 729 730 731
      incompatible_values = structurally_incompatible_values_for_or(other)

      unless incompatible_values.empty?
        raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
732 733
      end

734
      self.where_clause = self.where_clause.or(other.where_clause)
735
      self.having_clause = having_clause.or(other.having_clause)
736
      self.references_values += other.references_values
737 738 739 740

      self
    end

741 742 743 744
    # Allows to specify a HAVING clause. Note that you can't use HAVING
    # without also specifying a GROUP clause.
    #
    #   Order.having('SUM(price) > 30').group('user_id')
745
    def having(opts, *rest)
746
      opts.blank? ? self : spawn.having!(opts, *rest)
747 748
    end

J
Jon Leighton 已提交
749
    def having!(opts, *rest) # :nodoc:
750
      opts = sanitize_forbidden_attributes(opts)
751
      references!(PredicateBuilder.references(opts)) if Hash === opts
752

753
      self.having_clause += having_clause_factory.build(opts, rest)
754
      self
755 756
    end

757
    # Specifies a limit for the number of records to retrieve.
758 759 760 761
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
762
    def limit(value)
J
Jon Leighton 已提交
763
      spawn.limit!(value)
764 765
    end

J
Jon Leighton 已提交
766
    def limit!(value) # :nodoc:
767 768
      self.limit_value = value
      self
769 770
    end

771 772 773 774
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
775
    # Should be used with order.
776
    #
777
    #   User.offset(10).order("name ASC")
778
    def offset(value)
J
Jon Leighton 已提交
779
      spawn.offset!(value)
780 781
    end

J
Jon Leighton 已提交
782
    def offset!(value) # :nodoc:
783 784
      self.offset_value = value
      self
785 786
    end

787
    # Specifies locking settings (default to +true+). For more information
788
    # on locking, please see ActiveRecord::Locking.
789
    def lock(locks = true)
J
Jon Leighton 已提交
790
      spawn.lock!(locks)
791
    end
792

J
Jon Leighton 已提交
793
    def lock!(locks = true) # :nodoc:
794
      case locks
795
      when String, TrueClass, NilClass
796
        self.lock_value = locks || true
797
      else
798
        self.lock_value = false
799
      end
800

801
      self
802 803
    end

804
    # Returns a chainable relation with zero records.
805
    #
806 807 808
    # The returned relation implements the Null Object pattern. It is an
    # object with defined null behavior and always returns an empty array of
    # records without querying the database.
809 810 811 812
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
813 814
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
815 816 817
    #
    # For example:
    #
A
AvnerCohen 已提交
818
    #   @posts = current_user.visible_posts.where(name: params[:name])
819
    #   # the visible_posts method is expected to return a chainable Relation
820 821 822
    #
    #   def visible_posts
    #     case role
823
    #     when 'Country Manager'
A
AvnerCohen 已提交
824
    #       Post.where(country: country)
825
    #     when 'Reviewer'
826
    #       Post.published
827
    #     when 'Bad User'
828
    #       Post.none # It can't be chained if [] is returned.
829 830 831 832
    #     end
    #   end
    #
    def none
833
      spawn.none!
834 835
    end

J
Jon Leighton 已提交
836
    def none! # :nodoc:
837
      where!("1=0").extending!(NullRelation)
838 839
    end

840 841 842 843 844
    # Sets readonly attributes for the returned relation. If value is
    # true (default), attempting to update a record will result in an error.
    #
    #   users = User.readonly
    #   users.first.save
845
    #   => ActiveRecord::ReadOnlyRecord: User is marked as readonly
846
    def readonly(value = true)
J
Jon Leighton 已提交
847
      spawn.readonly!(value)
848 849
    end

J
Jon Leighton 已提交
850
    def readonly!(value = true) # :nodoc:
851 852
      self.readonly_value = value
      self
853 854
    end

855 856 857 858 859 860 861 862 863
    # Sets attributes to be used when creating new records from a
    # relation object.
    #
    #   users = User.where(name: 'Oscar')
    #   users.new.name # => 'Oscar'
    #
    #   users = users.create_with(name: 'DHH')
    #   users.new.name # => 'DHH'
    #
864
    # You can pass +nil+ to #create_with to reset attributes:
865 866 867
    #
    #   users = users.create_with(nil)
    #   users.new.name # => 'Oscar'
868
    def create_with(value)
J
Jon Leighton 已提交
869
      spawn.create_with!(value)
870 871
    end

J
Jon Leighton 已提交
872
    def create_with!(value) # :nodoc:
873 874 875 876
      if value
        value = sanitize_forbidden_attributes(value)
        self.create_with_value = create_with_value.merge(value)
      else
877
        self.create_with_value = FROZEN_EMPTY_HASH
878 879
      end

880
      self
881 882
    end

883 884 885
    # Specifies table from which the records will be fetched. For example:
    #
    #   Topic.select('title').from('posts')
886
    #   # SELECT title FROM posts
887 888 889
    #
    # Can accept other relation objects. For example:
    #
890
    #   Topic.select('title').from(Topic.approved)
891
    #   # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
892
    #
893
    #   Topic.select('a.title').from(Topic.approved, :a)
894
    #   # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
895 896 897
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
898 899
    end

J
Jon Leighton 已提交
900
    def from!(value, subquery_name = nil) # :nodoc:
901
      self.from_clause = Relation::FromClause.new(value, subquery_name)
902
      self
903 904
    end

905 906 907
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
908
    #   # Might return two records with the same name
909
    #
910
    #   User.select(:name).distinct
911
    #   # Returns 1 record per distinct name
912
    #
913
    #   User.select(:name).distinct.distinct(false)
914
    #   # You can also remove the uniqueness
915 916
    def distinct(value = true)
      spawn.distinct!(value)
917 918
    end

919 920 921
    # Like #distinct, but modifies relation in place.
    def distinct!(value = true) # :nodoc:
      self.distinct_value = value
922
      self
923 924
    end

925
    # Used to extend a scope with additional methods, either through
926 927
    # a module or through a block provided.
    #
928 929 930 931 932 933 934 935 936 937
    # The object returned is a relation, which can be further extended.
    #
    # === Using a module
    #
    #   module Pagination
    #     def page(number)
    #       # pagination code goes here
    #     end
    #   end
    #
938
    #   scope = Model.all.extending(Pagination)
939 940
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
941
    # You can also pass a list of modules:
942
    #
943
    #   scope = Model.all.extending(Pagination, SomethingElse)
944 945 946
    #
    # === Using a block
    #
947
    #   scope = Model.all.extending do
948
    #     def page(number)
949
    #       # pagination code goes here
950 951 952 953 954 955
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
956
    #   scope = Model.all.extending(Pagination) do
957
    #     def per_page(number)
958
    #       # pagination code goes here
959 960
    #     end
    #   end
961 962
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
963
        spawn.extending!(*modules, &block)
964 965 966 967
      else
        self
      end
    end
968

J
Jon Leighton 已提交
969
    def extending!(*modules, &block) # :nodoc:
970 971
      modules << Module.new(&block) if block
      modules.flatten!
972

973
      self.extending_values += modules
974
      extend(*extending_values) if extending_values.any?
975

976
      self
977 978
    end

R
Ryuta Kamizono 已提交
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
    # Specify optimizer hints to be used in the SELECT statement.
    #
    # Example (for MySQL):
    #
    #   Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
    #   # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
    #
    # Example (for PostgreSQL with pg_hint_plan):
    #
    #   Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
    #   # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
    def optimizer_hints(*args)
      check_if_method_has_arguments!(:optimizer_hints, args)
      spawn.optimizer_hints!(*args)
    end

    def optimizer_hints!(*args) # :nodoc:
      args.flatten!

998
      self.optimizer_hints_values |= args
R
Ryuta Kamizono 已提交
999 1000 1001
      self
    end

1002 1003 1004
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
1005
    def reverse_order
J
Jon Leighton 已提交
1006
      spawn.reverse_order!
1007 1008
    end

J
Jon Leighton 已提交
1009
    def reverse_order! # :nodoc:
1010
      orders = order_values.uniq
1011
      orders.compact_blank!
1012
      self.order_values = reverse_sql_order(orders)
1013
      self
1014 1015
    end

1016 1017
    def skip_query_cache!(value = true) # :nodoc:
      self.skip_query_cache_value = value
1018 1019 1020
      self
    end

1021 1022 1023 1024 1025
    def skip_preloading! # :nodoc:
      self.skip_preloading_value = true
      self
    end

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
    # Adds an SQL comment to queries generated from this relation. For example:
    #
    #   User.annotate("selecting user names").select(:name)
    #   # SELECT "users"."name" FROM "users" /* selecting user names */
    #
    #   User.annotate("selecting", "user", "names").select(:name)
    #   # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
    #
    # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
    def annotate(*args)
      check_if_method_has_arguments!(:annotate, args)
      spawn.annotate!(*args)
    end

    # Like #annotate, but modifies relation in place.
    def annotate!(*args) # :nodoc:
      self.annotate_values += args
      self
    end

1046
    # Returns the Arel object associated with the relation.
1047 1048
    def arel(aliases = nil) # :nodoc:
      @arel ||= build_arel(aliases)
1049 1050
    end

1051
    def construct_join_dependency(associations, join_type) # :nodoc:
1052
      ActiveRecord::Associations::JoinDependency.new(
1053
        klass, table, associations, join_type
1054 1055 1056
      )
    end

1057 1058 1059 1060 1061 1062 1063 1064 1065
    protected
      def build_subquery(subquery_alias, select_value) # :nodoc:
        subquery = except(:optimizer_hints).arel.as(subquery_alias)

        Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
          arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
        end
      end

1066
    private
1067 1068 1069 1070
      def assert_mutability!
        raise ImmutableRelation if @loaded
        raise ImmutableRelation if defined?(@arel) && @arel
      end
1071

1072
      def build_arel(aliases)
1073
        arel = Arel::SelectManager.new(table)
1074

1075 1076 1077 1078 1079
        if !joins_values.empty?
          build_joins(arel, joins_values.flatten, aliases)
        elsif !left_outer_joins_values.empty?
          build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
        end
1080

1081 1082
        arel.where(where_clause.ast) unless where_clause.empty?
        arel.having(having_clause.ast) unless having_clause.empty?
1083
        if limit_value
1084
          limit_attribute = ActiveModel::Attribute.with_cast_value(
1085
            "LIMIT",
1086 1087 1088 1089 1090 1091
            connection.sanitize_limit(limit_value),
            Type.default_value,
          )
          arel.take(Arel::Nodes::BindParam.new(limit_attribute))
        end
        if offset_value
1092
          offset_attribute = ActiveModel::Attribute.with_cast_value(
1093
            "OFFSET",
1094 1095 1096 1097 1098
            offset_value.to_i,
            Type.default_value,
          )
          arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
        end
1099
        arel.group(*arel_columns(group_values.uniq.compact_blank)) unless group_values.empty?
1100

1101
        build_order(arel)
1102

1103
        build_select(arel)
1104

R
Ryuta Kamizono 已提交
1105
        arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1106 1107 1108
        arel.distinct(distinct_value)
        arel.from(build_from) unless from_clause.empty?
        arel.lock(lock_value) if lock_value
1109
        arel.comment(*annotate_values) unless annotate_values.empty?
1110

1111
        arel
1112 1113
      end

1114 1115 1116 1117 1118
      def build_from
        opts = from_clause.value
        name = from_clause.name
        case opts
        when Relation
1119 1120 1121
          if opts.eager_loading?
            opts = opts.send(:apply_join_dependency)
          end
1122 1123
          name ||= "subquery"
          opts.arel.as(name.to_s)
1124
        else
1125
          opts
1126 1127 1128
        end
      end

1129 1130
      def select_association_list(associations)
        result = []
1131 1132
        associations.each do |association|
          case association
1133
          when Hash, Symbol, Array
1134
            result << association
1135
          else
1136
            yield if block_given?
1137
          end
A
Aaron Patterson 已提交
1138
        end
1139 1140 1141 1142 1143 1144 1145
        result
      end

      def valid_association_list(associations)
        select_association_list(associations) do
          raise ArgumentError, "only Hash, Symbol and Array are allowed"
        end
1146
      end
1147

1148
      def build_left_outer_joins(manager, outer_joins, aliases)
1149 1150
        buckets = Hash.new { |h, k| h[k] = [] }
        buckets[:association_join] = valid_association_list(outer_joins)
1151
        build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
1152
      end
1153

1154
      def build_joins(manager, joins, aliases)
1155 1156
        buckets = Hash.new { |h, k| h[k] = [] }

1157 1158
        unless left_outer_joins_values.empty?
          left_joins = valid_association_list(left_outer_joins_values.flatten)
1159 1160 1161
          buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
        end

1162 1163 1164 1165
        if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
          buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
        end

1166 1167 1168 1169 1170 1171
        joins.map! do |join|
          if join.is_a?(String)
            table.create_string_join(Arel.sql(join.strip)) unless join.blank?
          else
            join
          end
R
Ryuta Kamizono 已提交
1172
        end.compact_blank!.uniq!
1173 1174 1175 1176 1177 1178 1179 1180

        while joins.first.is_a?(Arel::Nodes::Join)
          join_node = joins.shift
          if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
            buckets[:join_node] << join_node
          else
            buckets[:leading_join] << join_node
          end
1181 1182
        end

1183
        joins.each do |join|
1184 1185
          case join
          when Hash, Symbol, Array
1186
            buckets[:association_join] << join
1187
          when ActiveRecord::Associations::JoinDependency
1188
            buckets[:stashed_join] << join
1189
          when Arel::Nodes::Join
1190
            buckets[:join_node] << join
1191 1192 1193 1194
          else
            raise "unknown class: %s" % join.class.name
          end
        end
1195

1196
        build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
1197
      end
1198

1199
      def build_join_query(manager, buckets, join_type, aliases)
1200 1201
        association_joins = buckets[:association_join]
        stashed_joins     = buckets[:stashed_join]
R
Ryuta Kamizono 已提交
1202 1203
        leading_joins     = buckets[:leading_join]
        join_nodes        = buckets[:join_node]
1204

1205
        join_sources = manager.join_sources
1206
        join_sources.concat(leading_joins) unless leading_joins.empty?
1207

1208
        unless association_joins.empty? && stashed_joins.empty?
1209
          alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
1210 1211 1212
          join_dependency = construct_join_dependency(association_joins, join_type)
          join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
        end
1213

1214
        join_sources.concat(join_nodes) unless join_nodes.empty?
1215 1216
      end

1217 1218 1219
      def build_select(arel)
        if select_values.any?
          arel.project(*arel_columns(select_values.uniq))
1220 1221
        elsif klass.ignored_columns.any?
          arel.project(*klass.column_names.map { |field| arel_attribute(field) })
1222
        else
R
Ryuta Kamizono 已提交
1223
          arel.project(table[Arel.star])
1224 1225 1226
        end
      end

1227
      def arel_columns(columns)
1228
        columns.flat_map do |field|
1229 1230
          case field
          when Symbol
1231 1232 1233
            arel_column(field.to_s) do |attr_name|
              connection.quote_table_name(attr_name)
            end
1234
          when String
1235
            arel_column(field, &:itself)
1236
          when Proc
1237
            field.call
1238 1239 1240 1241
          else
            field
          end
        end
1242
      end
B
Brian Mathiyakom 已提交
1243

1244
      def arel_column(field)
1245
        field = klass.attribute_aliases[field] || field
1246
        from = from_clause.name || from_clause.value
1247

1248
        if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1249 1250
          arel_attribute(field)
        else
1251
          yield field
1252 1253 1254
        end
      end

1255 1256 1257 1258
      def table_name_matches?(from)
        /(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
      end

1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
      def reverse_sql_order(order_query)
        if order_query.empty?
          return [arel_attribute(primary_key).desc] if primary_key
          raise IrreversibleOrderError,
            "Relation has no current order and table has no primary key to be used as default order"
        end

        order_query.flat_map do |o|
          case o
          when Arel::Attribute
            o.desc
          when Arel::Nodes::Ordering
            o.reverse
          when String
            if does_not_support_reverse?(o)
1274
              raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
1275 1276 1277
            end
            o.split(",").map! do |s|
              s.strip!
1278
              s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
1279 1280 1281
            end
          else
            o
1282
          end
1283
        end
1284
      end
1285

1286
      def does_not_support_reverse?(order)
1287 1288 1289 1290
        # Account for String subclasses like Arel::Nodes::SqlLiteral that
        # override methods like #count.
        order = String.new(order) unless order.instance_of?(String)

1291
        # Uses SQL function with multiple arguments.
R
Rafael Mendonça França 已提交
1292 1293
        (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
          # Uses "nulls first" like construction.
1294
          /\bnulls\s+(?:first|last)\b/i.match?(order)
1295
      end
1296

1297 1298
      def build_order(arel)
        orders = order_values.uniq
1299
        orders.compact_blank!
1300

1301 1302
        arel.order(*orders) unless orders.empty?
      end
1303

1304
      VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1305
                          "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
1306

1307 1308 1309 1310
      def validate_order_args(args)
        args.each do |arg|
          next unless arg.is_a?(Hash)
          arg.each do |_key, value|
1311 1312 1313 1314
            unless VALID_DIRECTIONS.include?(value)
              raise ArgumentError,
                "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
            end
1315
          end
1316 1317
        end
      end
1318

1319
      def preprocess_order_args(order_args)
1320
        order_args.reject!(&:blank?)
1321
        order_args.map! do |arg|
1322
          klass.sanitize_sql_for_order(arg)
1323
        end
1324
        order_args.flatten!
1325

1326
        @klass.disallow_raw_sql!(
1327
          order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1328
          permit: connection.column_name_with_order_matcher
1329 1330
        )

1331 1332 1333
        validate_order_args(order_args)

        references = order_args.grep(String)
1334
        references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1335 1336 1337 1338 1339 1340
        references!(references) if references.any?

        # if a symbol is given we prepend the quoted table name
        order_args.map! do |arg|
          case arg
          when Symbol
1341
            order_column(arg.to_s).asc
1342 1343
          when Hash
            arg.map { |field, dir|
1344 1345 1346 1347
              case field
              when Arel::Nodes::SqlLiteral
                field.send(dir.downcase)
              else
1348
                order_column(field.to_s).send(dir.downcase)
1349
              end
1350 1351 1352 1353 1354 1355
            }
          else
            arg
          end
        end.flatten!
      end
1356

1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
      def order_column(field)
        arel_column(field) do |attr_name|
          if attr_name == "count" && !group_values.empty?
            arel_attribute(attr_name)
          else
            Arel.sql(connection.quote_table_name(attr_name))
          end
        end
      end

1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382
      # Checks to make sure that the arguments are not blank. Note that if some
      # blank-like object were initially passed into the query method, then this
      # method will not raise an error.
      #
      # Example:
      #
      #    Post.references()   # raises an error
      #    Post.references([]) # does not raise an error
      #
      # This particular method should be called with a method_name and the args
      # passed into that method as an input. For example:
      #
      # def references(*args)
      #   check_if_method_has_arguments!("references", args)
      #   ...
      # end
1383 1384 1385 1386
      def check_if_method_has_arguments!(method_name, args)
        if args.blank?
          raise ArgumentError, "The method .#{method_name}() must contain arguments."
        end
1387
      end
S
Sean Griffin 已提交
1388

1389
      STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
1390
      def structurally_incompatible_values_for_or(other)
1391
        values = other.values
1392
        STRUCTURAL_OR_METHODS.reject do |method|
1393 1394
          v1, v2 = @values[method], values[method]
          v1 == v2 || (!v1 || v1.empty?) && (!v2 || v2.empty?)
1395
        end
1396
      end
1397

1398 1399 1400 1401
      def where_clause_factory
        @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
      end
      alias having_clause_factory where_clause_factory
1402 1403
  end
end