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

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

7 8 9 10 11 12 13
    # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
    # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation.
    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
      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
      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
      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
      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
      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
      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
      self.order_values = args + self.order_values
289
      self
290
    end
291

292 293 294 295 296 297 298 299
    # 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')
    #
300
    # generates a query with 'ORDER BY name ASC, id ASC'.
S
Sebastian Martinez 已提交
301
    def reorder(*args)
302
      has_arguments?("reorder", args)
303
      spawn.reorder!(*args)
304
    end
305

J
Jon Leighton 已提交
306
    def reorder!(*args) # :nodoc:
307
      args.flatten!
308
      validate_order_args args
309

310
      self.reordering_value = true
311
      self.order_values = args
312
      self
S
Sebastian Martinez 已提交
313 314
    end

315 316 317 318
    # Performs a joins on +args+:
    #
    #   User.joins(:posts)
    #   => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
319 320 321 322 323
    #
    # 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
324
    def joins(*args)
325
      has_arguments?("joins", args)
326
      spawn.joins!(*args.compact.flatten)
327
    end
328

J
Jon Leighton 已提交
329
    def joins!(*args) # :nodoc:
330 331
      self.joins_values += args
      self
P
Pratik Naik 已提交
332 333
    end

A
Aaron Patterson 已提交
334
    def bind(value)
J
Jon Leighton 已提交
335
      spawn.bind!(value)
336 337
    end

J
Jon Leighton 已提交
338
    def bind!(value) # :nodoc:
339 340
      self.bind_values += [value]
      self
A
Aaron Patterson 已提交
341 342
    end

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 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    # 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')
    #
418 419 420 421 422 423
    # 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 已提交
424 425
    #    Post.where(author: author)
    #    Post.where(author_id: author)
426 427 428
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
429 430
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
431 432
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
433 434
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
435
    #
436 437 438 439 440 441 442 443 444 445
    # === 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 已提交
446
    #    User.joins(:posts).where({ posts: { published: true } })
447
    #
448
    # === no argument
449
    #
450 451
    # 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.
452 453
    #
    #    User.where.not(name: "Jon")
454
    #    # SELECT * FROM users WHERE name != 'Jon'
455
    #
456
    # See WhereChain for more details on #not.
457
    #
458
    # === blank condition
459
    #
460
    # If the condition is any blank-ish object, then #where is a no-op and returns
461
    # the current relation.
462 463
    def where(opts = :chain, *rest)
      if opts == :chain
464 465 466 467 468 469
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
470 471
    end

472 473
    def where!(opts = :chain, *rest) # :nodoc:
      if opts == :chain
474 475 476
        WhereChain.new(self)
      else
        references!(PredicateBuilder.references(opts)) if Hash === opts
477

478 479 480
        self.where_values += build_where(opts, rest)
        self
      end
481
    end
P
Pratik Naik 已提交
482

483 484 485 486
    # 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')
487
    def having(opts, *rest)
488
      opts.blank? ? self : spawn.having!(opts, *rest)
489
      spawn.having!(opts, *rest)
490 491
    end

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

495 496
      self.having_values += build_where(opts, rest)
      self
497 498
    end

499
    # Specifies a limit for the number of records to retrieve.
500 501 502 503
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
504
    def limit(value)
J
Jon Leighton 已提交
505
      spawn.limit!(value)
506 507
    end

J
Jon Leighton 已提交
508
    def limit!(value) # :nodoc:
509 510
      self.limit_value = value
      self
511 512
    end

513 514 515 516
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
517
    # Should be used with order.
518
    #
519
    #   User.offset(10).order("name ASC")
520
    def offset(value)
J
Jon Leighton 已提交
521
      spawn.offset!(value)
522 523
    end

J
Jon Leighton 已提交
524
    def offset!(value) # :nodoc:
525 526
      self.offset_value = value
      self
527 528
    end

529
    # Specifies locking settings (default to +true+). For more information
530
    # on locking, please see +ActiveRecord::Locking+.
531
    def lock(locks = true)
J
Jon Leighton 已提交
532
      spawn.lock!(locks)
533
    end
534

J
Jon Leighton 已提交
535
    def lock!(locks = true) # :nodoc:
536
      case locks
537
      when String, TrueClass, NilClass
538
        self.lock_value = locks || true
539
      else
540
        self.lock_value = false
541
      end
542

543
      self
544 545
    end

546
    # Returns a chainable relation with zero records, specifically an
V
Vijay Dev 已提交
547
    # instance of the <tt>ActiveRecord::NullRelation</tt> class.
548
    #
V
Vijay Dev 已提交
549 550 551
    # 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.
