52 lines
1.4 KiB
Ruby
52 lines
1.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module SQL
|
|
# Class for easily building CTE statements.
|
|
#
|
|
# Example:
|
|
#
|
|
# cte = CTE.new(:my_cte_name)
|
|
# ns = Arel::Table.new(:namespaces)
|
|
#
|
|
# cte << Namespace.
|
|
# where(ns[:parent_id].eq(some_namespace_id))
|
|
#
|
|
# Namespace
|
|
# with(cte.to_arel).
|
|
# from(cte.alias_to(ns))
|
|
class CTE
|
|
attr_reader :table, :query
|
|
|
|
# name - The name of the CTE as a String or Symbol.
|
|
def initialize(name, query)
|
|
@table = Arel::Table.new(name)
|
|
@query = query
|
|
end
|
|
|
|
# Returns the Arel relation for this CTE.
|
|
def to_arel
|
|
sql = Arel::Nodes::SqlLiteral.new("(#{query.to_sql})")
|
|
|
|
Arel::Nodes::As.new(table, sql)
|
|
end
|
|
|
|
# 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.
|
|
def alias_to(alias_table)
|
|
Arel::Nodes::As.new(table, alias_table)
|
|
end
|
|
|
|
# Applies the CTE to the given relation, returning a new one that will
|
|
# query from it.
|
|
def apply_to(relation)
|
|
relation.except(:where)
|
|
.with(to_arel)
|
|
.from(alias_to(relation.model.arel_table))
|
|
end
|
|
end
|
|
end
|
|
end
|