Packages
jamdb_oracle
0.2.6
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
retired
0.5.1
retired
0.5.0
retired
0.4.12
retired
0.4.11
retired
0.4.10
retired
0.4.9
retired
0.4.8
retired
0.4.7
retired
0.4.6
retired
0.4.5
retired
0.4.4
retired
0.4.3
retired
0.4.2
retired
0.4.1
retired
0.4.0
retired
0.3.10
retired
0.3.9
retired
0.3.8
retired
0.3.7
retired
0.3.6
retired
0.3.5
retired
0.3.4
retired
0.3.3
retired
0.3.2
retired
0.3.1
retired
0.3.0
retired
0.2.7
retired
0.2.6
retired
0.2.5
retired
0.2.4
retired
0.2.3
retired
0.2.2
retired
0.2.1
retired
0.2.0
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
0.0.10
retired
0.0.9
retired
0.0.8
retired
0.0.7
retired
0.0.6
retired
0.0.5
retired
0.0.4
retired
0.0.3
retired
0.0.2
retired
0.0.1
retired
Erlang driver and Ecto adapter for Oracle
Retired package: Deprecated - deprecated
Current section
Files
Jump to
Current section
Files
lib/jamdb_oracle.ex
defmodule Jamdb.Oracle do
@moduledoc """
Adapter module for Oracle. `DBConnection` behaviour implementation.
It uses `jamdb_oracle` for communicating to the database.
"""
@behaviour DBConnection
defstruct [:pid, :mode, :cursors]
@doc """
Starts and links to a database connection process.
See [`Ecto.Adapters.Jamdb.Oracle`](Ecto.Adapters.Jamdb.Oracle.html#module-connection-options).
By default the `DBConnection` starts a pool with a single connection.
The ping interval to validate an idle connection can be given with
the `:idle_timeout` option.
"""
@spec start_link(opts :: Keyword.t) ::
{:ok, pid()} | {:error, any()}
def start_link(opts) do
DBConnection.start_link(Jamdb.Oracle, opts)
end
@doc """
Runs the SQL statement.
See `DBConnection.prepare_execute/4`.
In case of success, it must return an `:ok` tuple containing
a map with at least two keys:
* `:num_rows` - the number of rows affected
* `:rows` - the result set as a list
"""
@spec query(conn :: any(), sql :: any(), params :: any()) ::
{:ok, any()} | {:error | :disconnect, any()}
def query(conn, sql, params \\ [])
def query(pid, sql, params) when is_pid(pid), do: query(%{pid: pid}, sql, params)
def query(%{pid: pid}, sql, params) do
case :jamdb_oracle.sql_query(pid, stmt(sql, params)) do
{:ok, [{:result_set, columns, _, rows}]} ->
{:ok, %{num_rows: length(rows), rows: rows, columns: columns}}
{:ok, [{:fetched_rows, _, _, _} = result]} -> {:cont, result}
{:ok, [{:proc_result, 0, rows}]} -> {:ok, %{num_rows: length(rows), rows: rows}}
{:ok, [{:proc_result, _, _}] = result} -> {:error, result}
{:ok, [{:affected_rows, num_rows}]} -> {:ok, %{num_rows: num_rows, rows: nil}}
{:ok, result} -> {:ok, result}
{:error, _, err} -> {:disconnect, err}
end
end
defp stmt({:fetch, sql, params}, _), do: {:fetch, sql, params}
defp stmt({:fetch, cursor, row_format, last_row}, _), do: {:fetch, cursor, row_format, last_row}
defp stmt(sql, params), do: {sql, params}
@doc false
def connect(opts) do
database = Keyword.fetch!(opts, :database) |> to_charlist
env = if( hd(database) == ?:, do: [sid: tl(database)], else: [service_name: database] )
|> Keyword.put_new(:host, Keyword.fetch!(opts, :hostname) |> to_charlist)
|> Keyword.put_new(:port, Keyword.fetch!(opts, :port))
|> Keyword.put_new(:user, Keyword.fetch!(opts, :username) |> to_charlist)
|> Keyword.put_new(:password, Keyword.fetch!(opts, :password) |> to_charlist)
|> Keyword.put_new(:timeout, Keyword.fetch!(opts, :timeout))
params = if( Keyword.has_key?(opts, :parameters) == true,
do: opts[:parameters], else: [] )
sock_opts = if( Keyword.has_key?(opts, :socket_options) == true,
do: [socket_options: opts[:socket_options]], else: [] )
case :jamdb_oracle.start_link(sock_opts ++ params ++ env) do
{:ok, pid} -> {:ok, %Jamdb.Oracle{pid: pid, mode: :idle}}
{:error, [{:proc_result, _, msg}]} -> {:error, error!(msg)}
{:error, err} -> {:error, error!(err)}
end
end
@doc false
def disconnect(_err, %{pid: pid}) do
:jamdb_oracle.stop(pid)
end
@doc false
def handle_execute(query, params, opts, s) do
%Jamdb.Oracle.Query{statement: statement} = query
returning = Enum.map(Keyword.get(opts, :out, []), fn elem -> {:out, elem} end)
case query(s, statement |> to_charlist, Enum.concat(params, returning)) do
{:ok, result} -> {:ok, result, s}
{:error, [{:proc_result, _, msg}]} -> {:error, error!(msg), s}
{:error, err} -> {:error, error!(err), s}
{:disconnect, err} -> {:disconnect, error!(err), s}
end
end
@doc false
def handle_prepare(query, _opts, s) do
{:ok, query, s}
end
@doc false
def handle_begin(opts, %{mode: mode} = s) do
case Keyword.get(opts, :mode, :transaction) do
:transaction when mode == :idle ->
statement = "SAVEPOINT tran"
handle_transaction(statement, opts, %{s | mode: :transaction})
:savepoint when mode == :transaction ->
statement = "SAVEPOINT " <> Keyword.get(opts, :name, "svpt")
handle_transaction(statement, opts, %{s | mode: :transaction})
end
end
@doc false
def handle_commit(opts, %{mode: mode} = s) do
case Keyword.get(opts, :mode, :transaction) do
:transaction when mode == :transaction ->
statement = "COMMIT"
handle_transaction(statement, opts, %{s | mode: :idle})
:savepoint when mode == :transaction ->
{:ok, [], %{s | mode: :transaction}}
end
end
@doc false
def handle_rollback(opts, %{mode: mode} = s) do
case Keyword.get(opts, :mode, :transaction) do
:transaction when mode in [:transaction, :error] ->
statement = "ROLLBACK TO tran"
handle_transaction(statement, opts, %{s | mode: :idle})
:savepoint when mode in [:transaction, :error] ->
statement = "ROLLBACK TO " <> Keyword.get(opts, :name, "svpt")
handle_transaction(statement, opts, %{s | mode: :transaction})
end
end
defp handle_transaction(statement, _opts, s) do
case query(s, statement |> to_charlist) do
{:ok, result} -> {:ok, result, s}
{:error, err} -> {:error, error!(err), s}
{:disconnect, err} -> {:disconnect, error!(err), s}
end
end
@doc false
def handle_declare(_query, params, _opts, s) do
{:ok, %{params: params}, s}
end
@doc false
def handle_first(query, %{params: params}, _opts, %{cursors: nil} = s) do
%Jamdb.Oracle.Query{statement: statement} = query
case query(s, {:fetch, statement |> to_charlist, params}) do
{:cont, {_, cursor, row_format, rows}} ->
cursors = %{cursor: cursor, row_format: row_format, last_row: List.last(rows)}
{:ok, %{num_rows: length(rows), rows: rows}, %{s | cursors: cursors}}
{:ok, result} ->
{:deallocate, result, s}
{:error, err} -> {:error, error!(err), s}
{:disconnect, err} -> {:disconnect, error!(err), s}
end
end
@doc false
def handle_next(_query, _cursor, _opts, %{cursors: cursors} = s) do
%{cursor: cursor, row_format: row_format, last_row: last_row} = cursors
case query(s, {:fetch, cursor, row_format, last_row}) do
{:cont, {_, _, _, rows}} ->
rows = tl(rows)
{:ok, %{num_rows: length(rows), rows: rows},
%{s | cursors: %{cursors | last_row: List.last(rows)}}}
{:ok, %{rows: rows} = result} ->
rows = tl(rows)
{:deallocate, %{result | num_rows: length(rows), rows: rows}, s}
{:error, err} -> {:error, error!(err), s}
{:disconnect, err} -> {:disconnect, error!(err), s}
end
end
@doc false
def handle_deallocate(_query, _cursor, _opts, s) do
{:ok, nil, %{s | cursors: nil}}
end
@doc false
def handle_close(_query, _opts, s) do
{:ok, nil, s}
end
@doc false
def handle_info(_msg, s) do
{:ok, s}
end
@doc false
def checkin(s) do
{:ok, s}
end
@doc false
def checkout(s) do
case query(s, 'SESSION') do
{:ok, _} -> {:ok, s}
{:error, err} -> {:disconnect, error!(err), s}
end
end
@doc false
def ping(%{mode: :idle} = s) do
case query(s, 'PING') do
{:ok, _} -> {:ok, s}
{:error, err} -> {:disconnect, error!(err), s}
end
end
def ping(%{mode: :transaction} = s) do
{:ok, s}
end
defp error!(msg) do
DBConnection.ConnectionError.exception("#{inspect msg}")
end
@doc """
Returns the configured JSON library.
To customize the JSON library, include the following in your `config/config.exs`:
config :jamdb_oracle, :json_library, SomeJSONModule
Defaults to [`Jason`](https://hexdocs.pm/jason)
"""
@spec json_library() :: module()
def json_library() do
Application.get_env(:jamdb_oracle, :json_library, Jason)
end
end
defimpl DBConnection.Query, for: Jamdb.Oracle.Query do
def parse(query, _), do: query
def describe(query, _), do: query
def decode(_, %{rows: []} = result, _), do: result
def decode(_, %{rows: rows} = result, opts) when rows != nil,
do: %{result | rows: Enum.map(rows, fn row -> decode(row, opts[:decode_mapper]) end)}
def decode(_, result, _), do: result
defp decode(row, nil), do: Enum.map(row, fn elem -> decode(elem) end)
defp decode(row, mapper), do: mapper.(decode(row, nil))
defp decode(:null), do: nil
defp decode({elem}) when is_number(elem), do: elem
defp decode({date, time}) when is_tuple(date), do: to_naive({date, time})
defp decode({date, time, _}) when is_tuple(date), do: to_utc({date, time})
defp decode(elem) when is_list(elem), do: to_binary(elem)
defp decode(elem), do: elem
def encode(_, [], _), do: []
def encode(_, params, opts) do
charset = if( Keyword.has_key?(opts, :charset) == true,
do: Enum.member?(["al16utf16","ja16euc","zhs16gbk","zht16big5","zht16mswin950"],
opts[:charset]), else: false )
Enum.map(params, fn elem -> encode(elem, charset) end)
end
defp encode(nil, _), do: :null
defp encode(true, _), do: "1"
defp encode(false, _), do: "0"
defp encode(%Decimal{} = decimal, _), do: Decimal.to_float(decimal)
defp encode(%DateTime{} = datetime, _), do: NaiveDateTime.to_erl(DateTime.to_naive(datetime))
defp encode(%NaiveDateTime{} = naive, _), do: NaiveDateTime.to_erl(naive)
defp encode(%Ecto.Query.Tagged{value: elem}, _), do: elem
defp encode(elem, false) when is_binary(elem), do: elem |> to_charlist
defp encode(elem, charset) when is_map(elem),
do: encode(Jamdb.Oracle.json_library().encode!(elem), charset)
defp encode(elem, _), do: elem
defp expr(list) when is_list(list) do
Enum.map(list, fn
:null -> nil
elem -> elem
end)
end
defp to_binary(list) when is_list(list) do
try do
:binary.list_to_bin(list)
rescue
ArgumentError ->
Enum.map(expr(list), fn
elem when is_list(elem) -> expr(elem)
other -> other
end) |> Enum.join
end
end
defp to_naive({date, {hour, min, sec}}) when is_integer(sec),
do: NaiveDateTime.from_erl!({date, {hour, min, sec}})
defp to_naive({date, {hour, min, sec}}),
do: NaiveDateTime.from_erl!({date, {hour, min, trunc(sec)}}, parse_sec(sec))
defp to_utc({date, time}),
do: DateTime.from_naive!(to_naive({date, time}), "Etc/UTC")
defp parse_sec(sec),
do: {trunc((sec - trunc(sec)) * 1000000) , 6}
end