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