Current section

Files

Jump to
ecto lib ecto adapters postgres.ex
Raw

lib/ecto/adapters/postgres.ex

defmodule Ecto.Adapters.Postgres do
@moduledoc """
Adapter module for PostgreSQL.
It uses `postgrex` for communicating to the database
and manages a connection pool with `poolboy`.
## Features
* Full query support (including joins, preloads and associations)
* Support for transactions
* Support for data migrations
* Support for ecto.create and ecto.drop operations
* Support for transactional tests via `Ecto.Adapters.SQL`
## Options
Postgres options split in different categories described
below. All options should be given via the repository
configuration.
### Compile time options
Those options should be set in the config file and require
recompilation in order to make an effect.
* `:adapter` - The adapter name, in this case, `Ecto.Adapters.Postgres`
* `:timeout` - The default timeout to use on queries, defaults to `5000`
### Connection options
* `:hostname` - Server hostname
* `:port` - Server port (default: 5432)
* `:username` - Username
* `:password` - User password
* `:parameters` - Keyword list of connection parameters
* `:ssl` - Set to true if ssl should be used (default: false)
* `:ssl_opts` - A list of ssl options, see Erlang's `ssl` docs
* `:connect_timeout` - The timeout for establishing new connections, defaults to `5000`
* `:extensions` - Specify extensions to the postgres adapter
### Pool options
* `:size` - The number of connections to keep in the pool
* `:lazy` - If false all connections will be started immediately on Repo startup (default: true)
* `:max_overflow` - The maximum overflow of connections (see poolboy docs)
### Storage options
* `:encoding` - the database encoding (default: "UTF8")
* `:template` - the template to create the database from
* `:lc_collate` - the collation order
* `:lc_ctype` - the character classification
"""
# Inherit all behaviour from Ecto.Adapters.SQL
use Ecto.Adapters.SQL, :postgrex
# And provide a custom storage implementation
@behaviour Ecto.Adapter.Storage
## Storage API
@doc false
def storage_up(opts) do
database = Keyword.fetch!(opts, :database)
encoding = Keyword.get(opts, :encoding, "UTF8")
extra = ""
if template = Keyword.get(opts, :template) do
extra = extra <> " TEMPLATE=#{template}"
end
if lc_collate = Keyword.get(opts, :lc_collate) do
extra = extra <> " LC_COLLATE='#{lc_collate}'"
end
if lc_ctype = Keyword.get(opts, :lc_ctype) do
extra = extra <> " LC_CTYPE='#{lc_ctype}'"
end
{output, status} =
run_with_psql opts, "CREATE DATABASE " <> database <>
" ENCODING='#{encoding}'" <> extra
cond do
status == 0 -> :ok
String.contains?(output, "already exists") -> {:error, :already_up}
true -> {:error, output}
end
end
@doc false
def storage_down(opts) do
{output, status} = run_with_psql(opts, "DROP DATABASE #{opts[:database]}")
cond do
status == 0 -> :ok
String.contains?(output, "does not exist") -> {:error, :already_down}
true -> {:error, output}
end
end
defp run_with_psql(database, sql_command) do
unless System.find_executable("psql") do
raise "could not find executable `psql` in path, " <>
"please guarantee it is available before running ecto commands"
end
env =
if password = database[:password] do
[{"PGPASSWORD", password}]
else
[]
end
args = []
if username = database[:username] do
args = ["-U", username|args]
end
if port = database[:port] do
args = ["-p", to_string(port)|args]
end
host = database[:hostname] || System.get_env("PGHOST") || "localhost"
args = args ++ ["--quiet", "--host", host, "-d", "template1", "-c", sql_command]
System.cmd("psql", args, env: env, stderr_to_stdout: true)
end
@doc false
def supports_ddl_transaction? do
true
end
end