Commit 06a9974e authored by Alex Pooley's avatar Alex Pooley

Wrap ancestor scopes in CTE

parent 8fe0036d
......@@ -25,15 +25,20 @@ module Namespaces
def self_and_ancestors(include_self: true, hierarchy_order: nil)
return super unless use_traversal_ids_for_ancestor_scopes?
ancestors_cte, base_cte = ancestor_ctes
namespaces = Arel::Table.new(:namespaces)
records = unscoped
.where(id: select('unnest(traversal_ids)'))
.with(base_cte.to_arel, ancestors_cte.to_arel)
.distinct
.from([ancestors_cte.table, namespaces])
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
.order_by_depth(hierarchy_order)
.normal_select
if include_self
records
else
records.where.not(id: all.as_ids)
records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
end
end
......@@ -150,6 +155,20 @@ module Namespaces
records.where('namespaces.id <> base.id')
end
end
def ancestor_ctes
base_scope = all.select('namespaces.id', 'namespaces.traversal_ids')
base_cte = Gitlab::SQL::CTE.new(:base_ancestors_cte, base_scope)
# We have to alias id with 'AS' to avoid ambiguous column references by calling methods.
ancestors_scope = unscoped
.unscope(where: [:type])
.select('id as base_id', 'unnest(traversal_ids) as ancestor_id')
.from(base_cte.table)
ancestors_cte = Gitlab::SQL::CTE.new(:ancestors_cte, ancestors_scope)
[ancestors_cte, base_cte]
end
end
end
end
......
......@@ -21,6 +21,9 @@ RSpec.describe Gitlab::GroupPlansPreloader, :saas do
shared_examples 'preloading cases' do
it 'only executes three SQL queries to preload the data' do
# Pre-cache `select VERSION()` query to avoid counting.
Gitlab::Database::AsWithMaterialized.materialized_supported?
amount = ActiveRecord::QueryRecorder
.new { preloaded_groups }
.count
......
......@@ -124,6 +124,12 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { expect(subject[0, 2]).to contain_exactly(group_1, group_2) }
it { expect(subject[2, 2]).to contain_exactly(nested_group_1, nested_group_2) }
end
context 'with offset and limit' do
subject { described_class.where(id: [deep_nested_group_1, deep_nested_group_2]).offset(1).limit(1).self_and_ancestors }
it { is_expected.to contain_exactly(group_2, nested_group_2, deep_nested_group_2) }
end
end
describe '.self_and_ancestors' do
......@@ -168,6 +174,19 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { is_expected.to contain_exactly(group_1.id, group_2.id) }
end
context 'with offset and limit' do
subject do
described_class
.where(id: [deep_nested_group_1, deep_nested_group_2])
.limit(1)
.offset(1)
.self_and_ancestor_ids
.pluck(:id)
end
it { is_expected.to contain_exactly(group_2.id, nested_group_2.id, deep_nested_group_2.id) }
end
end
describe '.self_and_ancestor_ids' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment