lib/avalanche/bindings.ex

defmodule Avalanche.Bindings do
  @moduledoc """
  Prepares bindings from list of values.

  https://docs.snowflake.com/en/developer-guide/sql-api/submitting-requests.html#using-bind-variables-in-a-statement
  """

  @doc """
  Encodes the given values into an indexed map of bindings.

  Examples:

      iex> values = [123, 1.23, "uno, dos, tres", false, ~N[2015-01-14 13:00:07], DateTime.from_unix!(1_464_096_368), ~D[2015-01-15]]
      iex> Avalanche.Bindings.encode_params(values)
      %{
        "1" => %{type: "FIXED", value: "123"},
        "2" => %{type: "REAL", value: "1.23"},
        "3" => %{type: "TEXT", value: "uno, dos, tres"},
        "4" => %{type: "BOOLEAN", value: false},
        "5" => %{type: "TEXT", value: "2015-01-14T13:00:07"},
        "6" => %{type: "TEXT", value: "2016-05-24T13:26:08Z"},
        "7" => %{type: "TEXT", value: "2015-01-15"}
      }
  """
  def encode_params(values) when is_list(values) do
    values
    |> Enum.with_index(fn value, index -> {index + 1, value} end)
    |> Enum.reduce(%{}, fn {index, value}, acc ->
      Map.put(acc, to_string(index), encode(value))
    end)
  end

  defp encode(value) when is_integer(value) do
    %{type: "FIXED", value: to_string(value)}
  end

  defp encode(value) when is_float(value) do
    %{type: "REAL", value: to_string(value)}
  end

  defp encode(value) when is_binary(value) do
    %{type: "TEXT", value: value}
  end

  defp encode(value) when is_boolean(value) do
    %{type: "BOOLEAN", value: value}
  end

  defp encode(%NaiveDateTime{} = value) do
    %{type: "TEXT", value: NaiveDateTime.to_iso8601(value)}
  end

  defp encode(%DateTime{} = value) do
    %{type: "TEXT", value: DateTime.to_iso8601(value)}
  end

  defp encode(%Date{} = value) do
    %{type: "TEXT", value: Date.to_iso8601(value)}
  end

  defp encode(any) do
    msg = """
    Unable to encode value: #{inspect(any)}

    The value above will likely generate incorrect and unexpected results even
    if the SQL it generated was valid.

    If you believe there is an issue, please report it here:
    https://github.com/HGInsights/avalanche/issues
    """

    raise Avalanche.Error.application_error(msg)
  end
end