Project

General

Profile

Feature #44409 ยป 0001-Skip-the-nested-set-self-join-in-Issue.load_visible_.patch

Go MAEDA, 2026-09-02 11:24

View differences:

app/models/issue.rb
1258 1258
  # Preloads visible total spent time for a collection of issues
1259 1259
  def self.load_visible_total_spent_hours(issues, user=User.current)
1260
    if issues.any?
1260
    return if issues.empty?
1261

  
1262
    if issues.all?(&:leaf?)
1263
      # None of the issues has subtasks, so the expensive nested set
1264
      # self-join can be skipped.
1265
      # Keep this query identical to the one in load_visible_spent_hours so
1266
      # that it is served from the query cache when both are called in the
1267
      # same request (e.g. IssuesController#show and #index).
1268
      hours_by_issue_id = TimeEntry.visible(user).
1269
        where(:issue_id => issues.map(&:id)).group(:issue_id).sum(:hours)
1270
    else
1261 1271
      hours_by_issue_id = TimeEntry.visible(user).joins(:issue).
1262 1272
        joins("JOIN #{Issue.table_name} parent ON parent.root_id = #{Issue.table_name}.root_id" +
1263 1273
          " AND parent.lft <= #{Issue.table_name}.lft AND parent.rgt >= #{Issue.table_name}.rgt").
1264 1274
        where("parent.id IN (?)", issues.map(&:id)).group("parent.id").sum(:hours)
1265
      issues.each do |issue|
1266
        issue.instance_variable_set :@total_spent_hours, (hours_by_issue_id[issue.id] || 0.0)
1267
      end
1275
    end
1276
    issues.each do |issue|
1277
      issue.instance_variable_set :@total_spent_hours, (hours_by_issue_id[issue.id] || 0.0)
1268 1278
    end
1269 1279
  end
test/unit/issue_test.rb
240 240
    assert_include 'Parent task is invalid', issue.errors.full_messages
241 241
  end
242
  def test_load_visible_total_spent_hours_should_load_visible_hours_for_leaf_issues
243
    issues = Issue.where(:id => [1, 2]).to_a
244
    assert issues.all?(&:leaf?)
245
    user = User.generate!
246
    role = Role.generate!(:permissions => [:view_issues, :view_time_entries], :time_entries_visibility => 'own')
247
    User.add_to_project(user, Project.find(1), role)
248
    TimeEntry.generate!(:issue => issues[0], :hours => 100.0, :user => User.find(2))
249
    TimeEntry.generate!(:issue => issues[1], :hours => 100.0, :user => User.find(2))
250
    TimeEntry.generate!(:issue => issues[1], :hours => 2.5, :user => user)
251

  
252
    Issue.load_visible_total_spent_hours(issues, user)
253

  
254
    assert_equal 0.0, issues[0].total_spent_hours
255
    assert_equal 2.5, issues[1].total_spent_hours
256
  end
257

  
242 258
  def assert_visibility_match(user, issues)
243 259
    assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
244 260
  end
    (1-1/1)