query_methods.rb 40.5 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
      #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
44 45 46
      #
      #    User.where.not(name: "Jon", role: "admin")
      #    # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
47
      def not(opts, *rest)
48 49
        opts = sanitize_forbidden_attributes(opts)

50
        where_clause = @scope.send(:where_clause_factory).build(opts, rest)
51 52

        @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
53
        @scope.where_clause += where_clause.invert
54 55 56 57
        @scope
      end
    end

58
    FROZEN_EMPTY_ARRAY = [].freeze
59
    FROZEN_EMPTY_HASH = {}.freeze
60

61
    Relation::VALUE_METHODS.each do |name|
62 63 64 65 66 67
      method_name = \
        case name
        when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
        when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
        when *Relation::CLAUSE_METHODS then "#{name}_clause"
        end
68
      class_eval <<-CODE, __FILE__, __LINE__ + 1
69 70
        def #{method_name}                   # def includes_values
          get_value(#{name.inspect})         #   get_value(:includes)
71
        end                                  # end
72

73 74
        def #{method_name}=(value)           # def includes_values=(value)
          set_value(#{name.inspect}, value)  #   set_value(:includes, value)
75 76
        end                                  # end
      CODE
77 78
    end

79
    alias extensions extending_values
80

O
Oscar Del Ben 已提交
81 82 83 84 85 86 87 88 89 90
    # 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
91
    # performance improvement over a simple join.
92
    #
93 94 95 96 97 98 99 100
    # 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])
    #
101 102 103 104 105 106 107 108 109 110
    # === conditions
    #
    # If you want to add conditions to your included models you'll have
    # 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)
111
    #
112
    # Note that #includes works with association names while #references needs
113
    # the actual table name.
114
    def includes(*args)
115
      check_if_method_has_arguments!(:includes, args)
116
      spawn.includes!(*args)
117
    end
118

J
Jon Leighton 已提交
119
    def includes!(*args) # :nodoc:
120 121
      args.reject!(&:blank?)
      args.flatten!
A
Aaron Patterson 已提交
122

123
      self.includes_values |= args
124
      self
125
    end
126

127 128 129
    # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
    #
    #   User.eager_load(:posts)
130 131 132
    #   # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
    #   # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
    #   # "users"."id"
133
    def eager_load(*args)
134
      check_if_method_has_arguments!(:eager_load, args)
135
      spawn.eager_load!(*args)
136
    end
137

J
Jon Leighton 已提交
138
    def eager_load!(*args) # :nodoc:
139 140
      self.eager_load_values += args
      self
141 142
    end

143
    # Allows preloading of +args+, in the same way that #includes does:
144 145
    #
    #   User.preload(:posts)
146
    #   # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
147
    def preload(*args)
148
      check_if_method_has_arguments!(:preload, args)
149
      spawn.preload!(*args)
150
    end
151

J
Jon Leighton 已提交
152
    def preload!(*args) # :nodoc:
153 154
      self.preload_values += args
      self
155
    end
156

157 158
    # 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.
159
    # This method only works in conjunction with #includes.
160
    # See #includes for more details.
161 162
    #
    #   User.includes(:posts).where("posts.name = 'foo'")
163
    #   # Doesn't JOIN the posts table, resulting in an error.
164 165
    #
    #   User.includes(:posts).where("posts.name = 'foo'").references(:posts)
166
    #   # Query now knows the string references posts, so adds a JOIN
167 168 169
    def references(*table_names)
      check_if_method_has_arguments!(:references, table_names)
      spawn.references!(*table_names)
170
    end
171

172 173 174
    def references!(*table_names) # :nodoc:
      table_names.flatten!
      table_names.map!(&:to_s)
175

176
      self.references_values |= table_names
177
      self
178 179
    end

180
    # Works in two unique ways.
181
    #
182
    # First: takes a block so it can be used just like <tt>Array#select</tt>.
183
    #
184
    #   Model.all.select { |m| m.field == value }
185 186
    #
    # This will build an array of objects from the database for the scope,
187 188
    # converting them into an array and iterating through them using
    # <tt>Array#select</tt>.
189 190
    #
    # Second: Modifies the SELECT statement for the query so that only certain
V
Vijay Dev 已提交
191
    # fields are retrieved:
192
    #
193
    #   Model.select(:field)
194
    #   # => [#<Model id: nil, field: "value">]
195 196
    #
    # Although in the above example it looks as though this method returns an
V
Vijay Dev 已提交
197
    # array, it actually returns a relation object and can have other query
198 199
    # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
    #
200
    # The argument to the method can also be an array of fields.
201
    #
202
    #   Model.select(:field, :other_field, :and_one_more)
203
    #   # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
204
    #
205 206 207
    # 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')
208
    #   # => [#<Model id: nil, field: "value", other_field: "value">]
209 210 211 212 213 214
    #
    # If an alias was specified, it will be accessible from the resulting objects:
    #
    #   Model.select('field AS field_one').first.field_one
    #   # => "value"
    #
215
    # Accessing attributes of an object that do not have fields retrieved by a select
216
    # except +id+ will throw ActiveModel::MissingAttributeError:
217
    #
218 219 220
    #   Model.select(:field).first.other_field
    #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
    def select(*fields)
221 222
      if block_given?
        if fields.any?
223
          raise ArgumentError, "`select' with block doesn't take arguments."
224 225 226 227 228
        end

        return super()
      end

229
      raise ArgumentError, "Call `select' with at least one field" if fields.empty?
230
      spawn._select!(*fields)
231 232
    end

233
    def _select!(*fields) # :nodoc:
234
      fields.reject!(&:blank?)
235
      fields.flatten!
236
      fields.map! do |field|
237
        klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
238
      end
239
      self.select_values += fields
240
      self
241
    end
S
Santiago Pastorino 已提交
242

O
Oscar Del Ben 已提交
243 244 245
    # Allows to specify a group attribute:
    #
    #   User.group(:name)
246
    #   # SELECT "users".* FROM "users" GROUP BY name
O
Oscar Del Ben 已提交
247
    #
248
    # Returns an array with distinct records based on the +group+ attribute:
O
Oscar Del Ben 已提交
249 250
    #
    #   User.select([:id, :name])
251
    #   # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
O
Oscar Del Ben 已提交
252 253
    #
    #   User.group(:name)
254
    #   # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
255 256
    #
    #   User.group('name AS grouped_name, age')
257
    #   # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
258 259
    #
    # Passing in an array of attributes to group by is also supported.
260
    #
261
    #   User.select([:id, :first_name]).group(:id, :first_name).first(3)
262
    #   # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
263
    def group(*args)
264
      check_if_method_has_arguments!(:group, args)
265
      spawn.group!(*args)
266
    end
267

J
Jon Leighton 已提交
268
    def group!(*args) # :nodoc:
269 270 271
      args.flatten!

      self.group_values += args
272
      self
273
    end
274

O
Oscar Del Ben 已提交
275 276
    # Allows to specify an order attribute:
    #
277
    #   User.order(:name)
278
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
279
    #
280
    #   User.order(email: :desc)
281
    #   # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
282
    #
283
    #   User.order(:name, email: :desc)
284
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
285 286
    #
    #   User.order('name')
287
    #   # SELECT "users".* FROM "users" ORDER BY name
288 289
    #
    #   User.order('name DESC')
290
    #   # SELECT "users".* FROM "users" ORDER BY name DESC
291 292
    #
    #   User.order('name DESC, email')
293
    #   # SELECT "users".* FROM "users" ORDER BY name DESC, email
294
    def order(*args)
295
      check_if_method_has_arguments!(:order, args)
296
      spawn.order!(*args)
297
    end
298

299
    # Same as #order but operates on relation in-place instead of copying.
J
Jon Leighton 已提交
300
    def order!(*args) # :nodoc:
301
      preprocess_order_args(args)
302

303
      self.order_values += args
304
      self
305
    end
306

307 308 309 310 311 312 313 314
    # 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')
    #
315
    # generates a query with 'ORDER BY id ASC, name ASC'.
S
Sebastian Martinez 已提交
316
    def reorder(*args)
317
      check_if_method_has_arguments!(:reorder, args)
318
      spawn.reorder!(*args)
319
    end
320

321
    # Same as #reorder but operates on relation in-place instead of copying.
J
Jon Leighton 已提交
322
    def reorder!(*args) # :nodoc:
323
      preprocess_order_args(args)
324

325
      self.reordering_value = true
326
      self.order_values = args
327
      self
S
Sebastian Martinez 已提交
328 329
    end

330
    VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
331 332
                                     :limit, :offset, :joins, :left_outer_joins,
                                     :includes, :from, :readonly, :having])
