defmodule Selecto.DB.PostgreSQL do @moduledoc """ PostgreSQL adapter for Selecto. """ @behaviour Selecto.DB.Adapter @impl true def name, do: :postgresql @impl true def connect({:pool, _} = pool_ref), do: {:ok, pool_ref} def connect(connection) when is_pid(connection) or is_atom(connection), do: {:ok, connection} def connect(opts) when is_map(opts), do: connect(Map.to_list(opts)) def connect(opts) when is_list(opts) do case Postgrex.start_link(opts) do {:ok, conn} -> {:ok, conn} {:error, reason} -> {:error, reason} end end def connect(other), do: {:error, {:invalid_connection_options, other}} @impl true def execute({:pool, pool_ref}, query, params, opts) do case Selecto.ConnectionPool.execute(pool_ref, normalize_query(query), params, opts) do {:ok, result} -> {:ok, normalize_result(result)} {:error, reason} -> {:error, reason} end end def execute(connection, query, params, opts) when is_pid(connection) or is_atom(connection) do case Postgrex.query(connection, normalize_query(query), params, opts) do {:ok, result} -> {:ok, normalize_result(result)} {:error, reason} -> {:error, reason} end end def execute(connection, _query, _params, _opts), do: {:error, {:invalid_connection, connection}} @impl true def placeholder(index), do: ["$", Integer.to_string(index)] @impl true def quote_identifier(identifier) when is_binary(identifier) do escaped = String.replace(identifier, "\"", "\"\"") "\"#{escaped}\"" end def quote_identifier(identifier), do: identifier |> to_string() |> quote_identifier() @impl true def supports?(feature) do feature in [:cte, :jsonb, :array_ops, :returning, :window_functions, :lateral_join, :prefix] end defp normalize_query(query) when is_binary(query), do: query defp normalize_query(query), do: IO.iodata_to_binary(query) defp normalize_result(%{rows: rows, columns: columns}) do %{ rows: rows || [], columns: Enum.map(columns || [], &to_string/1) } end end