query_methods.rb 39.8 KB
Newer Older
1
require "active_record/relation/from_clause"
S
Sean Griffin 已提交
2
require "active_record/relation/query_attribute"
S
Sean Griffin 已提交
3
require "active_record/relation/where_clause"
4
require "active_record/relation/where_clause_factory"
5
require 'active_model/forbidden_attributes_protection'
6

7 8
module ActiveRecord
  module QueryMethods
9 10
    extend ActiveSupport::Concern

11 12
    include ActiveModel::ForbiddenAttributesProtection

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

18 19 20 21
      def initialize(scope)
        @scope = scope
      end

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

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

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

56 57
    Relation::MULTI_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
58 59 60 61 62 63 64 65
        def #{name}_values                    # def select_values
          @values[:#{name}] || []             #   @values[:select] || []
        end                                   # end
                                              #
        def #{name}_values=(values)           # def select_values=(values)
          assert_mutability!                  #   assert_mutability!
          @values[:#{name}] = values          #   @values[:select] = values
        end                                   # end
66 67 68 69 70
      CODE
    end

    (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
71 72 73
        def #{name}_value                    # def readonly_value
          @values[:#{name}]                  #   @values[:readonly]
        end                                  # end
74 75 76
      CODE
    end

77 78 79
    Relation::SINGLE_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}_value=(value)            # def readonly_value=(value)
80
          assert_mutability!                 #   assert_mutability!
81 82 83
          @values[:#{name}] = value          #   @values[:readonly] = value
        end                                  # end
      CODE
84 85
    end

S
Sean Griffin 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98
    Relation::CLAUSE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}_clause                           # def where_clause
          @values[:#{name}] || new_#{name}_clause    #   @values[:where] || new_where_clause
        end                                          # end
                                                     #
        def #{name}_clause=(value)                   # def where_clause=(value)
          assert_mutability!                         #   assert_mutability!
          @values[:#{name}] = value                  #   @values[:where] = value
        end                                          # end
      CODE
    end

99
    def bound_attributes
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
      result = from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds
      if limit_value && !string_containing_comma?(limit_value)
        result << Attribute.with_cast_value(
          "LIMIT".freeze,
          connection.sanitize_limit(limit_value),
          Type::Value.new,
        )
      end
      if offset_value
        result << Attribute.with_cast_value(
          "OFFSET".freeze,
          offset_value.to_i,
          Type::Value.new,
        )
      end
      result
S
Sean Griffin 已提交
116 117
    end

O
Oscar Del Ben 已提交
118
    def create_with_value # :nodoc:
119
      @values[:create_with] || {}
120
    end
121 122

    alias extensions extending_values
123

O
Oscar Del Ben 已提交
124 125 126 127 128 129 130 131 132 133
    # 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
134
    # performance improvement over a simple join.
135
    #
136 137 138 139 140 141 142 143
    # 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])
    #
144 145 146 147 148 149 150 151 152 153
    # === 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)
154
    #
155
    # Note that #includes works with association names while #references needs
156
    # the actual table name.
157
    def includes(*args)
158
      check_if_method_has_arguments!(:includes, args)
159
      spawn.includes!(*args)
160
    end
161

J
Jon Leighton 已提交
162
    def includes!(*args) # :nodoc:
163 164
      args.reject!(&:blank?)
      args.flatten!
A
Aaron Patterson 已提交
165

166
      self.includes_values |= args
167
      self
168
    end
169

170 171 172
    # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
    #
    #   User.eager_load(:posts)
173 174 175
    #   # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
    #   # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
    #   # "users"."id"
176
    def eager_load(*args)
177
      check_if_method_has_arguments!(:eager_load, args)
178
      spawn.eager_load!(*args)
179
    end
180

J
Jon Leighton 已提交
181
    def eager_load!(*args) # :nodoc:
182 183
      self.eager_load_values += args
      self
184 185
    end

186
    # Allows preloading of +args+, in the same way that #includes does:
187 188
    #
    #   User.preload(:posts)
189
    #   # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
190
    def preload(*args)
191
      check_if_method_has_arguments!(:preload, args)
192
      spawn.preload!(*args)
193
    end
194

J
Jon Leighton 已提交
195
    def preload!(*args) # :nodoc:
196 197
      self.preload_values += args
      self
198
    end
199

200 201
    # 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.
202
    # This method only works in conjunction with #includes.
203
    # See #includes for more details.
204 205
    #
    #   User.includes(:posts).where("posts.name = 'foo'")
206
    #   # Doesn't JOIN the posts table, resulting in an error.
207 208
    #
    #   User.includes(:posts).where("posts.name = 'foo'").references(:posts)
209
    #   # Query now knows the string references posts, so adds a JOIN
210 211 212
    def references(*table_names)
      check_if_method_has_arguments!(:references, table_names)
      spawn.references!(*table_names)
213
    end
214

215 216 217
    def references!(*table_names) # :nodoc:
      table_names.flatten!
      table_names.map!(&:to_s)
218

219
      self.references_values |= table_names
220
      self
221 222
    end

223
    # Works in two unique ways.
224
    #
225
    # First: takes a block so it can be used just like +Array#select+.
226
    #
227
    #   Model.all.select { |m| m.field == value }
228 229
    #
    # This will build an array of objects from the database for the scope,
230
    # converting them into an array and iterating through them using +Array#select+.
231 232
    #
    # Second: Modifies the SELECT statement for the query so that only certain
V
Vijay Dev 已提交
233
    # fields are retrieved:
234
    #
235
    #   Model.select(:field)
236
    #   # => [#<Model id: nil, field: "value">]
237 238
    #
    # Although in the above example it looks as though this method returns an
V
Vijay Dev 已提交
239
    # array, it actually returns a relation object and can have other query
240 241
    # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
    #
242
    # The argument to the method can also be an array of fields.
243
    #
244
    #   Model.select(:field, :other_field, :and_one_more)
245
    #   # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
246
    #
247 248 249
    # 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')
250
    #   # => [#<Model id: nil, field: "value", other_field: "value">]
251 252 253 254 255 256
    #
    # If an alias was specified, it will be accessible from the resulting objects:
    #
    #   Model.select('field AS field_one').first.field_one
    #   # => "value"
    #
257
    # Accessing attributes of an object that do not have fields retrieved by a select
258
    # except +id+ will throw ActiveModel::MissingAttributeError:
259
    #
260 261 262
    #   Model.select(:field).first.other_field
    #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
    def select(*fields)
263 264 265
      return super if block_given?
      raise ArgumentError, 'Call this with at least one field' if fields.empty?
      spawn._select!(*fields)
266 267
    end

268
    def _select!(*fields) # :nodoc:
269
      fields.flatten!
270
      fields.map! do |field|
271
        klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
272
      end
273
      self.select_values += fields
274
      self
275
    end
S
Santiago Pastorino 已提交
276

O
Oscar Del Ben 已提交
277 278 279
    # Allows to specify a group attribute:
    #
    #   User.group(:name)
280
    #   # SELECT "users".* FROM "users" GROUP BY name
O
Oscar Del Ben 已提交
281
    #
282
    # Returns an array with distinct records based on the +group+ attribute:
O
Oscar Del Ben 已提交
283 284
    #
    #   User.select([:id, :name])
285
    #   # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
O
Oscar Del Ben 已提交
286 287
    #
    #   User.group(:name)
288
    #   # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
289 290
    #
    #   User.group('name AS grouped_name, age')
291
    #   # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
292 293
    #
    # Passing in an array of attributes to group by is also supported.
294
    #
295
    #   User.select([:id, :first_name]).group(:id, :first_name).first(3)
296
    #   # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
297
    def group(*args)
298
      check_if_method_has_arguments!(:group, args)
299
      spawn.group!(*args)
300
    end
301

J
Jon Leighton 已提交
302
    def group!(*args) # :nodoc:
303 304 305
      args.flatten!

      self.group_values += args
306
      self
307
    end
308

O
Oscar Del Ben 已提交
309 310
    # Allows to specify an order attribute:
    #
311
    #   User.order(:name)
312
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
313
    #
314
    #   User.order(email: :desc)
315
    #   # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
316
    #
317
    #   User.order(:name, email: :desc)
318
    #   # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
319 320
    #
    #   User.order('name')
321
    #   # SELECT "users".* FROM "users" ORDER BY name
322 323
    #
    #   User.order('name DESC')
324
    #   # SELECT "users".* FROM "users" ORDER BY name DESC
325 326
    #
    #   User.order('name DESC, email')
327
    #   # SELECT "users".* FROM "users" ORDER BY name DESC, email
328
    def order(*args)
329
      check_if_method_has_arguments!(:order, args)
330
      spawn.order!(*args)
331
    end
332

J
Jon Leighton 已提交
333
    def order!(*args) # :nodoc:
334
      preprocess_order_args(args)
335

336
      self.order_values += args
337
      self
338
    end
339

340 341 342 343 344 345 346 347
    # 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')
    #
348
    # generates a query with 'ORDER BY id ASC, name ASC'.
S
Sebastian Martinez 已提交
349
    def reorder(*args)
350
      check_if_method_has_arguments!(:reorder, args)
351
      spawn.reorder!(*args)
352
    end
353

J
Jon Leighton 已提交
354
    def reorder!(*args) # :nodoc:
355
      preprocess_order_args(args)
356

357
      self.reordering_value = true
358
      self.order_values = args
359
      self
S
Sebastian Martinez 已提交
360 361
    end

362 363 364 365 366 367 368 369
    VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
                                     :limit, :offset, :joins, :includes, :from,
                                     :readonly, :having])

    # 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.
    #
370
    #   User.order('email DESC').unscope(:order) == User.all
371 372 373 374 375
    #
    # 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:
    #
376
    #   User.order('email DESC').select('id').where(name: "John")
377 378
    #       .unscope(:order, :select, :where) == User.all
    #
379
    # One can additionally pass a hash as an argument to unscope specific +:where+ values.
380
    # This is done by passing a hash with a single key-value pair. The key should be
381
    # +:where+ and the value should be the where value to unscope. For example:
382
    #
383 384
    #   User.where(name: "John", active: true).unscope(where: :name)
    #       == User.where(active: true)
385
    #
386 387
    # This method is similar to #except, but unlike
    # #except, it persists across merges:
388
    #
J
Jon Leighton 已提交
389 390
    #   User.order('email').merge(User.except(:order))
    #       == User.order('email')
391
    #
J
Jon Leighton 已提交
392 393 394 395 396
    #   User.order('email').merge(User.unscope(:order))
    #       == User.all
    #
    # This means it can be used in association definitions:
    #
R
Ryuta Kamizono 已提交
397
    #   has_many :comments, -> { unscope(where: :trashed) }
398 399
    #
    def unscope(*args)
400
      check_if_method_has_arguments!(:unscope, args)
401 402 403
      spawn.unscope!(*args)
    end

404
    def unscope!(*args) # :nodoc:
405
      args.flatten!
J
Jon Leighton 已提交
406
      self.unscope_values += args
407 408 409 410 411 412 413 414 415 416 417

      args.each do |scope|
        case scope
        when Symbol
          symbol_unscoping(scope)
        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

418 419
            target_values = Array(target_value).map(&:to_s)
            self.where_clause = where_clause.except(*target_values)
420 421 422 423 424 425 426 427 428
          end
        else
          raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
        end
      end

      self
    end

429 430
    # Performs a joins on +args+. The given symbol(s) should match the name of
    # the association(s).
431 432
    #
    #   User.joins(:posts)
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    #   # 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"
453 454 455 456
    #
    # 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")
457
    #   # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
458
    def joins(*args)
459 460
      check_if_method_has_arguments!(:joins, args)
      spawn.joins!(*args)
461
    end
462

J
Jon Leighton 已提交
463
    def joins!(*args) # :nodoc:
464 465
      args.compact!
      args.flatten!
466 467
      self.joins_values += args
      self
P
Pratik Naik 已提交
468 469
    end

470 471 472 473 474 475 476 477 478 479 480 481 482
    # 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)
      check_if_method_has_arguments!(:left_outer_joins, args)

      args.compact!
      args.flatten!

      spawn.left_outer_joins!(*args)
    end
483
    alias :left_joins :left_outer_joins
484 485 486 487 488

    def left_outer_joins!(*args) # :nodoc:
      self.left_outer_joins_values += args
      self
    end
489
    alias :left_joins! :left_outer_joins!
490

491 492 493 494 495 496 497 498 499 500
    # 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
501
    # constructor as an SQL fragment, and used in the where clause of the query.
502 503 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
    #
    #    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,
534
    # the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
    #
    #   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')
    #
566 567 568 569 570 571
    # 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 已提交
572 573
    #    Post.where(author: author)
    #    Post.where(author_id: author)
574 575 576
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
577 578
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
579 580
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
581 582
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
583
    #
584 585 586 587 588 589 590 591 592 593
    # === 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 已提交
594
    #    User.joins(:posts).where({ posts: { published: true } })
595
    #
596
    # === no argument
597
    #
598 599
    # 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.
600 601
    #
    #    User.where.not(name: "Jon")
602
    #    # SELECT * FROM users WHERE name != 'Jon'
603
    #
604
    # See WhereChain for more details on #not.
605
    #
606
    # === blank condition
607
    #
608
    # If the condition is any blank-ish object, then #where is a no-op and returns
609
    # the current relation.
610
    def where(opts = :chain, *rest)
611
      if :chain == opts
612 613 614 615 616 617
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
618 619
    end

620
    def where!(opts, *rest) # :nodoc:
621 622
      opts = sanitize_forbidden_attributes(opts)
      references!(PredicateBuilder.references(opts)) if Hash === opts
623
      self.where_clause += where_clause_factory.build(opts, rest)
624
      self
625
    end
P
Pratik Naik 已提交
626

627 628
    # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
    #
629 630 631 632 633 634 635 636
    #   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
637
    #
638 639
    # 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.
640 641 642 643
    def rewhere(conditions)
      unscope(where: conditions.keys).where(conditions)
    end

644 645 646 647
    # 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
648 649
    # 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.
650 651 652 653 654 655 656 657
    #
    #    Post.where("id = 1").or(Post.where("id = 2"))
    #    # SELECT `posts`.* FROM `posts`  WHERE (('id = 1' OR 'id = 2'))
    #
    def or(other)
      spawn.or!(other)
    end

658 659
    def or!(other) # :nodoc:
      unless structurally_compatible_for_or?(other)
660 661 662
        raise ArgumentError, 'Relation passed to #or must be structurally compatible'
      end

663 664
      self.where_clause = self.where_clause.or(other.where_clause)
      self.having_clause = self.having_clause.or(other.having_clause)
665 666 667 668

      self
    end

669 670 671 672
    # 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')
673
    def having(opts, *rest)
674
      opts.blank? ? self : spawn.having!(opts, *rest)
675 676
    end

J
Jon Leighton 已提交
677
    def having!(opts, *rest) # :nodoc:
678
      opts = sanitize_forbidden_attributes(opts)
679
      references!(PredicateBuilder.references(opts)) if Hash === opts
680

681
      self.having_clause += having_clause_factory.build(opts, rest)
682
      self
683 684
    end

685
    # Specifies a limit for the number of records to retrieve.
686 687 688 689
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
690
    def limit(value)
J
Jon Leighton 已提交
691
      spawn.limit!(value)
692 693
    end

J
Jon Leighton 已提交
694
    def limit!(value) # :nodoc:
695 696
      if string_containing_comma?(value)
        # Remove `string_containing_comma?` when removing this deprecation
S
Sean Griffin 已提交
697 698 699 700 701
        ActiveSupport::Deprecation.warn(<<-WARNING)
          Passing a string to limit in the form "1,2" is deprecated and will be
          removed in Rails 5.1. Please call `offset` explicitly instead.
        WARNING
      end
702 703
      self.limit_value = value
      self
704 705
    end

706 707 708 709
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
710
    # Should be used with order.
711
    #
712
    #   User.offset(10).order("name ASC")
713
    def offset(value)
J
Jon Leighton 已提交
714
      spawn.offset!(value)
715 716
    end

J
Jon Leighton 已提交
717
    def offset!(value) # :nodoc:
718 719
      self.offset_value = value
      self
720 721
    end

722
    # Specifies locking settings (default to +true+). For more information
723
    # on locking, please see ActiveRecord::Locking.
724
    def lock(locks = true)
J
Jon Leighton 已提交
725
      spawn.lock!(locks)
726
    end
727

J
Jon Leighton 已提交
728
    def lock!(locks = true) # :nodoc:
729
      case locks
730
      when String, TrueClass, NilClass
731
        self.lock_value = locks || true
732
      else
733
        self.lock_value = false
734
      end
735

736
      self
737 738
    end

739
    # Returns a chainable relation with zero records.
740
    #
741 742 743
    # 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.
744 745 746 747
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
748 749
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
750 751 752
    #
    # For example:
    #
A
AvnerCohen 已提交
753
    #   @posts = current_user.visible_posts.where(name: params[:name])
754
    #   # the visible_posts method is expected to return a chainable Relation
755 756 757
    #
    #   def visible_posts
    #     case role
758
    #     when 'Country Manager'
A
AvnerCohen 已提交
759
    #       Post.where(country: country)
760
    #     when 'Reviewer'
761
    #       Post.published
762
    #     when 'Bad User'
763
    #       Post.none # It can't be chained if [] is returned.
764 765 766 767
    #     end
    #   end
    #
    def none
768
      where("1=0").extending!(NullRelation)
769 770
    end

J
Jon Leighton 已提交
771
    def none! # :nodoc:
772
      where!("1=0").extending!(NullRelation)
773 774
    end

775 776 777 778 779
    # 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
780
    #   => ActiveRecord::ReadOnlyRecord: User is marked as readonly
781
    def readonly(value = true)
J
Jon Leighton 已提交
782
      spawn.readonly!(value)
783 784
    end

J
Jon Leighton 已提交
785
    def readonly!(value = true) # :nodoc:
786 787
      self.readonly_value = value
      self
788 789
    end

790 791 792 793 794 795 796 797 798
    # 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'
    #
799
    # You can pass +nil+ to #create_with to reset attributes:
800 801 802
    #
    #   users = users.create_with(nil)
    #   users.new.name # => 'Oscar'
803
    def create_with(value)
J
Jon Leighton 已提交
804
      spawn.create_with!(value)
805 806
    end

J
Jon Leighton 已提交
807
    def create_with!(value) # :nodoc:
808 809 810 811 812 813 814
      if value
        value = sanitize_forbidden_attributes(value)
        self.create_with_value = create_with_value.merge(value)
      else
        self.create_with_value = {}
      end

815
      self
816 817
    end

818 819 820
    # Specifies table from which the records will be fetched. For example:
    #
    #   Topic.select('title').from('posts')
821
    #   # SELECT title FROM posts
822 823 824
    #
    # Can accept other relation objects. For example:
    #
825
    #   Topic.select('title').from(Topic.approved)
826
    #   # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
827
    #
828
    #   Topic.select('a.title').from(Topic.approved, :a)
829
    #   # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
830 831 832
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
833 834
    end

J
Jon Leighton 已提交
835
    def from!(value, subquery_name = nil) # :nodoc:
836
      self.from_clause = Relation::FromClause.new(value, subquery_name)
837
      self
838 839
    end

840 841 842
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
843
    #   # Might return two records with the same name
844
    #
845
    #   User.select(:name).distinct
846
    #   # Returns 1 record per distinct name
847
    #
848
    #   User.select(:name).distinct.distinct(false)
849
    #   # You can also remove the uniqueness
850 851
    def distinct(value = true)
      spawn.distinct!(value)
852
    end
853
    alias uniq distinct
854
    deprecate uniq: :distinct
855

856 857 858
    # Like #distinct, but modifies relation in place.
    def distinct!(value = true) # :nodoc:
      self.distinct_value = value
859
      self
860
    end
861
    alias uniq! distinct!
862
    deprecate uniq!: :distinct!
863

864
    # Used to extend a scope with additional methods, either through
865 866
    # a module or through a block provided.
    #
867 868 869 870 871 872 873 874 875 876
    # 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
    #
877
    #   scope = Model.all.extending(Pagination)
878 879
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
880
    # You can also pass a list of modules:
881
    #
882
    #   scope = Model.all.extending(Pagination, SomethingElse)
883 884 885
    #
    # === Using a block
    #
886
    #   scope = Model.all.extending do
887
    #     def page(number)
888
    #       # pagination code goes here
889 890 891 892 893 894
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
895
    #   scope = Model.all.extending(Pagination) do
896
    #     def per_page(number)
897
    #       # pagination code goes here
898 899
    #     end
    #   end
900 901
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
902
        spawn.extending!(*modules, &block)
903 904 905 906
      else
        self
      end
    end
907

J
Jon Leighton 已提交
908
    def extending!(*modules, &block) # :nodoc:
909 910
      modules << Module.new(&block) if block
      modules.flatten!
911

912
      self.extending_values += modules
913
      extend(*extending_values) if extending_values.any?
914

915
      self
916 917
    end

918 919 920
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
921
    def reverse_order
J
Jon Leighton 已提交
922
      spawn.reverse_order!
923 924
    end

J
Jon Leighton 已提交
925
    def reverse_order! # :nodoc:
926 927 928
      orders = order_values.uniq
      orders.reject!(&:blank?)
      self.order_values = reverse_sql_order(orders)
929
      self
930 931
    end

932
    # Returns the Arel object associated with the relation.
933
    def arel # :nodoc:
934
      @arel ||= build_arel
935 936
    end

937 938
    private

939 940 941 942 943
    def assert_mutability!
      raise ImmutableRelation if @loaded
      raise ImmutableRelation if defined?(@arel) && @arel
    end

944
    def build_arel
945
      arel = Arel::SelectManager.new(table)
946

947
      build_joins(arel, joins_values.flatten) unless joins_values.empty?
948
      build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
949

950
      arel.where(where_clause.ast) unless where_clause.empty?
951
      arel.having(having_clause.ast) unless having_clause.empty?
952 953 954 955 956 957 958 959
      if limit_value
        if string_containing_comma?(limit_value)
          arel.take(connection.sanitize_limit(limit_value))
        else
          arel.take(Arel::Nodes::BindParam.new)
        end
      end
      arel.skip(Arel::Nodes::BindParam.new) if offset_value
960
      arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
961

962
      build_order(arel)
963

964
      build_select(arel)
965

966
      arel.distinct(distinct_value)
967
      arel.from(build_from) unless from_clause.empty?
968
      arel.lock(lock_value) if lock_value
969

970 971 972
      arel
    end

973 974 975 976 977
    def symbol_unscoping(scope)
      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

978 979 980 981 982 983 984
      clause_method = Relation::CLAUSE_METHODS.include?(scope)
      multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope)
      if clause_method
        unscope_code = "#{scope}_clause="
      else
        unscope_code = "#{scope}_value#{'s' if multi_val_method}="
      end
985 986 987 988 989

      case scope
      when :order
        result = []
      else
990
        result = [] if multi_val_method
991 992 993 994 995
      end

      self.send(unscope_code, result)
    end

996 997 998 999 1000 1001
    def association_for_table(table_name)
      table_name = table_name.to_s
      @klass._reflect_on_association(table_name) ||
        @klass._reflect_on_association(table_name.singularize)
    end

1002
    def build_from
1003 1004
      opts = from_clause.value
      name = from_clause.name
1005 1006 1007 1008 1009 1010 1011 1012 1013
      case opts
      when Relation
        name ||= 'subquery'
        opts.arel.as(name.to_s)
      else
        opts
      end
    end

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    def build_left_outer_joins(manager, outer_joins)
      buckets = outer_joins.group_by do |join|
        case join
        when Hash, Symbol, Array
          :association_join
        else
          raise ArgumentError, 'only Hash, Symbol and Array are allowed'
        end
      end

      build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
    end

1027
    def build_joins(manager, joins)
A
Aaron Patterson 已提交
1028 1029 1030
      buckets = joins.group_by do |join|
        case join
        when String
1031
          :string_join
A
Aaron Patterson 已提交
1032
        when Hash, Symbol, Array
1033
          :association_join
1034
        when ActiveRecord::Associations::JoinDependency
1035
          :stashed_join
1036
        when Arel::Nodes::Join
1037
          :join_node
A
Aaron Patterson 已提交
1038 1039 1040
        else
          raise 'unknown class: %s' % join.class.name
        end
1041
      end
1042 1043 1044 1045 1046

      build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
    end

    def build_join_query(manager, buckets, join_type)
1047
      buckets.default = []
1048

1049 1050 1051 1052
      association_joins         = buckets[:association_join]
      stashed_association_joins = buckets[:stashed_join]
      join_nodes                = buckets[:join_node].uniq
      string_joins              = buckets[:string_join].map(&:strip).uniq
1053

1054
      join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
1055

1056
      join_dependency = ActiveRecord::Associations::JoinDependency.new(
1057 1058 1059 1060
        @klass,
        association_joins,
        join_list
      )
1061

1062
      join_infos = join_dependency.join_constraints stashed_association_joins, join_type
1063

1064 1065
      join_infos.each do |info|
        info.joins.each { |join| manager.from(join) }
1066
        manager.bind_values.concat info.binds
1067
      end
1068

1069
      manager.join_sources.concat(join_list)
1070 1071

      manager
1072 1073
    end

1074 1075 1076 1077 1078 1079 1080
    def convert_join_strings_to_ast(table, joins)
      joins
        .flatten
        .reject(&:blank?)
        .map { |join| table.create_string_join(Arel.sql(join)) }
    end

1081 1082 1083
    def build_select(arel)
      if select_values.any?
        arel.project(*arel_columns(select_values.uniq))
1084
      else
1085
        arel.project(@klass.arel_table[Arel.star])
1086 1087 1088
      end
    end

1089
    def arel_columns(columns)
1090 1091 1092 1093 1094 1095 1096
      columns.map do |field|
        if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value
          arel_table[field]
        elsif Symbol === field
          connection.quote_table_name(field.to_s)
        else
          field
1097 1098 1099 1100
        end
      end
    end

1101
    def reverse_sql_order(order_query)
B
Brian Mathiyakom 已提交
1102 1103
      order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?

1104
      order_query.flat_map do |o|
1105
        case o
1106
        when Arel::Nodes::Ordering
1107
          o.reverse
1108
        when String
1109
          o.to_s.split(',').map! do |s|
1110 1111 1112
            s.strip!
            s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
          end
1113 1114 1115
        else
          o
        end
1116
      end
1117 1118
    end

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

1123 1124
      arel.order(*orders) unless orders.empty?
    end
1125

1126 1127
    VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
                        'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1128

1129
    def validate_order_args(args)
Y
Yves Senn 已提交
1130 1131 1132
      args.each do |arg|
        next unless arg.is_a?(Hash)
        arg.each do |_key, value|
1133 1134
          raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
                               "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1135 1136 1137
        end
      end
    end
P
Pratik Naik 已提交
1138

1139
    def preprocess_order_args(order_args)
1140
      order_args.map! do |arg|
1141
        klass.send(:sanitize_sql_for_order, arg)
1142
      end
1143 1144 1145 1146 1147 1148 1149 1150 1151
      order_args.flatten!
      validate_order_args(order_args)

      references = order_args.grep(String)
      references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
      references!(references) if references.any?

      # if a symbol is given we prepend the quoted table name
      order_args.map! do |arg|
1152 1153
        case arg
        when Symbol
1154
          arg = klass.attribute_alias(arg) if klass.attribute_alias?(arg)
1155 1156 1157
          table[arg].asc
        when Hash
          arg.map { |field, dir|
1158
            field = klass.attribute_alias(field) if klass.attribute_alias?(field)
1159
            table[field].send(dir.downcase)
1160 1161 1162 1163 1164
          }
        else
          arg
        end
      end.flatten!
1165 1166
    end

1167 1168 1169 1170 1171 1172
    # 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:
    #
1173 1174
    #    Post.references()   # raises an error
    #    Post.references([]) # does not raise an error
1175 1176 1177 1178 1179
    #
    # 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)
1180
    #   check_if_method_has_arguments!("references", args)
1181 1182
    #   ...
    # end
1183
    def check_if_method_has_arguments!(method_name, args)
1184 1185 1186 1187
      if args.blank?
        raise ArgumentError, "The method .#{method_name}() must contain arguments."
      end
    end
S
Sean Griffin 已提交
1188

1189 1190 1191 1192 1193 1194
    def structurally_compatible_for_or?(other)
      Relation::SINGLE_VALUE_METHODS.all? { |m| send("#{m}_value") == other.send("#{m}_value") } &&
        (Relation::MULTI_VALUE_METHODS - [:extending]).all? { |m| send("#{m}_values") == other.send("#{m}_values") } &&
        (Relation::CLAUSE_METHODS - [:having, :where]).all? { |m| send("#{m}_clause") != other.send("#{m}_clause") }
    end

S
Sean Griffin 已提交
1195 1196 1197
    def new_where_clause
      Relation::WhereClause.empty
    end
1198
    alias new_having_clause new_where_clause
1199 1200 1201 1202

    def where_clause_factory
      @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
    end
1203
    alias having_clause_factory where_clause_factory
1204 1205 1206 1207

    def new_from_clause
      Relation::FromClause.empty
    end
1208 1209 1210 1211

    def string_containing_comma?(value)
      ::String === value && value.include?(",")
    end
1212 1213
  end
end