333 334 335 336 337

    # 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.
    #
338
    #   User.order('email DESC').unscope(:order) == User.all
339 340 341 342 343
    #
    # 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:
    #
344
    #   User.order('email DESC').select('id').where(name: "John")
345 346
    #       .unscope(:order, :select, :where) == User.all
    #
347
    # One can additionally pass a hash as an argument to unscope specific +:where+ values.
348
    # This is done by passing a hash with a single key-value pair. The key should be
349
    # +:where+ and the value should be the where value to unscope. For example:
350
    #
351 352
    #   User.where(name: "John", active: true).unscope(where: :name)
    #       == User.where(active: true)
353
    #
354 355
    # This method is similar to #except, but unlike
    # #except, it persists across merges:
356
    #
J
Jon Leighton 已提交
357 358
    #   User.order('email').merge(User.except(:order))
    #       == User.order('email')
359
    #
J
Jon Leighton 已提交
360 361 362 363 364
    #   User.order('email').merge(User.unscope(:order))
    #       == User.all
    #
    # This means it can be used in association definitions:
    #
R
Ryuta Kamizono 已提交
365
    #   has_many :comments, -> { unscope(where: :trashed) }
366 367
    #
    def unscope(*args)
368
      check_if_method_has_arguments!(:unscope, args)
