Current section

Files

Jump to
posterize lib posterize.ex
Raw

lib/posterize.ex

defmodule :posterize do
@moduledoc """
an erlang API for postgrex, a postgres client
"""
@typedoc """
a connection process name, pid or reference
a connection reference is used when making multiple requests to the same
connection, see `transaction/3`
"""
@type conn :: DBConnection.conn | {:sbroker, DBConnection.conn} | {:poolboy, DBConnection.conn}
@pool_timeout 5000
@timeout 15000
### PUBLIC API ###
@doc """
start the connection process and connect to postgres
## options
* `hostname` - server hostname (default: PGHOST env variable, then localhost);
* `port` - server port (default: PGPORT env variable, then 5432);
* `database` - database (required);
* `username` - username (default: PGUSER env variable, then USER env var);
* `password` - user password (default PGPASSWORD env variable);
* `parameters` - proplist of connection parameters;
* `timeout` - socket receive timeout when idle in milliseconds (default: `#{@timeout}`);
* `connect_timeout` - socket connect timeout in milliseconds (default: `#{@timeout}`);
* `handshake_timeout` - connection handshake timeout in milliseconds (default: `#{@timeout}`);
* `ssl` - set to `true` if ssl should be used (default: `false`);
* `ssl_opts` - a list of ssl options, see ssl docs;
* `socket_options` - options to be given to the underlying socket;
* `prepare` - how to prepare queries, either `named` to use named queries
or `unnamed` to force unnamed queries. use `unnamed` when using a proxy like
pgbouncer that doesn't support named queries (default: `named`);
* `transactions` - set to `strict` to error on unexpected transaction state,
otherwise set to `naive` (default: `naive`);
`Postgrex` (and posterize) use the `DBConnection` framework and support all
`DBConnection` options. see `DBConnection` for more information
## examples
1> {ok, Pid} = posterize:start_link([{database, <<"postgres">>}])
{ok, <0.62.0>}
"""
@spec start_link(Keyword.t) :: {:ok, pid} | {:error, Postgrex.Error.t | term}
def start_link(opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
# use erlang friendly type module unless the user specifies another
opts = Keyword.put_new(opts, :types, :posterize_typemodule)
Postgrex.start_link(opts)
end
@doc """
runs an (extended) query and returns the result as `{ok, Result}`
or `{error, Error}` if there was a database error. parameters can be
set in the query as `$1` embedded in the query string. parameters are given as
a list of erlang values. see the README for information on how posterize
encodes and decodes erlang values by default. see `Postgrex.Result` for the
result data
## options
* `pool_timeout` - time to wait in the queue for the connection
(default: `#{@pool_timeout}`)
* `queue` - whether to wait for connection in a queue (default: `true`);
* `timeout` - query request timeout (default: `#{@timeout}`);
* `decode_mapper` - fun to map each row in the result to a term after
decoding, (default: `fun(X) -> X end`);
* `mode` - set to `savepoint` to use a savepoint to rollback to before the
query on error, otherwise set to `transaction` (default: `transaction`);
## examples
posterize:query(Conn, "CREATE TABLE posts (id serial, title text)", []).
posterize:query(Conn, "INSERT INTO posts (title) VALUES ('my title')", []).
posterize:query(Conn, "SELECT title FROM posts", []).
posterize:query(Conn, "SELECT id FROM posts WHERE title like $1", [<<"%my%">>]).
posterize:query(Conn, "COPY posts TO STDOUT", []).
"""
@spec query(conn, iodata, list, Keyword.t) :: {:ok, Postgrex.Result.t} | {:error, Postgrex.Error.t}
def query(conn, statement, params, opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
try do
Postgrex.query(conn, statement, params, opts)
rescue
# this is most likely an exception in an encoder/decoder
e in ArgumentError -> { :error, e }
end
end
def query(conn, statement, params), do: query(conn, statement, params, [])
@doc """
prepares an (extended) query and returns a prepared query as `{ok, Query}`
or `{error, Error}` if there was an error. execute the returned query with
`execute/3,4`
## options
* `pool_timeout` - time to wait in the queue for the connection
(default: `#{@pool_timeout}`)
* `queue` - whether to wait for connection in a queue (default: `true`);
* `timeout` - query request timeout (default: `#{@timeout}`);
* `mode` - set to `savepoint` to use a savepoint to rollback to before the
query on error, otherwise set to `transaction` (default: `transaction`);
## examples
{ok, Query} = posterize:prepare(Conn, "name", "CREATE TABLE posts (id serial, title text)").
"""
@spec prepare(conn, iodata, iodata, Keyword.t) :: {:ok, Postgrex.Query.t} | {:error, Postgrex.Error.t}
def prepare(conn, name, statement, opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
Postgrex.prepare(conn, name, statement, opts)
end
def prepare(conn, name, statement), do: prepare(conn, name, statement, [])
@doc """
runs an (extended) prepared query and returns the result as `{ok, Result}`
or `{error, Error}` if there was an error
## options
* `pool_timeout` - time to wait in the queue for the connection
(default: `#{@pool_timeout}`)
* `queue` - whether to wait for connection in a queue (default: `true`);
* `timeout` - query request timeout (default: `#{@timeout}`);
* `decode_mapper` - fun to map each row in the result to a term after
decoding, (default: `fun(X) -> X end`);
* `mode` - set to `savepoint` to use a savepoint to rollback to before the
query on error, otherwise set to `transaction` (default: `transaction`);
## examples
{ok, Query} = posterize:prepare(Conn, "", "CREATE TABLE posts (id serial, title text)"),
{ok, Result} = posterize:execute(Conn, Query, []).
{ok, Query} = posterize:prepare(Conn, "", "SELECT id FROM posts WHERE title like $1"),
{ok, Result} = posterize:execute(Conn, Query, [<<"%my%">>]).
"""
@spec execute(conn, Postgrex.Query.t, list, Keyword.t) ::
{:ok, Postgrex.Result.t} | {:error, Postgrex.Error.t}
def execute(conn, query, params, opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
try do
Postgrex.execute(conn, query, params, opts)
rescue
# this is most likely an exception in an encoder/decoder
e in ArgumentError -> { :error, e }
end
end
def execute(conn, query, params), do: execute(conn, query, params, [])
@doc """
closes an (extended) prepared query and returns `ok` or `{error, Error`}
if there was an error. closing a query releases any resources held by
postgresql for a prepared query with that name
## options
* `pool_timeout` - time to wait in the queue for the connection
(default: `#{@pool_timeout}`)
* `queue` - whether to wait for connection in a queue (default: `true`);
* `timeout` - query request timeout (default: `#{@timeout}`);
* `mode` - set to `savepoint` to use a savepoint to rollback to before the
query on error, otherwise set to `transaction` (default: `transaction`);
## examples
{ok, Query} = posterize:prepare(Conn, "", "CREATE TABLE posts (id serial, title text)"),
ok = posterize:close(Conn, Query).
"""
@spec close(conn, Postgrex.Query.t, Keyword.t) :: :ok | {:error, Postgrex.Error.t}
def close(conn, query, opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
Postgrex.close(conn, query, opts)
end
def close(conn, query), do: close(conn, query, [])
@doc """
acquire a lock on a connection and run a series of requests inside a
transaction. the result of the transaction fun is return inside an `ok`
tuple: `{ok, Result}`
to use the locked connection call the request with the connection
reference passed as the single argument to the `Fun`. if the
connection disconnects all future calls using that connection
reference will fail
`rollback/2` rolls back the transaction and causes the function to
return `{error, Error}`
`transaction/3` can be nested multiple times if the connection
reference is used to start a nested transaction. the top level
transaction function is the actual transaction
## options
* `pool_timeout` - time to wait in the queue for the connection
(default: `#{@pool_timeout}`)
* `queue` - whether to wait for connection in a queue (default: `true`);
* `timeout` - transaction timeout (default: `#{@timeout}`);
* `mode` - set to `savepoint` to use a savepoint to rollback to before the
query on error, otherwise set to `transaction` (default: `transaction`);
the `timeout` is for the duration of the transaction and all nested
transactions and requests. this timeout overrides timeouts set by internal
transactions and requests. the `pool` and `mode` will be used for all
requests inside the transaction function
## example
Fun = fun(Conn) -> posterize:query(Conn, "", "SELECT title FROM posts", []) end,
{ok, Result} = posterize:transaction(Conn, Fun).
"""
@spec transaction(conn, ((DBConnection.t) -> result), Keyword.t) ::
{:ok, result} | {:error, any} when result: var
def transaction(conn, fun, opts) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
Postgrex.transaction(conn, fun, opts)
end
def transaction(conn, fun), do: transaction(conn, fun, [])
@doc """
rollback a transaction, does not return
aborts the current transaction fun. if inside multiple `transaction/3`
functions, bubbles up to the top level
## example
{error, oops} = posterize:transaction(Conn, fun(Conn) ->
posterize:rollback(Conn, bar),
io:format("never reaches here!~n", [])
end).
"""
@spec rollback(DBConnection.t, any) :: no_return()
defdelegate rollback(conn, any), to: DBConnection
@doc """
returns a cached map of connection parameters.
## options
* `timeout` - Call timeout (default: `#{@timeout}`)
"""
@spec parameters(pid, Keyword.t) :: map
def parameters(pid, opts \\ []) do
# use sbroker as the default pool unless the user specifies another
opts = Keyword.put_new(opts, :pool, DBConnection.Sojourn)
Postgrex.parameters(pid, opts)
end
end