defmodule Snowflex do alias Snowflex.Worker @moduledoc """ The client interface for connecting to the Snowflake data warehouse. The main entry point to this module is `Snowflex.sql_query`. This function takes a string containing a SQL query and returns a list of maps (one per row). NOTE: due to the way the Erlang ODBC works, all values comeback as strings. You will need to cast values appropriately. """ @timeout :timer.seconds(60) @type query_param :: {:odbc.odbc_data_type(), list(:odbc.value())} @type sql_data :: list(%{optional(String.t()) => String.t()}) @spec sql_query(String.t(), integer() | :infinity) :: sql_data() def sql_query(query, timeout \\ @timeout) do case :poolboy.transaction( :snowflex_pool, fn pid -> Worker.sql_query(pid, query, timeout) end, timeout ) do {:ok, results} -> process_results(results) err -> err end end @spec param_query(String.t(), list(query_param()), integer() | :infinity) :: sql_data() def param_query(query, params \\ [], timeout \\ @timeout) do case :poolboy.transaction( :snowflex_pool, fn pid -> Worker.param_query(pid, query, params, timeout) end, timeout ) do {:ok, results} -> process_results(results) err -> err end end # Helpers defp process_results(data) when is_list(data) do Enum.map(data, &process_results(&1)) end defp process_results({:selected, headers, rows}) do bin_headers = headers |> Enum.map(fn header -> header |> to_string() |> String.downcase() end) |> Enum.with_index() Enum.map(rows, fn row -> Enum.reduce(bin_headers, %{}, fn {col, index}, map -> data = row |> elem(index) |> to_string_if_charlist() Map.put(map, col, data) end) end) end defp to_string_if_charlist(data) when is_list(data), do: to_string(data) defp to_string_if_charlist(data), do: data end