369 370 371
      spawn.unscope!(*args)
    end

372
    def unscope!(*args) # :nodoc:
373
      args.flatten!
J
Jon Leighton 已提交
374
      self.unscope_values += args
375 376 377 378

      args.each do |scope|
        case scope
        when Symbol
379
          scope = :left_outer_joins if scope == :left_joins
380 381 382
          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
383
          set_value(scope, DEFAULT_VALUES[scope])
384 385 386 387 388 389
        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

390 391
            target_values = Array(target_value).map(&:to_s)
            self.where_clause = where_clause.except(*target_values)
392 393 394 395 396 397 398 399 400
          end
        else
          raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
        end
      end

      self
    end

401 402
    # Performs a joins on +args+. The given symbol(s) should match the name of
    # the association(s).
403 404
    #
    #   User.joins(:posts)
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    #   # 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"
425 426 427 428
    #
    # 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")
429
    #   # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
430
    def joins(*args)
431 432
      check_if_method_has_arguments!(:joins, args)
      spawn.joins!(*args)
433
    end
434

J
Jon Leighton 已提交
435
    def joins!(*args) # :nodoc:
436 437
      args.compact!
      args.flatten!
438 439
      self.joins_values += args
      self
P
Pratik Naik 已提交
440 441
    end

442 443 444 445 446 447
    # 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)
448
      check_if_method_has_arguments!(__callee__, args)
449 450
      spawn.left_outer_joins!(*args)
    end
451
    alias :left_joins :left_outer_joins
452 453

    def left_outer_joins!(*args) # :nodoc:
454 455
      args.compact!
      args.flatten!
456 457 458 459
      self.left_outer_joins_values += args
      self
    end

460 461 462 463 464 465 466 467 468 469
    # 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
470
    # constructor as an SQL fragment, and used in the where clause of the query.
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    #
    #    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,
503
    # the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
    #
    #   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')
    #
535 536 537 538 539 540
    # 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 已提交
541 542
    #    Post.where(author: author)
    #    Post.where(author_id: author)
543 544 545
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
546 547
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
548 549
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
550 551
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
552
    #
553 554 555 556 557 558 559 560 561 562
    # === 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 已提交
563
    #    User.joins(:posts).where({ posts: { published: true } })
