defmodule Mix.Tasks.Ecto.Ch.Schema do @moduledoc """ Shows an Ecto schema hint for a table. Examples: $ mix ecto.ch.schema $ mix ecto.ch.schema system.numbers """ use Mix.Task alias Ch.Connection, as: Conn def run([]) do IO.puts(@moduledoc) end def run([source]) do [_, table] = source = case String.split(source, ".") do [_table] = source -> [nil | source] [_prefix, _table] = source -> source end {where, params} = case source do [nil, table] -> {"where table = {table:String}", %{"table" => table}} [database, table] -> {"where database = {database:String} and table = {table:String}", %{"database" => database, "table" => table}} end statement = "select name, type from system.columns " <> where conn = connect(_config = []) case query(conn, statement, params) do {%Ch.Result{rows: [_ | _] = rows}, _conn} -> schema = [ """ @primary_key false schema "#{table}" do """, Enum.map(rows, fn [name, type] -> [" ", build_field(name, type), ?\n] end), "end" ] IO.puts(schema) {%Ch.Result{rows: []}, _conn} -> raise "table not found" end end defp connect(config) do case Conn.connect(config) do {:ok, conn} -> conn {:error, reason} -> raise reason end end defp query(conn, statement, params, opts \\ []) do query = Ch.Query.build(statement) params = DBConnection.Query.encode(query, params, opts) case Conn.handle_execute(query, params, opts, conn) do {:ok, query, result, conn} -> {DBConnection.Query.decode(query, result, opts), conn} {:disconnect, reason, _conn} -> raise reason {:error, reason, _conn} -> raise reason end end @doc false def build_field(name, type) do type = Ch.Types.decode(type) ecto_type = ecto_type(type) clickhouse_type = clickhouse_type(type) case {ecto_type, clickhouse_type} do {ecto_type, nil} -> ~s[field :"#{name}", #{inspect(ecto_type)}] {ecto_type, clickhouse_type} -> ~s[field :"#{name}", #{inspect(ecto_type)}, type: "#{clickhouse_type}"] end end defp ecto_type({:array, type}), do: {:array, ecto_type(type)} defp ecto_type(type) when type in [:string, :date, :boolean], do: type defp ecto_type(:uuid), do: Ecto.UUID defp ecto_type(_type), do: Ch defp clickhouse_type({:array, type}), do: clickhouse_type(type) defp clickhouse_type(type) when type in [:uuid, :string, :date, :boolean], do: nil defp clickhouse_type(type), do: Ch.Types.encode(type) end