Class: Gitlab::SQL::RecursiveCTE

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/sql/recursive_cte.rb

Overview

Class for easily building recursive CTE statements.

Example:

cte = RecursiveCTE.new(:my_cte_name)
ns = Arel::Table.new(:namespaces)

cte << Namespace.
  where(ns[:parent_id].eq(some_namespace_id))

cte << Namespace.
  from([ns, cte.table]).
  where(ns[:parent_id].eq(cte.table[:id]))

Namespace.with.
  recursive(cte.to_arel).
  from(cte.alias_to(ns))

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, union_args: {}) ⇒ RecursiveCTE

name - The name of the CTE as a String or Symbol. union_args - The arguments supplied to Gitlab::SQL::Union class when building inner recursive query


27
28
29
30
31
# File 'lib/gitlab/sql/recursive_cte.rb', line 27

def initialize(name, union_args: {})
  @table = Arel::Table.new(name)
  @queries = []
  @union_args = union_args
end

Instance Attribute Details

#tableObject (readonly)

Returns the value of attribute table.


23
24
25
# File 'lib/gitlab/sql/recursive_cte.rb', line 23

def table
  @table
end

Instance Method Details

#<<(relation) ⇒ Object

Adds a query to the body of the CTE.

relation - The relation object to add to the body of the CTE.


36
37
38
# File 'lib/gitlab/sql/recursive_cte.rb', line 36

def <<(relation)
  @queries << relation
end

#alias_to(alias_table) ⇒ Object

Returns an “AS” statement that aliases the CTE name as the given table name. This allows one to trick ActiveRecord into thinking it's selecting from an actual table, when in reality it's selecting from a CTE.

alias_table - The Arel table to use as the alias.


52
53
54
# File 'lib/gitlab/sql/recursive_cte.rb', line 52

def alias_to(alias_table)
  Arel::Nodes::As.new(table, Arel::Table.new(alias_table.name.tr('.', '_')))
end

#apply_to(relation) ⇒ Object

Applies the CTE to the given relation, returning a new one that will query from it.


58
59
60
61
62
63
# File 'lib/gitlab/sql/recursive_cte.rb', line 58

def apply_to(relation)
  relation.except(:where)
    .with
    .recursive(to_arel)
    .from(alias_to(relation.model.arel_table))
end

#to_arelObject

Returns the Arel relation for this CTE.


41
42
43
44
45
# File 'lib/gitlab/sql/recursive_cte.rb', line 41

def to_arel
  sql = Arel::Nodes::SqlLiteral.new(Union.new(@queries, **@union_args).to_sql)

  Arel::Nodes::As.new(table, Arel::Nodes::Grouping.new(sql))
end