564
    #
565
    # === no argument
566
    #
567 568
    # 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.
569 570
    #
    #    User.where.not(name: "Jon")
571
    #    # SELECT * FROM users WHERE name != 'Jon'
572
    #
573
    # See WhereChain for more details on #not.
574
    #
575
    # === blank condition
576
    #
577
    # If the condition is any blank-ish object, then #where is a no-op and returns
578
    # the current relation.
579
    def where(opts = :chain, *rest)
580
      if :chain == opts
581 582 583 584 585 586
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
587 588
    end

589
    def where!(opts, *rest) # :nodoc:
590 591
      opts = sanitize_forbidden_attributes(opts)
      references!(PredicateBuilder.references(opts)) if Hash === opts
592
      self.where_clause += where_clause_factory.build(opts, rest)
593
      self
594
    end
P
Pratik Naik 已提交
595

596 597
    # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
    #
598 599 600 601 602 603 604 605
    #   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
606
    #
607 608
    # 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.
609 610 611 612
    def rewhere(conditions)
      unscope(where: conditions.keys).where(conditions)
    end

613 614 615 616
    # 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
617 618
    # 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.
619
    #
620
    #    Post.where("id = 1").or(Post.where("author_id = 3"))
R
Ryuta Kamizono 已提交
621
    #    # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
622 623
    #
    def or(other)
624 625 626 627
      unless other.is_a? Relation
        raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
      end

628 629 630
      spawn.or!(other)
    end

631
    def or!(other) # :nodoc:
632 633 634 635
      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}"
636 637
      end

638
      self.where_clause = self.where_clause.or(other.where_clause)
639
      self.having_clause = having_clause.or(other.having_clause)
640
      self.references_values += other.references_values
641 642 643 644

      self
    end

645 646 647 648
    # 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')
649
    def having(opts, *rest)
650
      opts.blank? ? self : spawn.having!(opts, *rest)
651 652
    end

J
Jon Leighton 已提交
653
    def having!(opts, *rest) # :nodoc:
654
      opts = sanitize_forbidden_attributes(opts)
655
      references!(PredicateBuilder.references(opts)) if Hash === opts
656

657
      self.having_clause += having_clause_factory.build(opts, rest)
658
      self
659 660
    end

661
    # Specifies a limit for the number of records to retrieve.
662 663 664 665
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
666
    def limit(value)
J
Jon Leighton 已提交
667
      spawn.limit!(value)
668 669
    end

J
Jon Leighton 已提交
670
    def limit!(value) # :nodoc:
671 672
      self.limit_value = value
      self
673 674
    end

675 676 677 678
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
679
    # Should be used with order.
680
    #
681
    #   User.offset(10).order("name ASC")
682
    def offset(value)
J
Jon Leighton 已提交
683
      spawn.offset!(value)
684 685
    end

J
Jon Leighton 已提交
686
    def offset!(value) # :nodoc:
687 688
      self.offset_value = value
      self
689 690
    end

691
    # Specifies locking settings (default to +true+). For more information
692
    # on locking, please see ActiveRecord::Locking.
693
    def lock(locks = true)
J
Jon Leighton 已提交
694
      spawn.lock!(locks)
695
    end
696

J
Jon Leighton 已提交
697
    def lock!(locks = true) # :nodoc:
698
      case locks
699
      when String, TrueClass, NilClass
700
        self.lock_value = locks || true
701
      else
702
        self.lock_value = false
703
      end
704

705
      self
706 707
    end

708
    # Returns a chainable relation with zero records.
709
    #
710 711 712
    # 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.
713 714 715 716
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
717 718
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
719 720 721
    #
    # For example:
    #
A
AvnerCohen 已提交
722
    #   @posts = current_user.visible_posts.where(name: params[:name])
723
    #   # the visible_posts method is expected to return a chainable Relation
724 725 726
    #
    #   def visible_posts
    #     case role
727
    #     when 'Country Manager'
A
AvnerCohen 已提交
728
    #       Post.where(country: country)
729
    #     when 'Reviewer'
730
    #       Post.published
731
    #     when 'Bad User'