552 553 554 555
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
556 557
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
558 559 560
    #
    # For example:
    #
A
AvnerCohen 已提交
561
    #   @posts = current_user.visible_posts.where(name: params[:name])
562
    #   # => the visible_posts method is expected to return a chainable Relation
563 564 565
    #
    #   def visible_posts
    #     case role
566
    #     when 'Country Manager'
A
AvnerCohen 已提交
567
    #       Post.where(country: country)
568
    #     when 'Reviewer'
569
    #       Post.published
570
    #     when 'Bad User'
571 572 573 574 575
    #       Post.none # => returning [] instead breaks the previous code
    #     end
    #   end
    #
    def none
576
      extending(NullRelation)
577 578
    end

J
Jon Leighton 已提交
579
    def none! # :nodoc:
580 581 582
      extending!(NullRelation)
    end

583 584 585 586 587 588
    # 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
589
    def readonly(value = true)
J
Jon Leighton 已提交
590
      spawn.readonly!(value)
591 592
    end

J
Jon Leighton 已提交
593
    def readonly!(value = true) # :nodoc:
594 595
      self.readonly_value = value
      self
596 597
    end

598 599 600 601 602 603 604 605 606 607 608 609 610
    # 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'
611
    def create_with(value)
J
Jon Leighton 已提交
612
      spawn.create_with!(value)
613 614
    end

J
Jon Leighton 已提交
615
    def create_with!(value) # :nodoc:
616 617
      self.create_with_value = value ? create_with_value.merge(value) : {}
      self
618 619
    end

620 621 622 623 624 625 626
    # 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:
    #
627
    #   Topic.select('title').from(Topic.approved)
628 629
    #   # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
    #
630
    #   Topic.select('a.title').from(Topic.approved, :a)
631 632 633 634
    #   # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
635 636
    end

J
Jon Leighton 已提交
637
    def from!(value, subquery_name = nil) # :nodoc:
638
      self.from_value = [value, subquery_name]
639
      self
640 641
    end

642 643 644 645 646 647 648 649 650 651 652
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
    #   # => Might return two records with the same name
    #
    #   User.select(:name).uniq
    #   # => Returns 1 record per unique name
    #
    #   User.select(:name).uniq.uniq(false)
    #   # => You can also remove the uniqueness
    def uniq(value = true)
J
Jon Leighton 已提交
653
      spawn.uniq!(value)
654 655
    end

656
    # Like #uniq, but modifies relation in place.
J
Jon Leighton 已提交
657
    def uniq!(value = true) # :nodoc:
658 659
      self.uniq_value = value
      self
660 661
    end

662
    # Used to extend a scope with additional methods, either through
663 664
    # a module or through a block provided.
    #
665 666 667 668 669 670 671 672 673 674
    # 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
    #
675
    #   scope = Model.all.extending(Pagination)
676 677
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
678
    # You can also pass a list of modules:
679
    #
680
    #   scope = Model.all.extending(Pagination, SomethingElse)
681 682 683
    #
    # === Using a block
    #
684
    #   scope = Model.all.extending do
685
    #     def page(number)
686
    #       # pagination code goes here
687 688 689 690 691 692
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
693
    #   scope = Model.all.extending(Pagination) do
694
    #     def per_page(number)
695
    #       # pagination code goes here
696 697
    #     end
    #   end
698 699
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
700
        spawn.extending!(*modules, &block)
701 702 703 704
      else
        self
      end
    end
705

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

J
Jon Leighton 已提交
709
      self.extending_values += modules.flatten
710
      extend(*extending_values) if extending_values.any?
711

712
      self
713 714
    end

715 716 717
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
718
    def reverse_order
J
Jon Leighton 已提交
719
      spawn.reverse_order!
720 721
    end

J
Jon Leighton 已提交
722
    def reverse_order! # :nodoc:
723 724
      self.reverse_order_value = !reverse_order_value
      self
725 726
    end

727
    # Returns the Arel object associated with the relation.
728
    def arel
729
      @arel ||= with_default_scope.build_arel
730 731
    end

732
    # Like #arel, but ignores the default scope of the model.
733
    def build_arel
734
      arel = Arel::SelectManager.new(table.engine, table)
735

736
      build_joins(arel, joins_values) unless joins_values.empty?
737

738
      collapse_wheres(arel, (where_values - ['']).uniq)
739

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

742 743
      arel.take(connection.sanitize_limit(limit_value)) if limit_value
      arel.skip(offset_value.to_i) if offset_value
A
Aaron Patterson 已提交
744

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

747
      build_order(arel)
748

