query_methods.rb 15.5 KB
Newer Older
1
require 'active_support/core_ext/array/wrap'
2 3
require 'active_support/core_ext/object/blank'

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

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
    Relation::MULTI_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}_values            # def select_values
          @values[:#{name}] || []     #   @values[:select] || []
        end                           # end
                                      #
        def #{name}_values=(values)   # def select_values=(values)
          @values[:#{name}] = values  #   @values[:select] = values
        end                           # end
      CODE
    end

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

    def create_with_value
      @values[:create_with] || {}
    end

    def create_with_value=(value)
      @values[:create_with] = value
    end
39 40

    alias extensions extending_values
41

42
    def includes(*args)
J
Jon Leighton 已提交
43
      args.empty? ? self : spawn.includes!(*args)
44
    end
45

46 47
    def includes!(*args)
      args.reject! {|a| a.blank? }
A
Aaron Patterson 已提交
48

49 50
      self.includes_values = (includes_values + args).flatten.uniq
      self
51
    end
52

53
    def eager_load(*args)
J
Jon Leighton 已提交
54
      args.blank? ? self : spawn.eager_load!(*args)
55
    end
56

57 58 59
    def eager_load!(*args)
      self.eager_load_values += args
      self
60 61 62
    end

    def preload(*args)
J
Jon Leighton 已提交
63
      args.blank? ? self : spawn.preload!(*args)
64
    end
65

66 67 68
    def preload!(*args)
      self.preload_values += args
      self
69
    end
70

71 72 73 74 75 76 77 78 79
    # 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)
J
Jon Leighton 已提交
80
      args.blank? ? self : spawn.references!(*args)
81
    end
82

83 84 85
    def references!(*args)
      self.references_values = (references_values + args.flatten.map(&:to_s)).uniq
      self
86 87
    end

88
    # Works in two unique ways.
89
    #
90 91 92 93 94 95 96 97
    # First: takes a block so it can be used just like Array#select.
    #
    #   Model.scoped.select { |m| m.field == value }
    #
    # 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 已提交
98
    # fields are retrieved:
99 100 101 102 103
    #
    #   >> Model.select(:field)
    #   => [#<Model field:value>]
    #
    # Although in the above example it looks as though this method returns an
V
Vijay Dev 已提交
104
    # array, it actually returns a relation object and can have other query
105 106
    # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
    #
107
    # The argument to the method can also be an array of fields.
108
    #
109
    #   >> Model.select([:field, :other_field, :and_one_more])
V
Vijay Dev 已提交
110
    #   => [#<Model field: "value", other_field: "value", and_one_more: "value">]
111
    #
112 113
    # Accessing attributes of an object that do not have fields retrieved by a select
    # will throw <tt>ActiveModel::MissingAttributeError</tt>:
114 115
    #
    #   >> Model.select(:field).first.other_field
116
    #   => ActiveModel::MissingAttributeError: missing attribute: other_field
117
    def select(value = Proc.new)
118
      if block_given?
119 120
        to_a.select { |*block_args| value.call(*block_args) }
      else
J
Jon Leighton 已提交
121
        spawn.select!(value)
122 123 124
      end
    end

125 126 127
    def select!(value)
      self.select_values += Array.wrap(value)
      self
128
    end
S
Santiago Pastorino 已提交
129

130
    def group(*args)
J
Jon Leighton 已提交
131
      args.blank? ? self : spawn.group!(*args)
132
    end
133

134 135 136
    def group!(*args)
      self.group_values += args.flatten
      self
137
    end
138

139
    def order(*args)
J
Jon Leighton 已提交
140
      args.blank? ? self : spawn.order!(*args)
141
    end
142

143
    def order!(*args)
144
      args       = args.flatten
145

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

151 152
      self.order_values += args
      self
153
    end
154

155 156 157 158 159 160 161 162 163
    # 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')
    #
    # generates a query with 'ORDER BY id ASC, name ASC'.
S
Sebastian Martinez 已提交
164
    def reorder(*args)
J
Jon Leighton 已提交
165
      args.blank? ? self : spawn.reorder!(*args)
166
    end
167

168 169 170 171
    def reorder!(*args)
      self.reordering_value = true
      self.order_values = args.flatten
      self
S
Sebastian Martinez 已提交
172 173
    end

174
    def joins(*args)
J
Jon Leighton 已提交
175
      args.compact.blank? ? self : spawn.joins!(*args)
176
    end
177

178
    def joins!(*args)
A
Aaron Patterson 已提交
179
      args.flatten!
180

181 182
      self.joins_values += args
      self
P
Pratik Naik 已提交
183 184
    end

A
Aaron Patterson 已提交
185
    def bind(value)
J
Jon Leighton 已提交
186
      spawn.bind!(value)
187 188 189 190 191
    end

    def bind!(value)
      self.bind_values += [value]
      self
A
Aaron Patterson 已提交
192 193
    end

194
    def where(opts, *rest)
J
Jon Leighton 已提交
195
      opts.blank? ? self : spawn.where!(opts, *rest)
196 197 198 199
    end

    def where!(opts, *rest)
      references!(PredicateBuilder.references(opts)) if Hash === opts
200

201 202
      self.where_values += build_where(opts, rest)
      self
203
    end
P
Pratik Naik 已提交
204

205
    def having(opts, *rest)
J
Jon Leighton 已提交
206
      opts.blank? ? self : spawn.having!(opts, *rest)
207 208 209 210
    end

    def having!(opts, *rest)
      references!(PredicateBuilder.references(opts)) if Hash === opts
211

212 213
      self.having_values += build_where(opts, rest)
      self
214 215
    end

216 217 218 219 220 221 222
    # Specifies a limit of records.
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    # Replaces any existing previous limit.
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
223
    def limit(value)
J
Jon Leighton 已提交
224
      spawn.limit!(value)
225 226 227 228 229
    end

    def limit!(value)
      self.limit_value = value
      self
230 231
    end

232 233 234 235 236 237 238
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
    # Should be used with order.  
    #
    #   User.offset(10).order("name ASC") 
239
    def offset(value)
J
Jon Leighton 已提交
240
      spawn.offset!(value)
241 242 243 244 245
    end

    def offset!(value)
      self.offset_value = value
      self
246 247 248
    end

    def lock(locks = true)
J
Jon Leighton 已提交
249
      spawn.lock!(locks)
250
    end
251

252
    def lock!(locks = true)
253
      case locks
254
      when String, TrueClass, NilClass
255
        self.lock_value = locks || true
256
      else
257
        self.lock_value = false
258
      end
259

260
      self
261 262
    end

263 264 265 266 267
    # Returns a chainable relation with zero records, specifically an
    # instance of the NullRelation class.
    #
    # The returned NullRelation inherits from Relation and implements the
    # Null Object pattern so it is an object with defined null behavior:
268
    # it always returns an empty array of records and does not query the database.
269 270 271 272
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
273 274
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
275 276 277 278
    #
    # For example:
    #
    #   @posts = current_user.visible_posts.where(:name => params[:name])
279
    #   # => the visible_posts method is expected to return a chainable Relation
280 281 282
    #
    #   def visible_posts
    #     case role
283
    #     when 'Country Manager'
284
    #       Post.where(:country => country)
285
    #     when 'Reviewer'
286
    #       Post.published
287
    #     when 'Bad User'
288 289 290 291 292
    #       Post.none # => returning [] instead breaks the previous code
    #     end
    #   end
    #
    def none
A
Akira Matsuda 已提交
293
      scoped.extending(NullRelation)
294 295
    end

296
    def readonly(value = true)
J
Jon Leighton 已提交
297
      spawn.readonly!(value)
298 299 300 301 302
    end

    def readonly!(value = true)
      self.readonly_value = value
      self
303 304
    end

305
    def create_with(value)
J
Jon Leighton 已提交
306
      spawn.create_with!(value)
307 308 309 310 311
    end

    def create_with!(value)
      self.create_with_value = value ? create_with_value.merge(value) : {}
      self
312 313
    end

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    # 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:
    #
    #   Topic.select('title').from(Topics.approved)
    #   # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
    #
    #   Topics.select('a.title').from(Topics.approved, :a)
    #   # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
329 330
    end

331 332
    def from!(value, subquery_name = nil)
      self.from_value = [value, subquery_name]
333
      self
334 335
    end

336 337 338 339 340 341 342 343 344 345 346
    # 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 已提交
347
      spawn.uniq!(value)
348 349 350 351 352
    end

    def uniq!(value = true)
      self.uniq_value = value
      self
353 354
    end

355
    # Used to extend a scope with additional methods, either through
356 357
    # a module or through a block provided.
    #
358 359 360 361 362 363 364 365 366 367 368 369 370
    # 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
    #
    #   scope = Model.scoped.extending(Pagination)
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
371
    # You can also pass a list of modules:
372 373 374 375 376 377 378
    #
    #   scope = Model.scoped.extending(Pagination, SomethingElse)
    #
    # === Using a block
    #
    #   scope = Model.scoped.extending do
    #     def page(number)
379
    #       # pagination code goes here
380 381 382 383 384 385 386 387
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
    #   scope = Model.scoped.extending(Pagination) do
    #     def per_page(number)
388
    #       # pagination code goes here
389 390
    #     end
    #   end
391 392
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
393
        spawn.extending!(*modules, &block)
394 395 396 397
      else
        self
      end
    end
398

399 400
    def extending!(*modules, &block)
      modules << Module.new(&block) if block_given?
401

402
      self.extending_values = modules.flatten
403
      extend(*extending_values) if extending_values.any?
404

405
      self
406 407
    end

408 409 410
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
411
    def reverse_order
J
Jon Leighton 已提交
412
      spawn.reverse_order!
413 414 415 416 417
    end

    def reverse_order!
      self.reverse_order_value = !reverse_order_value
      self
418 419
    end

420
    def arel
421
      @arel ||= with_default_scope.build_arel
422 423
    end

424
    def build_arel
425
      arel = table.from table
426

427
      build_joins(arel, joins_values) unless joins_values.empty?
428

429
      collapse_wheres(arel, (where_values - ['']).uniq)
430

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

433 434
      arel.take(connection.sanitize_limit(limit_value)) if limit_value
      arel.skip(offset_value.to_i) if offset_value
A
Aaron Patterson 已提交
435

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

438 439
      order = order_values
      order = reverse_sql_order(order) if reverse_order_value
440
      arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
441

442
      build_select(arel, select_values.uniq)
443

444
      arel.distinct(uniq_value)
445
      arel.from(build_from) if from_value
446
      arel.lock(lock_value) if lock_value
447 448

      arel
449 450
    end

451 452
    private

453
    def custom_join_ast(table, joins)
454 455
      joins = joins.reject { |join| join.blank? }

456
      return [] if joins.empty?
457 458 459

      @implicit_readonly = true

460
      joins.map do |join|
461 462 463 464 465 466
        case join
        when Array
          join = Arel.sql(join.join(' ')) if array_of_strings?(join)
        when String
          join = Arel.sql(join)
        end
467
        table.create_string_join(join)
468 469 470
      end
    end

471 472 473
    def collapse_wheres(arel, wheres)
      equalities = wheres.grep(Arel::Nodes::Equality)

A
Aaron Patterson 已提交
474
      arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
475 476 477

      (wheres - equalities).each do |where|
        where = Arel.sql(where) if String === where
478
        arel.where(Arel::Nodes::Grouping.new(where))
479 480 481
      end
    end

482
    def build_where(opts, other = [])
A
Aaron Patterson 已提交
483 484
      case opts
      when String, Array
485
        [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
A
Aaron Patterson 已提交
486 487
      when Hash
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
488
        PredicateBuilder.build_from_hash(table.engine, attributes, table)
489
      else
490
        [opts]
491 492 493
      end
    end

494 495 496 497 498 499 500 501 502 503 504
    def build_from
      opts, name = from_value
      case opts
      when Relation
        name ||= 'subquery'
        opts.arel.as(name.to_s)
      else
        opts
      end
    end

505
    def build_joins(manager, joins)
A
Aaron Patterson 已提交
506 507 508 509 510 511
      buckets = joins.group_by do |join|
        case join
        when String
          'string_join'
        when Hash, Symbol, Array
          'association_join'
512
        when ActiveRecord::Associations::JoinDependency::JoinAssociation
A
Aaron Patterson 已提交
513
          'stashed_join'
514 515
        when Arel::Nodes::Join
          'join_node'
A
Aaron Patterson 已提交
516 517 518
        else
          raise 'unknown class: %s' % join.class.name
        end
519 520
      end

A
Aaron Patterson 已提交
521 522
      association_joins         = buckets['association_join'] || []
      stashed_association_joins = buckets['stashed_join'] || []
523
      join_nodes                = (buckets['join_node'] || []).uniq
A
Aaron Patterson 已提交
524 525 526
      string_joins              = (buckets['string_join'] || []).map { |x|
        x.strip
      }.uniq
527

528
      join_list = join_nodes + custom_join_ast(manager, string_joins)
529

530
      join_dependency = ActiveRecord::Associations::JoinDependency.new(
531 532 533 534
        @klass,
        association_joins,
        join_list
      )
535 536 537 538 539

      join_dependency.graft(*stashed_association_joins)

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

A
Aaron Patterson 已提交
540
      # FIXME: refactor this to build an AST
541
      join_dependency.join_associations.each do |association|
542
        association.join_to(manager)
543 544
      end

545
      manager.join_sources.concat join_list
546 547

      manager
548 549
    end

550
    def build_select(arel, selects)
551
      unless selects.empty?
552
        @implicit_readonly = false
553
        arel.project(*selects)
554
      else
555
        arel.project(@klass.arel_table[Arel.star])
556 557 558
      end
    end

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

562 563
      order_query.map do |o|
        case o
564
        when Arel::Nodes::Ordering
565 566
          o.reverse
        when String, Symbol
567 568 569 570
          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
571 572 573 574
        else
          o
        end
      end.flatten
575 576
    end

P
Pratik Naik 已提交
577 578 579 580
    def array_of_strings?(o)
      o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
    end

581 582
  end
end