732
    #       Post.none # It can't be chained if [] is returned.
733 734 735 736
    #     end
    #   end
    #
    def none
737
      spawn.none!
738 739
    end

J
Jon Leighton 已提交
740
    def none! # :nodoc:
741
      where!("1=0").extending!(NullRelation)
742 743
    end

744 745 746 747 748
    # 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
749
    #   => ActiveRecord::ReadOnlyRecord: User is marked as readonly
750
    def readonly(value = true)
J
Jon Leighton 已提交
751
      spawn.readonly!(value)
752 753
    end

J
Jon Leighton 已提交
754
    def readonly!(value = true) # :nodoc:
755 756
      self.readonly_value = value
      self
757 758
    end

759 760 761 762 763 764 765 766 767
    # 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'
    #
768
    # You can pass +nil+ to #create_with to reset attributes:
769 770 771
    #
    #   users = users.create_with(nil)
    #   users.new.name # => 'Oscar'
772
    def create_with(value)
J
Jon Leighton 已提交
773
      spawn.create_with!(value)
774 775
    end

J
Jon Leighton 已提交
776
    def create_with!(value) # :nodoc:
777 778 779 780
      if value
        value = sanitize_forbidden_attributes(value)
        self.create_with_value = create_with_value.merge(value)
      else
781
        self.create_with_value = FROZEN_EMPTY_HASH
782 783
      end

784
      self
785 786
    end

787 788 789
    # Specifies table from which the records will be fetched. For example:
    #
    #   Topic.select('title').from('posts')
790
    #   # SELECT title FROM posts
791 792 793
    #
    # Can accept other relation objects. For example:
    #
794
    #   Topic.select('title').from(Topic.approved)
795
    #   # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
796
    #
797
    #   Topic.select('a.title').from(Topic.approved, :a)
798
    #   # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
799 800 801
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
802 803
    end

J
Jon Leighton 已提交
804
    def from!(value, subquery_name = nil) # :nodoc:
805
      self.from_clause = Relation::FromClause.new(value, subquery_name)
806
      self
807 808
    end

809 810 811
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
812
    #   # Might return two records with the same name
813
    #
814
    #   User.select(:name).distinct
815
    #   # Returns 1 record per distinct name
816
    #
817
    #   User.select(:name).distinct.distinct(false)
818
    #   # You can also remove the uniqueness
819 820
    def distinct(value = true)
      spawn.distinct!(value)
821 822
    end

823 824 825
    # Like #distinct, but modifies relation in place.
    def distinct!(value = true) # :nodoc:
      self.distinct_value = value
826
      self
827 828
    end

829
    # Used to extend a scope with additional methods, either through
830 831
    # a module or through a block provided.
    #
832 833 834 835 836 837 838 839 840 841
    # 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
    #
842
    #   scope = Model.all.extending(Pagination)
843 844
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
845
    # You can also pass a list of modules:
846
    #
847
    #   scope = Model.all.extending(Pagination, SomethingElse)
848 849 850
    #
    # === Using a block
    #
851
    #   scope = Model.all.extending do
852
    #     def page(number)
853
    #       # pagination code goes here
854 855 856 857 858 859
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
860
    #   scope = Model.all.extending(Pagination) do
861
    #     def per_page(number)
862
    #       # pagination code goes here
863 864
    #     end
    #   end
865 866
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
867
        spawn.extending!(*modules, &block)
868 869 870 871
      else
        self
      end
    end
872

J
Jon Leighton 已提交
873
    def extending!(*modules, &block) # :nodoc:
874 875
      modules << Module.new(&block) if block
      modules.flatten!
876

877
      self.extending_values += modules
878
      extend(*extending_values) if extending_values.any?
879

880
      self
881 882
    end

883 884 885
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
886
    def reverse_order
J
Jon Leighton 已提交
887
      spawn.reverse_order!
888 889
    end

J
Jon Leighton 已提交
890
    def reverse_order! # :nodoc:
891 892 893
      orders = order_values.uniq
      orders.reject!(&:blank?)
      self.order_values = reverse_sql_order(orders)
894
      self
