@@ -296,10 +296,9 @@ def search(options = {})
296296 scope = scope . except ( :offset , :limit , :order )
297297 scope = scope_targets ( klass , scope , user_filters , user , miq_group )
298298 . where ( conditions ) . where ( sub_filter ) . where ( where_clause ) . where ( exp_sql ) . where ( ids_clause )
299- . includes ( include_for_find ) . includes ( exp_includes )
300299 . order ( order )
301300
302- scope = include_references ( scope , klass , references , exp_includes )
301+ scope = include_references ( scope , klass , include_for_find , references , exp_includes )
303302 scope = scope . limit ( limit ) . offset ( offset ) if attrs [ :apply_limit_in_sql ]
304303
305304 # SELECT col1, (SELECT "abc") AS virtual_col
@@ -324,11 +323,17 @@ def search(options = {})
324323 # Hence auth_count calculated from inner_scope.
325324 #
326325 if inline_view? ( options , scope )
327- inner_scope = scope . except ( :select , :includes , :references )
328- scope . includes_values . each { |hash | inner_scope = add_joins ( klass , inner_scope , hash ) }
326+ inner_scope = scope . except ( :select , :includes , :references , :eager_load , :preload )
327+ # similar to include_references but using joins
328+ # TODO: optimization: Can we remove these references from the outer query?
329+ # To do this, we may need to introduce join() to complete the triad: include/reference/join
330+ inner_scope = add_joins ( klass , inner_scope , references )
331+ # TODO: do we want to klass.prune_references(exp_includes)? (see also include_references)
332+ inner_scope = add_joins ( klass , inner_scope , exp_includes )
329333 if inner_scope . order_values . present?
330334 inner_scope = apply_select ( klass , inner_scope , select_from_order_columns ( inner_scope . order_values ) )
331335 end
336+ # TODO: Convert from(inner_scope.to_sql) to from(inner_scope)
332337 scope = scope . from ( Arel . sql ( "(#{ inner_scope . to_sql } )" ) . as ( scope . table_name ) )
333338 . except ( :offset , :limit , :where )
334339
@@ -424,25 +429,42 @@ def select_from_order_columns(columns)
424429 end
425430 end
426431
427- def include_references ( scope , klass , references , exp_includes )
428- scope . references ( klass . includes_to_references ( references ) ) . references ( klass . includes_to_references ( exp_includes ) )
432+ def include_references ( scope , klass , includes , references , exp_includes )
433+ if scope . respond_to? ( :eager_load )
434+ # TODO: do we want to klass.prune_references(exp_includes)? (see same comment for inline_view? section)
435+ scope . eager_load ( references || { } ) . eager_load ( exp_includes || { } ) . preload ( includes )
436+ else
437+ # This is the AAAR / QueryRelation branch
438+ # TODO: drop this fallback once https://github.com/ManageIQ/query_relation/pull/43 is merged
439+ scope . references ( references || { } ) . references ( exp_includes || { } ) . includes ( includes )
440+ end
429441 end
430442
431443 # @param includes [Array, Hash]
432444 def add_joins ( klass , scope , includes )
433445 return scope unless includes
434446
447+ # NOTE: We should be pre-pruning polymorphic out of here, since they shouldn't be in references.
448+ # TODO: do we want to be less lenient? i.e.: raise PolymorphicError if reflection&.polymorphic?
435449 includes = Array ( includes ) unless includes . kind_of? ( Enumerable )
436450 includes . each do |association , value |
437451 reflection = klass . reflect_on_association ( association )
438452 if reflection && !reflection . polymorphic?
439453 scope = value ? scope . left_outer_joins ( association => value ) : scope . left_outer_joins ( association )
440- scope = scope . distinct if reflection . try ( :collection? )
441454 end
442455 end
456+ # This adds a DISTINCT to safeguard "JOIN has_many" screwing up the LIMIT.
457+ scope = scope . distinct if !scope . distinct_value && is_collection? ( klass , includes )
443458 scope
444459 end
445460
461+ def is_collection? ( klass , includes )
462+ Array ( includes ) . any? do |association , value |
463+ reflection = klass . reflect_on_association ( association )
464+ reflection &.collection? || !reflection . polymorphic? && is_collection? ( reflection . klass , value )
465+ end
466+ end
467+
446468 def filtered ( objects , options = { } )
447469 Rbac . search ( options . reverse_merge ( :targets => objects , :skip_counts => true ) ) . first
448470 end
0 commit comments