Skip to main content

lib/ex_sql/ast/select.ex

defmodule ExSQL.AST.Select do
  @moduledoc """
  A `SELECT` statement.

  `columns` is a list of `:star`, `{:qualified_star, table}`, or
  `{expr, alias | nil}` tuples. Expressions are tagged tuples as produced by
  `ExSQL.Parser` — see that module for the expression grammar.

  `from` is a join tree:

    * `nil` — no FROM clause
    * `{:table, name, alias | nil}`
    * `{:subquery, %Select{}, alias | nil}`
    * `{:join, type, left, right, constraint}` where `type` is
      `%{natural: boolean, left: boolean}`, `left` is a join tree, `right`
      a single source, and `constraint` is `nil`, `{:on, expr}`, or
      `{:using, [name]}`. Comma joins, `CROSS JOIN`, and `INNER JOIN` all
      parse to `left: false` — they differ only as planner hints in SQLite.
  """

  defstruct columns: [],
            from: nil,
            where: nil,
            group_by: [],
            having: nil,
            windows: %{},
            order_by: [],
            limit: nil,
            offset: nil,
            distinct: false

  @type expr :: ExSQL.Parser.expr()

  @type join_type :: %{natural: boolean(), left: boolean()}
  @type constraint :: nil | {:on, expr()} | {:using, [String.t()]}
  @type source ::
          {:table, String.t(), String.t() | nil}
          | {:subquery, t(), String.t() | nil}
          | {:join, join_type(), source(), source(), constraint()}

  @type t :: %__MODULE__{
          columns: [:star | {:qualified_star, String.t()} | {expr(), String.t() | nil}],
          from: source() | nil,
          where: expr() | nil,
          group_by: [expr()],
          having: expr() | nil,
          windows: %{String.t() => map()},
          order_by: [{expr(), :asc | :desc}],
          limit: expr() | nil,
          offset: expr() | nil,
          distinct: boolean()
        }
end