895 896
    end

897 898
    def skip_query_cache!(value = true) # :nodoc:
      self.skip_query_cache_value = value
899 900 901
      self
    end

902 903 904 905 906
    def skip_preloading! # :nodoc:
      self.skip_preloading_value = true
      self
    end

907
    # Returns the Arel object associated with the relation.
908 909
    def arel(aliases = nil) # :nodoc:
      @arel ||= build_arel(aliases)
910 911
    end

912 913 914 915 916
    private
      # Returns a relation value with a given name
      def get_value(name)
        @values.fetch(name, DEFAULT_VALUES[name])
      end
917

918
      # Sets the relation value with the given name
919
      def set_value(name, value)
920 921 922
        assert_mutability!
        @values[name] = value
      end
923

924 925 926 927
      def assert_mutability!
        raise ImmutableRelation if @loaded
        raise ImmutableRelation if defined?(@arel) && @arel
      end
928

929
      def build_arel(aliases)
930
        arel = Arel::SelectManager.new(table)
931

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?
934

935 936
        arel.where(where_clause.ast) unless where_clause.empty?
        arel.having(having_clause.ast) unless having_clause.empty?
937
        if limit_value
938
          limit_attribute = ActiveModel::Attribute.with_cast_value(
939
            "LIMIT",
940 941 942 943 944 945
            connection.sanitize_limit(limit_value),
            Type.default_value,
          )
          arel.take(Arel::Nodes::BindParam.new(limit_attribute))
        end
        if offset_value
946
          offset_attribute = ActiveModel::Attribute.with_cast_value(
947
            "OFFSET",
948 949 950 951 952
            offset_value.to_i,
            Type.default_value,
          )
          arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
        end
953
        arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
954

955
        build_order(arel)
956

957
        build_select(arel)
958

959 960 961
        arel.distinct(distinct_value)
        arel.from(build_from) unless from_clause.empty?
        arel.lock(lock_value) if lock_value
962

963
        arel
964 965
      end

966 967 968 969 970
      def build_from
        opts = from_clause.value
        name = from_clause.name
        case opts
        when Relation
971 972 973
          if opts.eager_loading?
            opts = opts.send(:apply_join_dependency)
          end
974 975
          name ||= "subquery"
          opts.arel.as(name.to_s)
976
        else
977
          opts
978 979 980
        end
      end

981
      def build_left_outer_joins(manager, outer_joins, aliases)
982 983 984 985
        buckets = outer_joins.group_by do |join|
          case join
          when Hash, Symbol, Array
            :association_join
986 987
          when ActiveRecord::Associations::JoinDependency
            :stashed_join
988 989 990
          else
            raise ArgumentError, "only Hash, Symbol and Array are allowed"
          end
A
Aaron Patterson 已提交
991
        end
992

993
        build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
994
      end
995

996
      def build_joins(manager, joins, aliases)
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
        buckets = joins.group_by do |join|
          case join
          when String
            :string_join
          when Hash, Symbol, Array
            :association_join
          when ActiveRecord::Associations::JoinDependency
            :stashed_join
          when Arel::Nodes::Join
            :join_node
          else
            raise "unknown class: %s" % join.class.name
          end
        end
1011

1012
        build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
1013
      end
1014

1015
      def build_join_query(manager, buckets, join_type, aliases)
1016
        buckets.default = []
1017

1018 1019 1020 1021
        association_joins = buckets[:association_join]
        stashed_joins     = buckets[:stashed_join]
        join_nodes        = buckets[:join_node].uniq
        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)
1027

1028
        joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
1029
        joins.each { |join| manager.from(join) }
1030

1031
        manager.join_sources.concat(join_list)
1032

1033
        alias_tracker.aliases
1034
      end
1035

1036
      def convert_join_strings_to_ast(joins)
1037 1038 1039 1040
        joins
          .flatten
          .reject(&:blank?)
          .map { |join| table.create_string_join(Arel.sql(join)) }
1041 1042
      end

1043 1044 1045
      def build_select(arel)
        if select_values.any?
          arel.project(*arel_columns(select_values.uniq))
1046 1047
        elsif klass.ignored_columns.any?
          arel.project(*klass.column_names.map { |field| arel_attribute(field) })
1048
        else
R
Ryuta Kamizono 已提交
1049
          arel.project(table[Arel.star])
1050 1051 1052
        end
      end

1053
      def arel_columns(columns)
1054
        columns.flat_map do |field|
1055 1056 1057 1058 1059 1060 1061
          case field
          when Symbol
            field = field.to_s
            arel_column(field) { connection.quote_table_name(field) }
          when String
            arel_column(field) { field }
          when Proc
1062
            field.call
1063 1064 1065 1066
          else
            field
          end
        end
1067
      end
B
Brian Mathiyakom 已提交
1068

1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
      def arel_column(field)
        field = klass.attribute_alias(field) if klass.attribute_alias?(field)

        if klass.columns_hash.key?(field) && !from_clause.value
          arel_attribute(field)
        else
          yield
        end
      end

1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
      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)
              raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
            end
            o.split(",").map! do |s|
              s.strip!
1098
              s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
1099 1100 1101
            end
          else
            o
1102
          end
1103
        end
1104
      end
1105

1106
      def does_not_support_reverse?(order)
1107 1108 1109 1110
        # Account for String subclasses like Arel::Nodes::SqlLiteral that
        # override methods like #count.
        order = String.new(order) unless order.instance_of?(String)

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

1117 1118 1119
      def build_order(arel)
        orders = order_values.uniq
        orders.reject!(&:blank?)
1120

1121 1122
        arel.order(*orders) unless orders.empty?
      end
1123

1124
      VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1125
                          "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
1126

1127 1128 1129 1130
      def validate_order_args(args)
        args.each do |arg|
          next unless arg.is_a?(Hash)
          arg.each do |_key, value|
1131 1132 1133 1134
            unless VALID_DIRECTIONS.include?(value)
              raise ArgumentError,
                "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
            end
1135
          end
1136 1137
        end
      end
1138

1139 1140
      def preprocess_order_args(order_args)
        order_args.map! do |arg|
1141
          klass.sanitize_sql_for_order(arg)
1142
        end
1143
        order_args.flatten!
1144

1145
        @klass.disallow_raw_sql!(
1146
          order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1147
          permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
1148 1149
        )

1150 1151 1152
        validate_order_args(order_args)

        references = order_args.grep(String)
1153
        references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1154 1155 1156 1157 1158 1159
        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
1160 1161 1162 1163
            field = arg.to_s
            arel_column(field) {
              Arel.sql(connection.quote_table_name(field))
            }.asc
1164 1165
          when Hash
            arg.map { |field, dir|
1166 1167 1168 1169
              case field
              when Arel::Nodes::SqlLiteral
                field.send(dir.downcase)
              else
1170 1171 1172 1173
                field = field.to_s
                arel_column(field) {
                  Arel.sql(connection.quote_table_name(field))
                }.send(dir.downcase)
1174
              end
1175 1176 1177 1178 1179 1180
            }
          else
            arg
          end
        end.flatten!
      end
1181

1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
      # 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
1198 1199 1200 1201
      def check_if_method_has_arguments!(method_name, args)
        if args.blank?
          raise ArgumentError, "The method .#{method_name}() must contain arguments."
        end
1202
      end
S
Sean Griffin 已提交
1203

1204
      STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
1205
      def structurally_incompatible_values_for_or(other)
1206
        values = other.values
1207
        STRUCTURAL_OR_METHODS.reject do |method|
1208
          get_value(method) == values.fetch(method, DEFAULT_VALUES[method])
1209
        end
1210
      end
1211

1212 1213 1214 1215
      def where_clause_factory
        @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
      end
      alias having_clause_factory where_clause_factory
1216

1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
      DEFAULT_VALUES = {
        create_with: FROZEN_EMPTY_HASH,
        where: Relation::WhereClause.empty,
        having: Relation::WhereClause.empty,
        from: Relation::FromClause.empty
      }

      Relation::MULTI_VALUE_METHODS.each do |value|
        DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
      end
1227 1228
  end
end