diff options
Diffstat (limited to 'app/models/namespaces/traversal')
-rw-r--r-- | app/models/namespaces/traversal/linear.rb | 13 | ||||
-rw-r--r-- | app/models/namespaces/traversal/linear_scopes.rb | 37 | ||||
-rw-r--r-- | app/models/namespaces/traversal/recursive_scopes.rb | 4 |
3 files changed, 45 insertions, 9 deletions
diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 5a5f2a5d063..757a0e40eb3 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -57,6 +57,13 @@ module Namespaces traversal_ids.present? end + def use_traversal_ids_for_self_and_hierarchy? + return false unless use_traversal_ids? + return false unless Feature.enabled?(:use_traversal_ids_for_self_and_hierarchy, root_ancestor, default_enabled: :yaml) + + traversal_ids.present? + end + def use_traversal_ids_for_ancestors? return false unless use_traversal_ids? return false unless Feature.enabled?(:use_traversal_ids_for_ancestors, root_ancestor, default_enabled: :yaml) @@ -107,6 +114,12 @@ module Namespaces self_and_descendants.where.not(id: id) end + def self_and_hierarchy + return super unless use_traversal_ids_for_self_and_hierarchy? + + self_and_descendants.or(ancestors) + end + def ancestors(hierarchy_order: nil) return super unless use_traversal_ids_for_ancestors? diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb index 0dfb7320461..9f0f49e729c 100644 --- a/app/models/namespaces/traversal/linear_scopes.rb +++ b/app/models/namespaces/traversal/linear_scopes.rb @@ -22,19 +22,28 @@ module Namespaces unscoped.where(id: root_ids) end - def self_and_ancestors(include_self: true, hierarchy_order: nil) + def self_and_ancestors(include_self: true, upto: nil, 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) + unless include_self + records = records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id])) + end + + if upto + records = records.where.not(id: unscoped.where(id: upto).select('unnest(traversal_ids)')) end + + records end def self_and_ancestor_ids(include_self: true) @@ -150,6 +159,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 diff --git a/app/models/namespaces/traversal/recursive_scopes.rb b/app/models/namespaces/traversal/recursive_scopes.rb index 925d9b8bb0c..583c53f8221 100644 --- a/app/models/namespaces/traversal/recursive_scopes.rb +++ b/app/models/namespaces/traversal/recursive_scopes.rb @@ -17,8 +17,8 @@ module Namespaces .where(namespaces: { parent_id: nil }) end - def self_and_ancestors(include_self: true, hierarchy_order: nil) - records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(hierarchy_order: hierarchy_order) + def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil) + records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(upto: upto, hierarchy_order: hierarchy_order) if include_self records |