query_methods.rb 33.6 KB
Newer Older
1
require 'active_support/core_ext/array/wrap'
2

3 4
module ActiveRecord
  module QueryMethods
5 6
    extend ActiveSupport::Concern

7
    # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
8
    # In this case, #where must be chained with #not to return a new relation.
9 10 11 12 13
    class WhereChain
      def initialize(scope)
        @scope = scope
      end

14 15
      # Returns a new relation expressing WHERE + NOT condition according to
      # the conditions in the arguments.
16
      #
17 18
      # +not+ accepts conditions as a string, array, or hash. See #where for
      # more details on each format.
19 20
      #
      #    User.where.not("name = 'Jon'")
21
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
22 23
      #
      #    User.where.not(["name = ?", "Jon"])
24
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
25
      #
26
      #    User.where.not(name: "Jon")
27
      #    # SELECT * FROM users WHERE name != 'Jon'
28 29 30 31
      #
      #    User.where.not(name: nil)
      #    # SELECT * FROM users WHERE name IS NOT NULL
      #
32
      #    User.where.not(name: %w(Ko1 Nobu))
33
      #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
34 35 36 37
      #
      #    User.where.not(name: "Jon", role: "admin")
      #    # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
      #
38 39 40 41 42
      def not(opts, *rest)
        where_value = @scope.send(:build_where, opts, rest).map do |rel|
          case rel
          when Arel::Nodes::In
            Arel::Nodes::NotIn.new(rel.left, rel.right)
43 44
          when Arel::Nodes::Equality
            Arel::Nodes::NotEqual.new(rel.left, rel.right)
45 46 47 48 49 50 51 52 53 54 55
          when String
            Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
          else
            Arel::Nodes::Not.new(rel)
          end
        end
        @scope.where_values += where_value
        @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)
          raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
          @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 80 81 82 83
    Relation::SINGLE_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}_value=(value)            # def readonly_value=(value)
          raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
          @values[:#{name}] = value          #   @values[:readonly] = value
        end                                  # end
      CODE
84 85
    end

O
Oscar Del Ben 已提交
86
    def create_with_value # :nodoc:
87
      @values[:create_with] || {}
88
    end
89 90

    alias extensions extending_values
91

O
Oscar Del Ben 已提交
92 93 94 95 96 97 98 99 100 101
    # 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
102 103 104 105 106 107 108 109 110 111 112 113
    # performance improvement over a simple +join+.
    #
    # === 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)
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
      args.reject! {|a| a.blank? }
A
Aaron Patterson 已提交
121

122 123
      self.includes_values = (includes_values + args).flatten.uniq
      self
124
    end
125

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

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

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

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

156 157 158 159 160 161 162 163 164
    # Used to indicate that an association is referenced by an SQL string, and should
    # therefore be JOINed in any query rather than loaded separately.
    #
    #   User.includes(:posts).where("posts.name = 'foo'")
    #   # => Doesn't JOIN the posts table, resulting in an error.
    #
    #   User.includes(:posts).where("posts.name = 'foo'").references(:posts)
    #   # => Query now knows the string references posts, so adds a JOIN
    def references(*args)
165
      check_if_method_has_arguments!("references", args)
166
      spawn.references!(*args)
167
    end
168

J
Jon Leighton 已提交
169
    def references!(*args) # :nodoc:
170 171 172
      args.flatten!

      self.references_values = (references_values + args.map!(&:to_s)).uniq
173
      self
174 175
    end

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

J
Jon Leighton 已提交
224
    def select!(*fields) # :nodoc:
225
      self.select_values += fields.flatten
226
      self
227
    end
S
Santiago Pastorino 已提交
228

O
Oscar Del Ben 已提交
229 230 231 232 233
    # Allows to specify a group attribute:
    #
    #   User.group(:name)
    #   => SELECT "users".* FROM "users" GROUP BY name
    #
234
    # Returns an array with distinct records based on the +group+ attribute:
O
Oscar Del Ben 已提交
235 236 237 238 239 240
    #
    #   User.select([:id, :name])
    #   => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
    #
    #   User.group(:name)
    #   => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
241 242 243
    #
    #   User.group('name AS grouped_name, age')
    #   => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
244
    def group(*args)
245
      check_if_method_has_arguments!("group", args)
246
      spawn.group!(*args)
247
    end
248

J
Jon Leighton 已提交
249
    def group!(*args) # :nodoc:
250 251 252
      args.flatten!

      self.group_values += args
253
      self
254
    end
255

O
Oscar Del Ben 已提交
256 257 258 259 260 261 262 263 264 265
    # Allows to specify an order attribute:
    #
    #   User.order('name')
    #   => SELECT "users".* FROM "users" ORDER BY name
    #
    #   User.order('name DESC')
    #   => SELECT "users".* FROM "users" ORDER BY name DESC
    #
    #   User.order('name DESC, email')
    #   => SELECT "users".* FROM "users" ORDER BY name DESC, email
266
    #
267 268
    #   User.order(:name)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
269
    #
270 271
    #   User.order(email: :desc)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
272
    #
273 274
    #   User.order(:name, email: :desc)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
275
    def order(*args)
276
      check_if_method_has_arguments!("order", args)
277
      spawn.order!(*args)
278
    end
279

J
Jon Leighton 已提交
280
    def order!(*args) # :nodoc:
281
      args.flatten!
282
      validate_order_args args
283

284
      references = args.reject { |arg| Arel::Node === arg }
285
      references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
286
      references!(references) if references.any?
287

288 289 290 291 292
      # if a symbol is given we prepend the quoted table name
      args = args.map { |arg|
        arg.is_a?(Symbol) ? "#{quoted_table_name}.#{arg} ASC" : arg
      }

293
      self.order_values = args + self.order_values
294
      self
295
    end
296

297 298 299 300 301 302 303 304
    # 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')
    #
305
    # generates a query with 'ORDER BY name ASC, id ASC'.
S
Sebastian Martinez 已提交
306
    def reorder(*args)
307
      check_if_method_has_arguments!("reorder", args)
308
      spawn.reorder!(*args)
309
    end
310

J
Jon Leighton 已提交
311
    def reorder!(*args) # :nodoc:
312
      args.flatten!
313
      validate_order_args args
314

315
      self.reordering_value = true
316
      self.order_values = args
317
      self
S
Sebastian Martinez 已提交
318 319
    end

320 321 322 323 324 325 326 327
    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.
    #
328
    #   User.order('email DESC').unscope(:order) == User.all
329 330 331 332 333
    #
    # 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:
    #
334
    #   User.order('email DESC').select('id').where(name: "John")
335 336 337 338 339 340
    #       .unscope(:order, :select, :where) == User.all
    #
    # One can additionally pass a hash as an argument to unscope specific :where values.
    # This is done by passing a hash with a single key-value pair. The key should be
    # :where and the value should be the where value to unscope. For example:
    #
341 342
    #   User.where(name: "John", active: true).unscope(where: :name)
    #       == User.where(active: true)
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    #
    # Note that this method is more generalized than ActiveRecord::SpawnMethods#except
    # because #except will only affect a particular relation's values. It won't wipe
    # the order, grouping, etc. when that relation is merged. For example:
    #
    #   Post.comments.except(:order)
    #
    # will still have an order if it comes from the default_scope on Comment.
    def unscope(*args)
      check_if_method_has_arguments!("unscope", args)
      spawn.unscope!(*args)
    end

    def unscope!(*args)
      args.flatten!

      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

            Array(target_value).each do |val|
              where_unscoping(val)
            end
          end
        else
          raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
        end
      end

      self
    end

381 382 383 384
    # Performs a joins on +args+:
    #
    #   User.joins(:posts)
    #   => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
385 386 387 388 389
    #
    # 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")
    #   => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
390
    def joins(*args)
391
      check_if_method_has_arguments!("joins", args)
392
      spawn.joins!(*args.compact.flatten)
393
    end
394

J
Jon Leighton 已提交
395
    def joins!(*args) # :nodoc:
396 397
      self.joins_values += args
      self
P
Pratik Naik 已提交
398 399
    end

A
Aaron Patterson 已提交
400
    def bind(value)
J
Jon Leighton 已提交
401
      spawn.bind!(value)
402 403
    end

J
Jon Leighton 已提交
404
    def bind!(value) # :nodoc:
405 406
      self.bind_values += [value]
      self
A
Aaron Patterson 已提交
407 408
    end

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    # 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
    # constructor as a SQL fragment, and used in the where clause of the query.
    #
    #    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,
    # the values are inserted using the same escapes as the Ruby core method <tt>Kernel::sprintf</tt>.
    #
    #   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')
    #
484 485 486 487 488 489
    # 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 已提交
490 491
    #    Post.where(author: author)
    #    Post.where(author_id: author)
492 493 494
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
495 496
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
497 498
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
499 500
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
501
    #
502 503 504 505 506 507 508 509 510 511
    # === 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 已提交
512
    #    User.joins(:posts).where({ posts: { published: true } })
513
    #
514
    # === no argument
515
    #
516 517
    # 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.
518 519
    #
    #    User.where.not(name: "Jon")
520
    #    # SELECT * FROM users WHERE name != 'Jon'
521
    #
522
    # See WhereChain for more details on #not.
523
    #
524
    # === blank condition
525
    #
526
    # If the condition is any blank-ish object, then #where is a no-op and returns
527
    # the current relation.
528 529
    def where(opts = :chain, *rest)
      if opts == :chain
530 531 532 533 534 535
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
536 537
    end

538 539
    def where!(opts = :chain, *rest) # :nodoc:
      if opts == :chain
540 541 542
        WhereChain.new(self)
      else
        references!(PredicateBuilder.references(opts)) if Hash === opts
543

544 545 546
        self.where_values += build_where(opts, rest)
        self
      end
547
    end
P
Pratik Naik 已提交
548

549 550 551 552
    # 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')
553
    def having(opts, *rest)
554
      opts.blank? ? self : spawn.having!(opts, *rest)
555
      spawn.having!(opts, *rest)
556 557
    end

J
Jon Leighton 已提交
558
    def having!(opts, *rest) # :nodoc:
559
      references!(PredicateBuilder.references(opts)) if Hash === opts
560

561 562
      self.having_values += build_where(opts, rest)
      self
563 564
    end

565
    # Specifies a limit for the number of records to retrieve.
566 567 568 569
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
570
    def limit(value)
J
Jon Leighton 已提交
571
      spawn.limit!(value)
572 573
    end

J
Jon Leighton 已提交
574
    def limit!(value) # :nodoc:
575 576
      self.limit_value = value
      self
577 578
    end

579 580 581 582
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
583
    # Should be used with order.
584
    #
585
    #   User.offset(10).order("name ASC")
586
    def offset(value)
J
Jon Leighton 已提交
587
      spawn.offset!(value)
588 589
    end

J
Jon Leighton 已提交
590
    def offset!(value) # :nodoc:
591 592
      self.offset_value = value
      self
593 594
    end

595
    # Specifies locking settings (default to +true+). For more information
596
    # on locking, please see +ActiveRecord::Locking+.
597
    def lock(locks = true)
J
Jon Leighton 已提交
598
      spawn.lock!(locks)
599
    end
600

J
Jon Leighton 已提交
601
    def lock!(locks = true) # :nodoc:
602
      case locks
603
      when String, TrueClass, NilClass
604
        self.lock_value = locks || true
605
      else
606
        self.lock_value = false
607
      end
608

609
      self
610 611
    end

612
    # Returns a chainable relation with zero records, specifically an
V
Vijay Dev 已提交
613
    # instance of the <tt>ActiveRecord::NullRelation</tt> class.
614
    #
V
Vijay Dev 已提交
615 616 617
    # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
    # Null Object pattern. It is an object with defined null behavior and always returns an empty
    # array of records without quering the database.
618 619 620 621
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
622 623
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
624 625 626
    #
    # For example:
    #
A
AvnerCohen 已提交
627
    #   @posts = current_user.visible_posts.where(name: params[:name])
628
    #   # => the visible_posts method is expected to return a chainable Relation
629 630 631
    #
    #   def visible_posts
    #     case role
632
    #     when 'Country Manager'
A
AvnerCohen 已提交
633
    #       Post.where(country: country)
634
    #     when 'Reviewer'
635
    #       Post.published
636
    #     when 'Bad User'
637 638 639 640 641
    #       Post.none # => returning [] instead breaks the previous code
    #     end
    #   end
    #
    def none
642
      extending(NullRelation)
643 644
    end

J
Jon Leighton 已提交
645
    def none! # :nodoc:
646 647 648
      extending!(NullRelation)
    end

649 650 651 652 653 654
    # 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
    #   => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
655
    def readonly(value = true)
J
Jon Leighton 已提交
656
      spawn.readonly!(value)
657 658
    end

J
Jon Leighton 已提交
659
    def readonly!(value = true) # :nodoc:
660 661
      self.readonly_value = value
      self
662 663
    end

664 665 666 667 668 669 670 671 672 673 674 675 676
    # 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'
    #
    # You can pass +nil+ to +create_with+ to reset attributes:
    #
    #   users = users.create_with(nil)
    #   users.new.name # => 'Oscar'
677
    def create_with(value)
J
Jon Leighton 已提交
678
      spawn.create_with!(value)
679 680
    end

J
Jon Leighton 已提交
681
    def create_with!(value) # :nodoc:
682 683
      self.create_with_value = value ? create_with_value.merge(value) : {}
      self
684 685
    end

686 687 688 689 690 691 692
    # Specifies table from which the records will be fetched. For example:
    #
    #   Topic.select('title').from('posts')
    #   #=> SELECT title FROM posts
    #
    # Can accept other relation objects. For example:
    #
693
    #   Topic.select('title').from(Topic.approved)
694 695
    #   # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
    #
696
    #   Topic.select('a.title').from(Topic.approved, :a)
697 698 699 700
    #   # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
701 702
    end

J
Jon Leighton 已提交
703
    def from!(value, subquery_name = nil) # :nodoc:
704
      self.from_value = [value, subquery_name]
705
      self
706 707
    end

708 709 710 711 712
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
    #   # => Might return two records with the same name
    #
713 714
    #   User.select(:name).distinct
    #   # => Returns 1 record per distinct name
715
    #
716
    #   User.select(:name).distinct.distinct(false)
717
    #   # => You can also remove the uniqueness
718 719
    def distinct(value = true)
      spawn.distinct!(value)
720
    end
721
    alias uniq distinct
722

723 724 725
    # Like #distinct, but modifies relation in place.
    def distinct!(value = true) # :nodoc:
      self.distinct_value = value
726
      self
727
    end
728
    alias uniq! distinct!
729

730
    # Used to extend a scope with additional methods, either through
731 732
    # a module or through a block provided.
    #
733 734 735 736 737 738 739 740 741 742
    # 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
    #
743
    #   scope = Model.all.extending(Pagination)
744 745
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
746
    # You can also pass a list of modules:
747
    #
748
    #   scope = Model.all.extending(Pagination, SomethingElse)
749 750 751
    #
    # === Using a block
    #
752
    #   scope = Model.all.extending do
753
    #     def page(number)
754
    #       # pagination code goes here
755 756 757 758 759 760
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
761
    #   scope = Model.all.extending(Pagination) do
762
    #     def per_page(number)
763
    #       # pagination code goes here
764 765
    #     end
    #   end
766 767
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
768
        spawn.extending!(*modules, &block)
769 770 771 772
      else
        self
      end
    end
773

J
Jon Leighton 已提交
774
    def extending!(*modules, &block) # :nodoc:
775
      modules << Module.new(&block) if block_given?
776

J
Jon Leighton 已提交
777
      self.extending_values += modules.flatten
778
      extend(*extending_values) if extending_values.any?
779

780
      self
781 782
    end

783 784 785
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
786
    def reverse_order
J
Jon Leighton 已提交
787
      spawn.reverse_order!
788 789
    end

J
Jon Leighton 已提交
790
    def reverse_order! # :nodoc:
791 792
      self.reverse_order_value = !reverse_order_value
      self
793 794
    end

795
    # Returns the Arel object associated with the relation.
796
    def arel
797
      @arel ||= with_default_scope.build_arel
798 799
    end

800
    # Like #arel, but ignores the default scope of the model.
801
    def build_arel
802
      arel = Arel::SelectManager.new(table.engine, table)
803

804
      build_joins(arel, joins_values) unless joins_values.empty?
805

806
      collapse_wheres(arel, (where_values - ['']).uniq)
807

808
      arel.having(*having_values.uniq.reject{|h| h.blank?}) unless having_values.empty?
809

810 811
      arel.take(connection.sanitize_limit(limit_value)) if limit_value
      arel.skip(offset_value.to_i) if offset_value
A
Aaron Patterson 已提交
812

813
      arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty?
814

815
      build_order(arel)
816

817
      build_select(arel, select_values.uniq)
818

819
      arel.distinct(distinct_value)
820
      arel.from(build_from) if from_value
821
      arel.lock(lock_value) if lock_value
822 823

      arel
824 825
    end

826 827
    private

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
    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

      single_val_method = Relation::SINGLE_VALUE_METHODS.include?(scope)
      unscope_code = :"#{scope}_value#{'s' unless single_val_method}="

      case scope
      when :order
        self.send(:reverse_order_value=, false)
        result = []
      else
        result = [] unless single_val_method
      end

      self.send(unscope_code, result)
    end

    def where_unscoping(target_value)
      target_value_sym = target_value.to_sym

      where_values.reject! do |rel|
        case rel
        when Arel::Nodes::In, Arel::Nodes::Equality
          subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
          subrelation.name.to_sym == target_value_sym
        else
          raise "unscope(where: #{target_value.inspect}) failed: unscoping #{rel.class} is unimplemented."
        end
      end
    end

861
    def custom_join_ast(table, joins)
862 863
      joins = joins.reject { |join| join.blank? }

864
      return [] if joins.empty?
865 866 867

      @implicit_readonly = true

868
      joins.map do |join|
869 870 871 872 873 874
        case join
        when Array
          join = Arel.sql(join.join(' ')) if array_of_strings?(join)
        when String
          join = Arel.sql(join)
        end
875
        table.create_string_join(join)
876 877 878
      end
    end

879 880 881
    def collapse_wheres(arel, wheres)
      equalities = wheres.grep(Arel::Nodes::Equality)

A
Aaron Patterson 已提交
882
      arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
883 884 885

      (wheres - equalities).each do |where|
        where = Arel.sql(where) if String === where
886
        arel.where(Arel::Nodes::Grouping.new(where))
887 888 889
      end
    end

890
    def build_where(opts, other = [])
A
Aaron Patterson 已提交
891 892
      case opts
      when String, Array
893
        [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
A
Aaron Patterson 已提交
894
      when Hash
895
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
896 897 898 899 900

        attributes.values.grep(ActiveRecord::Relation) do |rel|
          self.bind_values += rel.bind_values
        end

901
        PredicateBuilder.build_from_hash(klass, attributes, table)
902
      else
903
        [opts]
904 905 906
      end
    end

907 908 909 910 911 912 913 914 915 916 917
    def build_from
      opts, name = from_value
      case opts
      when Relation
        name ||= 'subquery'
        opts.arel.as(name.to_s)
      else
        opts
      end
    end

918
    def build_joins(manager, joins)
A
Aaron Patterson 已提交
919 920 921
      buckets = joins.group_by do |join|
        case join
        when String
922
          :string_join
A
Aaron Patterson 已提交
923
        when Hash, Symbol, Array
924
          :association_join
925
        when ActiveRecord::Associations::JoinDependency::JoinAssociation
926
          :stashed_join
927
        when Arel::Nodes::Join
928
          :join_node
A
Aaron Patterson 已提交
929 930 931
        else
          raise 'unknown class: %s' % join.class.name
        end
932 933
      end

934 935 936 937
      association_joins         = buckets[:association_join] || []
      stashed_association_joins = buckets[:stashed_join] || []
      join_nodes                = (buckets[:join_node] || []).uniq
      string_joins              = (buckets[:string_join] || []).map { |x|
A
Aaron Patterson 已提交
938 939
        x.strip
      }.uniq
940

941
      join_list = join_nodes + custom_join_ast(manager, string_joins)
942

943
      join_dependency = ActiveRecord::Associations::JoinDependency.new(
944 945 946 947
        @klass,
        association_joins,
        join_list
      )
948 949 950 951 952

      join_dependency.graft(*stashed_association_joins)

      @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?

A
Aaron Patterson 已提交
953
      # FIXME: refactor this to build an AST
954
      join_dependency.join_associations.each do |association|
955
        association.join_to(manager)
956 957
      end

958
      manager.join_sources.concat join_list
959 960

      manager
961 962
    end

963
    def build_select(arel, selects)
964
      unless selects.empty?
965
        @implicit_readonly = false
966
        arel.project(*selects)
967
      else
968
        arel.project(@klass.arel_table[Arel.star])
969 970 971
      end
    end

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

975
      order_query.flat_map do |o|
976
        case o
977
        when Arel::Nodes::Ordering
978
          o.reverse
979
        when String
980 981 982 983
          o.to_s.split(',').collect do |s|
            s.strip!
            s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
          end
984
        when Symbol
985
          { o => :desc }
986
        when Hash
987
          o.each_with_object({}) do |(field, dir), memo|
988 989
            memo[field] = (dir == :asc ? :desc : :asc )
          end
990 991 992
        else
          o
        end
993
      end
994 995
    end

P
Pratik Naik 已提交
996 997 998
    def array_of_strings?(o)
      o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
    end
999

1000 1001 1002
    def build_order(arel)
      orders = order_values
      orders = reverse_sql_order(orders) if reverse_order_value
1003

1004
      orders = orders.uniq.reject(&:blank?).flat_map do |order|
1005 1006 1007 1008 1009
        case order
        when Symbol
          table[order].asc
        when Hash
          order.map { |field, dir| table[field].send(dir) }
1010
        else
1011 1012
          order
        end
1013
      end
1014

1015 1016
      arel.order(*orders) unless orders.empty?
    end
1017

1018 1019 1020 1021 1022 1023 1024
    def validate_order_args(args)
      args.select { |a| Hash === a  }.each do |h|
        unless (h.values - [:asc, :desc]).empty?
          raise ArgumentError, 'Direction should be :asc or :desc'
        end
      end
    end
P
Pratik Naik 已提交
1025

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
    # 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)
1039
    #   check_if_method_has_arguments!("references", args)
1040 1041
    #   ...
    # end
1042
    def check_if_method_has_arguments!(method_name, args)
1043 1044 1045 1046
      if args.blank?
        raise ArgumentError, "The method .#{method_name}() must contain arguments."
      end
    end
1047 1048
  end
end