Skip to content

Commit 7fb2ba5

Browse files
committed
Optimizer hints should be applied on Top level query as much as possible
I've experienced this issue in our app, some hints only works on Top level query (e.g. `MAX_EXECUTION_TIME`).
1 parent 464d625 commit 7fb2ba5

File tree

5 files changed

+30
-5
lines changed

5 files changed

+30
-5
lines changed

activerecord/lib/active_record/collection_cache_key.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :no
2323
query = collection.select("#{column} AS collection_cache_key_timestamp")
2424
subquery_alias = "subquery_for_cache_key"
2525
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
26-
subquery = query.arel.as(subquery_alias)
27-
arel = Arel::SelectManager.new(subquery).project(select_values % subquery_column)
26+
arel = query.send(:build_subquery, subquery_alias, select_values % subquery_column)
2827
else
2928
query = collection.unscope(:order)
3029
query.select_values = [select_values % column]

activerecord/lib/active_record/relation/calculations.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,16 +416,17 @@ def select_for_count
416416

417417
def build_count_subquery(relation, column_name, distinct)
418418
if column_name == :all
419+
column_alias = Arel.star
419420
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
420421
else
421422
column_alias = Arel.sql("count_column")
422423
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
423424
end
424425

425-
subquery = relation.arel.as(Arel.sql("subquery_for_count"))
426-
select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
426+
subquery_alias = Arel.sql("subquery_for_count")
427+
select_value = operation_over_aggregate_column(column_alias, "count", false)
427428

428-
Arel::SelectManager.new(subquery).project(select_value)
429+
relation.build_subquery(subquery_alias, select_value)
429430
end
430431
end
431432
end

activerecord/lib/active_record/relation/query_methods.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,15 @@ def arel(aliases = nil) # :nodoc:
992992
@arel ||= build_arel(aliases)
993993
end
994994

995+
protected
996+
def build_subquery(subquery_alias, select_value) # :nodoc:
997+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
998+
999+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
1000+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1001+
end
1002+
end
1003+
9951004
private
9961005
# Returns a relation value with a given name
9971006
def get_value(name)

activerecord/test/cases/adapters/mysql2/optimizer_hints_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ def test_optimizer_hints
1515
end
1616
end
1717

18+
def test_optimizer_hints_with_count_subquery
19+
assert_sql(%r{\ASELECT /\*\+ NO_RANGE_OPTIMIZATION\(posts index_posts_on_author_id\) \*/}) do
20+
posts = Post.optimizer_hints("NO_RANGE_OPTIMIZATION(posts index_posts_on_author_id)")
21+
posts = posts.select(:id).where(author_id: [0, 1]).limit(5)
22+
assert_equal 5, posts.count
23+
end
24+
end
25+
1826
def test_optimizer_hints_is_sanitized
1927
assert_sql(%r{\ASELECT /\*\+ NO_RANGE_OPTIMIZATION\(posts index_posts_on_author_id\) \*/}) do
2028
posts = Post.optimizer_hints("/*+ NO_RANGE_OPTIMIZATION(posts index_posts_on_author_id) */")

activerecord/test/cases/adapters/postgresql/optimizer_hints_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ def test_optimizer_hints
1919
end
2020
end
2121

22+
def test_optimizer_hints_with_count_subquery
23+
assert_sql(%r{\ASELECT /\*\+ SeqScan\(posts\) \*/}) do
24+
posts = Post.optimizer_hints("SeqScan(posts)")
25+
posts = posts.select(:id).where(author_id: [0, 1]).limit(5)
26+
assert_equal 5, posts.count
27+
end
28+
end
29+
2230
def test_optimizer_hints_is_sanitized
2331
assert_sql(%r{\ASELECT /\*\+ SeqScan\(posts\) \*/}) do
2432
posts = Post.optimizer_hints("/*+ SeqScan(posts) */")

0 commit comments

Comments
 (0)