749
      build_select(arel, select_values.uniq)
750

751
      arel.distinct(uniq_value)
752
      arel.from(build_from) if from_value
753
      arel.lock(lock_value) if lock_value
754 755

      arel
756 757
    end

758 759
    private

760
    def custom_join_ast(table, joins)
761 762
      joins = joins.reject { |join| join.blank? }

763
      return [] if joins.empty?
764 765 766

      @implicit_readonly = true

767
      joins.map do |join|
768 769 770 771 772 773
        case join
        when Array
          join = Arel.sql(join.join(' ')) if array_of_strings?(join)
        when String
          join = Arel.sql(join)
        end
774
        table.create_string_join(join)
775 776 777
      end
    end

778 779 780
    def collapse_wheres(arel, wheres)
      equalities = wheres.grep(Arel::Nodes::Equality)

A
Aaron Patterson 已提交
781
      arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
782 783 784

      (wheres - equalities).each do |where|
        where = Arel.sql(where) if String === where
785
        arel.where(Arel::Nodes::Grouping.new(where))
786 787 788
      end
    end

789
    def build_where(opts, other = [])
A
Aaron Patterson 已提交
790 791
      case opts
      when String, Array
792
        [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
A
Aaron Patterson 已提交
793
      when Hash
794
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
795 796 797 798 799

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

800
        PredicateBuilder.build_from_hash(klass, attributes, table)
801
      else
802
        [opts]
803 804 805
      end
    end

806 807 808 809 810 811 812 813 814 815 816
    def build_from
      opts, name = from_value
      case opts
      when Relation
        name ||= 'subquery'
        opts.arel.as(name.to_s)
      else
        opts
      end
    end

817
    def build_joins(manager, joins)
A
Aaron Patterson 已提交
818 819 820
      buckets = joins.group_by do |join|
        case join
        when String
821
          :string_join
A
Aaron Patterson 已提交
822
        when Hash, Symbol, Array
823
          :association_join
824
        when ActiveRecord::Associations::JoinDependency::JoinAssociation
825
          :stashed_join
826
        when Arel::Nodes::Join
827
          :join_node
A
Aaron Patterson 已提交
828 829 830
        else
          raise 'unknown class: %s' % join.class.name
        end
831 832
      end

833 834 835 836
      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 已提交
837 838
        x.strip
      }.uniq
839

840
      join_list = join_nodes + custom_join_ast(manager, string_joins)
841

842
      join_dependency = ActiveRecord::Associations::JoinDependency.new(
843 844 845 846
        @klass,
        association_joins,
        join_list
      )
847 848 849 850 851

      join_dependency.graft(*stashed_association_joins)

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

A
Aaron Patterson 已提交
852
      # FIXME: refactor this to build an AST
853
      join_dependency.join_associations.each do |association|
854
        association.join_to(manager)
855 856
      end

857
      manager.join_sources.concat join_list
858 859

      manager
860 861
    end

862
    def build_select(arel, selects)
863
      unless selects.empty?
864
        @implicit_readonly = false
865
        arel.project(*selects)
866
      else
867
        arel.project(@klass.arel_table[Arel.star])
868 869 870
      end
    end

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

874
      order_query.flat_map do |o|
875
        case o
876
        when Arel::Nodes::Ordering
877
          o.reverse
878
        when String
879 880 881 882
          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
883
        when Symbol
884
          { o => :desc }
885
        when Hash
886
          o.each_with_object({}) do |(field, dir), memo|
887 888
            memo[field] = (dir == :asc ? :desc : :asc )
          end
889 890 891
        else
          o
        end
892
      end
893 894
    end

P
Pratik Naik 已提交
895 896 897
    def array_of_strings?(o)
      o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
    end
898

899 900 901
    def build_order(arel)
      orders = order_values
      orders = reverse_sql_order(orders) if reverse_order_value
902

903
      orders = orders.uniq.reject(&:blank?).flat_map do |order|
904 905 906 907 908
        case order
        when Symbol
          table[order].asc
        when Hash
          order.map { |field, dir| table[field].send(dir) }
909
        else
910 911
          order
        end
912
      end
913

914 915
      arel.order(*orders) unless orders.empty?
    end
916

917 918 919 920 921 922 923
    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 已提交
924

925 926 927 928 929 930 931 932 933 934 935 936 937
    # 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)
938
    #   has_arguments?("references", args)
939 940
    #   ...
    # end
941
    def has_arguments?(method_name, args)
942 943 944 945
      if args.blank?
        raise ArgumentError, "The method .#{method_name}() must contain arguments."
      end
    end
946 947
  end
end