Current section

Files

Jump to
monet lib transaction.ex
Raw

lib/transaction.ex

defmodule Monet.Transaction do
@moduledoc """
Created via `Monet.transaction/1` or `Monet.transaction/2`.
"""
require Record
alias Monet.{Connection, Error, Prepared, Reader, Writer}
Record.defrecord(:transaction, conn: nil, ref: nil, pool_name: nil)
def new(conn) do
transaction(
conn: conn,
ref: make_ref(),
pool_name: Connection.pool_name(conn)
)
end
@doc """
Executes the query using the specific transaction
"""
def query(tx, name_or_sql, args \\ nil)
def query(tx, name, args) when is_atom(name) do
ref = transaction(tx, :ref)
pool_name = transaction(tx, :pool_name)
case :ets.lookup(pool_name, {ref, name}) do
[{_, prepared}] -> Prepared.exec(prepared, args)
_ -> {:error, Error.new(:driver, "unknown prepared statement", name)}
end
end
def query(tx, sql, args) do
# Connection.query returns {result, conn} for the pool
# we only care about the result
tx
|> transaction(:conn)
|> Connection.query(sql, args)
|> elem(0)
end
@doc """
Commits the transaction
"""
def commit(tx) do
conn = transaction(tx, :conn)
with :ok <- Writer.query(conn, "commit"),
{:ok, "&4 t\n"} <- Reader.message(conn) # make sure auto-commit is turned back on
do
:ok
else
{:ok, data} -> {:error, Error.new(:driver, "invalid commit response", data)}
err -> err
end
end
@doc """
Rollsback the transaction
"""
def rollback(tx) do
conn = transaction(tx, :conn)
with :ok <- Writer.query(conn, "rollback"),
{:ok, "&4 t\n"} <- Reader.message(conn)
do
:ok
else
{:ok, data} -> {:error, Error.new(:driver, "invalid rollback response", data)}
err -> err
end
end
@doc """
Prepares the statement and stores it in the transaction cache. See
`Monet.prepare/3`.
"""
def prepare(tx, name, sql) do
with {:ok, prepared} <- Prepared.new(transaction(tx, :conn), sql) do
ref = transaction(tx, :ref)
pool_name = transaction(tx, :pool_name)
:ets.insert(pool_name, {{ref, name}, prepared})
:ok
end
end
@doc """
Deallocates any prepared statements that were allocated as part of this
transaction
"""
def close(tx) do
ref = transaction(tx, :ref)
pool_name = transaction(tx, :pool_name)
Enum.each(:ets.match(pool_name, {{ref, :_}, :'$1'}), fn [prepared] ->
Prepared.close(prepared)
end)
:ets.match_delete(pool_name, {{ref, :_}, :'$1'})
end
end