Skip to main content

lib/quack_db/query.ex

defmodule QuackDB.Query do
  @moduledoc """
  Query metadata used by the DBConnection driver.
  """

  @type t :: %__MODULE__{
          statement: iodata(),
          columns: [String.t()] | nil,
          result_types: [term()] | nil,
          result_uuid: integer() | nil,
          operation: term() | nil
        }

  defstruct [:statement, :columns, :result_types, :result_uuid, :operation]
end

defimpl DBConnection.Query, for: QuackDB.Query do
  def parse(%QuackDB.Query{operation: nil} = query, _options) do
    %{query | statement: IO.iodata_to_binary(query.statement)}
  end

  def parse(%QuackDB.Query{} = query, _options), do: query

  def describe(%QuackDB.Query{} = query, _options), do: query

  def encode(_query, params, _options), do: params

  def decode(_query, %QuackDB.Result{rows: nil} = result, _options), do: result

  def decode(_query, %QuackDB.Result{rows: rows} = result, options) do
    %{result | rows: decode_rows(rows, Keyword.get(options, :decode_mapper))}
  end

  defp decode_rows(rows, nil), do: rows
  defp decode_rows(rows, mapper), do: Enum.map(rows, mapper)
end

defimpl Inspect, for: QuackDB.Query do
  import Inspect.Algebra

  def inspect(query, opts) do
    fields = [
      statement: QuackDB.Inspect.truncate(query.statement),
      columns: query.columns,
      result_uuid: query.result_uuid
    ]

    concat(QuackDB.Inspect.container("QuackDB.Query", fields, opts))
  end
end

defimpl String.Chars, for: QuackDB.Query do
  def to_string(%QuackDB.Query{statement: statement}) do
    IO.iodata_to_binary(statement)
  end
end