lib/archeometer/query/subquery.ex

defmodule Archeometer.Query.Subquery do
  @moduledoc """
  This module represents SQL subqueries. This subqueries partially mimics a
  query. At the moment, only a `select` field is allowed.
  """
  defstruct source: %{},
            tables: %{},
            aliases: %{},
            bindings: %{},
            distinct: false,
            limit: nil,
            select: [],
            subquery?: true

  @doc """
  Creates a new subquery from with the given query as parent.
  """
  def new(q, select: ast) do
    %__MODULE__{
      source: q.source,
      tables: q.tables,
      aliases: q.aliases,
      bindings: q.bindings,
      select: [[ast]]
    }
  end

  @subquery_keywords [:exists]

  @doc """
  Currently, the only way to use sub queries is through an SQL keyword with
  support for them. Of course, initially those keywords have regular ASTs as
  arguments.

  Here, we replace the AST of the arguments of those keywords with subqueries.
  """
  def escape_subqueries(q, ast) do
    Macro.prewalk(ast, fn
      {op, meta, [arg]} when op in @subquery_keywords ->
        {op, meta, [new(q, select: arg)]}

      other ->
        other
    end)
  end
end