defmodule ExSQL.Ecto.Query do @moduledoc false defstruct statement: nil, name: nil, command: nil @type t :: %__MODULE__{ statement: iodata(), name: String.t() | nil, command: atom() | nil } def build(opts) do statement = Keyword.fetch!(opts, :statement) %__MODULE__{ statement: statement, name: Keyword.get(opts, :name), command: Keyword.get(opts, :command) || extract_command(statement) } end @commands %{ "alter" => :alter, "analyze" => :analyze, "attach" => :attach, "begin" => :begin, "commit" => :commit, "create" => :create, "delete" => :delete, "detach" => :detach, "drop" => :drop, "explain" => :explain, "insert" => :insert, "pragma" => :pragma, "reindex" => :reindex, "release" => :release, "replace" => :replace, "rollback" => :rollback, "savepoint" => :savepoint, "select" => :select, "update" => :update, "vacuum" => :vacuum, "values" => :select, "with" => :select } defp extract_command(statement) do statement |> IO.iodata_to_binary() |> String.trim_leading() |> String.split(~r/\s+/, parts: 2) |> List.first() |> case do nil -> nil command -> Map.get(@commands, String.downcase(command)) end end def encode_params(params) when is_list(params), do: Enum.map(params, &encode_param/1) def encode_params(params) when is_map(params) do Map.new(params, fn {key, value} -> {key, encode_param(value)} end) end def encode_params(params), do: params defp encode_param(true), do: 1 defp encode_param(false), do: 0 defp encode_param(%Date{} = value), do: Date.to_iso8601(value) defp encode_param(%NaiveDateTime{} = value), do: NaiveDateTime.to_iso8601(value) defp encode_param(%DateTime{} = value), do: DateTime.to_iso8601(value) defp encode_param(%Time{} = value), do: Time.to_iso8601(value) defp encode_param(value) when is_list(value) or is_map(value), do: Jason.encode!(value) defp encode_param(value), do: value defimpl DBConnection.Query do def parse(query, _opts), do: query def describe(query, _opts), do: query def encode(_query, params, _opts), do: ExSQL.Ecto.Query.encode_params(params) def decode(_query, result, _opts), do: result end defimpl String.Chars do def to_string(query), do: IO.iodata_to_binary(query.statement) end end