Current section

Files

Jump to
snowflex lib snowflex.ex
Raw

lib/snowflex.ex

defmodule Snowflex do
@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.
"""
alias Ecto.Changeset
alias Snowflex.Worker
@type query_param :: {:odbc.odbc_data_type(), list(:odbc.value())}
@type sql_data :: list(%{optional(String.t()) => String.t()})
@spec sql_query(atom(), String.t(), timeout()) ::
sql_data() | {:error, term}
def sql_query(pool_name, query, timeout) do
case :poolboy.transaction(
pool_name,
&Worker.sql_query(&1, query, timeout),
timeout
) do
{:ok, results} -> process_results(results)
err -> err
end
end
@spec param_query(atom(), String.t(), list(query_param()), timeout()) ::
sql_data() | {:error, term}
def param_query(pool_name, query, params \\ [], timeout) do
case :poolboy.transaction(
pool_name,
&Worker.param_query(&1, query, params, timeout),
timeout
) do
{:ok, results} -> process_results(results)
err -> err
end
end
def cast_results(data, schema) do
Enum.map(data, &cast_row(&1, schema))
end
def int_param(val), do: {:sql_integer, val}
def string_param(val, length \\ 250), do: {{:sql_varchar, length}, val}
# 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
defp cast_row(row, schema) do
schema
|> struct()
|> Changeset.cast(row, schema.__schema__(:fields))
|> Changeset.apply_changes()
end
end