-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
defmodule Firebirdex do | ||
alias Firebirdex.Query | ||
|
||
@type conn :: DBConnection.conn() | ||
|
||
@spec start_link(keyword()) :: {:ok, pid()} | {:error, Firebirdex.Error.t()} | ||
def start_link(opts) do | ||
DBConnection.start_link(Firebirdex.Protocol, opts) | ||
end | ||
|
||
@spec query(conn, iodata, list, keyword()) :: | ||
{:ok, Firebirdex.Result.t()} | {:error, Firebirdex.Error.t()} | ||
def query(conn, statement, params \\ [], opts \\ []) do | ||
query = %Query{name: "", statement: statement} | ||
case DBConnection.prepare_execute(conn, query, params, opts) do | ||
{:ok, _, result} -> | ||
{:ok, result} | ||
{:error, _} = error -> | ||
error | ||
end | ||
|
||
end | ||
|
||
@spec query!(conn, iodata, list, keyword()) :: Firebirdex.Result.t() | ||
def query!(conn, statement, params \\ [], opts \\ []) do | ||
case query(conn, statement, params, opts) do | ||
{:ok, result} -> result | ||
{:error, exception} -> raise exception | ||
end | ||
end | ||
|
||
@spec prepare(conn(), iodata(), iodata(), keyword()) :: | ||
{:ok, Firebirdex.Query.t()} | {:error, Firebirdex.Error.t()} | ||
def prepare(conn, name, statement, opts \\ []) do | ||
query = %Firebirdex.Query{name: name, statement: statement, ref: make_ref()} | ||
DBConnection.prepare(conn, query, opts) | ||
end | ||
|
||
@spec prepare!(conn(), iodata(), iodata(), keyword()) :: Firebirdex.Query.t() | ||
def prepare!(conn, name, statement, opts \\ []) do | ||
query = %Firebirdex.Query{name: name, statement: statement, ref: make_ref()} | ||
DBConnection.prepare!(conn, query, opts) | ||
end | ||
|
||
@spec prepare_execute(conn, iodata, iodata, list, keyword()) :: | ||
{:ok, Firebirdex.Query.t(), Firebirdex.Result.t()} | {:error, Firebirdex.Error.t()} | ||
def prepare_execute(conn, name, statement, params \\ [], opts \\ []) | ||
when is_binary(statement) or is_list(statement) do | ||
query = %Firebirdex.Query{name: name, statement: statement, ref: make_ref()} | ||
DBConnection.prepare_execute(conn, query, params, opts) | ||
end | ||
|
||
@spec prepare_execute!(conn, iodata, iodata, list, keyword()) :: | ||
{Firebirdex.Query.t(), Firebirdex.Result.t()} | ||
def prepare_execute!(conn, name, statement, params \\ [], opts \\ []) | ||
when is_binary(statement) or is_list(statement) do | ||
query = %Firebirdex.Query{name: name, statement: statement, ref: make_ref()} | ||
DBConnection.prepare_execute!(conn, query, params, opts) | ||
end | ||
|
||
@spec execute(conn(), Firebirdex.Query.t(), list(), keyword()) :: | ||
{:ok, Firebirdex.Query.t(), Firebirdex.Result.t()} | {:error, Firebirdex.Error.t()} | ||
defdelegate execute(conn, query, params \\ [], opts \\ []), to: DBConnection | ||
|
||
@spec execute!(conn(), Firebirdex.Query.t(), list(), keyword()) :: Firebirdex.Result.t() | ||
defdelegate execute!(conn, query, params \\ [], opts \\ []), to: DBConnection | ||
|
||
@spec close(conn(), Firebirdex.Query.t(), keyword()) :: :ok | ||
def close(conn, %Firebirdex.Query{} = query, opts \\ []) do | ||
case DBConnection.close(conn, query, opts) do | ||
{:ok, _} -> | ||
:ok | ||
|
||
{:error, _} = error -> | ||
error | ||
end | ||
end | ||
|
||
@spec transaction(conn, (DBConnection.t() -> result), keyword()) :: | ||
{:ok, result} | {:error, any} | ||
when result: var | ||
defdelegate transaction(conn, fun, opts \\ []), to: DBConnection | ||
|
||
@spec rollback(DBConnection.t(), any()) :: no_return() | ||
defdelegate rollback(conn, reason), to: DBConnection | ||
|
||
@spec child_spec(keyword()) :: Supervisor.child_spec() | ||
def child_spec(opts) do | ||
DBConnection.child_spec(Firebirdex.Protocol, opts) | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
defmodule Firebirdex.Error do | ||
defexception [ | ||
:message, | ||
:statement | ||
] | ||
|
||
@type t :: %__MODULE__{ | ||
message: String.t(), | ||
statement: iodata() | nil | ||
} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
defmodule Firebirdex.Protocol do | ||
@moduledoc false | ||
require Record | ||
use DBConnection | ||
Record.defrecord :conn, Record.extract(:conn, from_lib: "efirebirdsql/include/efirebirdsql.hrl") | ||
Record.defrecord :stmt, Record.extract(:stmt, from_lib: "efirebirdsql/include/efirebirdsql.hrl") | ||
|
||
alias Firebirdex.{Query, Result} | ||
|
||
defstruct [ | ||
:conn, | ||
transaction_status: :idle | ||
] | ||
|
||
@impl true | ||
def connect(opts) do | ||
hostname = to_charlist(opts[:hostname]) | ||
username = to_charlist(opts[:username]) | ||
password = to_charlist(opts[:password]) | ||
database = to_charlist(opts[:database]) | ||
case :efirebirdsql_protocol.connect(hostname, username, password, database, opts) do | ||
{:ok, conn} -> | ||
{:ok, conn} = :efirebirdsql_protocol.begin_transaction(false, conn) | ||
{:ok, %__MODULE__{conn: conn}} | ||
{:error, message, _conn} -> | ||
{:error, %Firebirdex.Error{message: message}} | ||
end | ||
end | ||
|
||
@impl true | ||
def disconnect(_reason, state) do | ||
:efirebirdsql_protocol.close(state.conn) | ||
:ok | ||
end | ||
|
||
@impl true | ||
def ping(state) do | ||
# TODO | ||
{:ok, state} | ||
end | ||
|
||
@impl true | ||
def checkout(state) do | ||
{:ok, state} | ||
end | ||
|
||
@impl true | ||
def checkin(state) do | ||
{:ok, state} | ||
end | ||
|
||
@impl true | ||
def handle_prepare(%Query{} = query, _opts, state) do | ||
{:ok, conn, stmt} = :efirebirdsql_protocol.allocate_statement(state.conn) | ||
{:ok, conn, stmt} = :efirebirdsql_protocol.prepare_statement( | ||
query.statement, conn, stmt) | ||
{:ok, %Query{query | stmt: stmt}, %__MODULE__{state | conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_execute(%Query{} = query, params, _opts, state) do | ||
{:ok, conn, stmt} = :efirebirdsql_protocol.execute(state.conn, query.stmt, params) | ||
{:ok, rows, conn} = :efirebirdsql_protocol.fetchall(conn, stmt) | ||
columns = :efirebirdsql_protocol.column_names(stmt) | ||
{:ok, query, %Result{rows: rows, columns: columns}, %__MODULE__{state | conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_close(_query, _opts, state) do | ||
{:ok, conn} = :efirebirdsql_protocol.close(state.conn) | ||
{:ok, %__MODULE__{conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_status(_opts, status) do | ||
{status.transaction_status, status} | ||
end | ||
|
||
@impl true | ||
def handle_declare(query, _params, _opt, state) do | ||
{:ok, query, query.stmt, state} | ||
end | ||
|
||
@impl true | ||
def handle_begin(_opts, %{transaction_status: _status} = state) do | ||
{:ok, conn} = :efirebirdsql_protocol.begin_transaction(false, state.conn) | ||
{:ok, %__MODULE__{conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_commit(_opts, state) do | ||
{:ok, conn} = :efirebirdsql_protocol.commit(state.conn) | ||
{:ok, %__MODULE__{conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_rollback(_opts, state) do | ||
{:ok, conn} = :efirebirdsql_protocol.rollback(state.conn) | ||
{:ok, %__MODULE__{conn: conn}} | ||
end | ||
|
||
@impl true | ||
def handle_fetch(_query, %Result{} = result, _opts, s) do | ||
{:halt, result, s} | ||
end | ||
|
||
@impl true | ||
def handle_deallocate(query, _cursor, _opts, state) do | ||
{:ok, conn} = :efirebirdsql_protocol.free_statement(state.conn, query.stmt, :drop) | ||
{:ok, %__MODULE__{state | conn: conn}} | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
defmodule Firebirdex.Query do | ||
require Record | ||
Record.defrecord :stmt, Record.extract(:stmt, from_lib: "efirebirdsql/include/efirebirdsql.hrl") | ||
|
||
@type t :: %__MODULE__{ | ||
ref: reference() | nil, | ||
name: iodata(), | ||
statement: iodata(), | ||
stmt: tuple() | ||
} | ||
|
||
defstruct name: "", | ||
ref: nil, | ||
stmt: nil, | ||
statement: nil, | ||
stmt: nil | ||
|
||
defimpl DBConnection.Query do | ||
def parse(query, _opts) do | ||
query | ||
end | ||
|
||
def describe(query, _opts) do | ||
query | ||
end | ||
|
||
def encode(%{stmt: nil} = query, _params, _opts) do | ||
raise ArgumentError, "query #{inspect(query)} has not been prepared" | ||
end | ||
|
||
def encode(_query, params, _opts) do | ||
params | ||
end | ||
|
||
def decode(_query, result, _opts) do | ||
result | ||
end | ||
end | ||
|
||
defimpl String.Chars do | ||
def to_string(%{statement: statement}) do | ||
IO.iodata_to_binary(statement) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
defmodule Firebirdex.Result do | ||
@type t :: %__MODULE__{ | ||
columns: [String.t()] | nil, | ||
rows: [[term()]] | nil | ||
} | ||
|
||
defstruct [ | ||
:columns, | ||
:rows | ||
] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
defmodule Firebirdex.Mixfile do | ||
use Mix.Project | ||
|
||
@version "0.0.1" | ||
|
||
def project() do | ||
[ | ||
app: :firebirdex, | ||
version: @version, | ||
elixir: "~> 1.4", | ||
name: "Firebirdex", | ||
description: "Firebird driver for Elixir", | ||
source_url: "https://github.com/nakagami/firebirdex", | ||
package: package(), | ||
deps: deps() | ||
] | ||
end | ||
|
||
def application() do | ||
[ | ||
extra_applications: [:logger], | ||
] | ||
end | ||
|
||
defp package do | ||
[ | ||
maintainers: ["Hajime Nakagami"], | ||
licenses: ["Apache 2.0"], | ||
links: %{"Github" => "https://github.com/nakagami/firebirdex"} | ||
] | ||
end | ||
|
||
defp deps() do | ||
[ | ||
{:db_connection, "~> 2.0"}, | ||
{:decimal, "~> 1.6"}, | ||
{:efirebirdsql, "~> 0.5.2"}, | ||
] | ||
end | ||
|
||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
%{ | ||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, | ||
"db_connection": {:hex, :db_connection, "2.0.2", "440c05518b0bdca0469dafaf45403597430448c1281def14ef9ccaa41833ea1e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, | ||
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"}, | ||
"efirebirdsql": {:hex, :efirebirdsql, "0.5.2", "53dbc9f4fe332d6daa8a972ec7ec89b2947ccc6107762d22aa4b19ef94827436", [:rebar3], [], "hexpm"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
defmodule FirebirdexTest do | ||
use ExUnit.Case, async: true | ||
|
||
@opts TestHelpers.opts() | ||
|
||
describe "connect" do | ||
opts = @opts | ||
{:ok, conn} = Firebirdex.start_link(opts) | ||
{:ok, %Firebirdex.Result{}} = Firebirdex.query(conn, "SELECT 1 AS C FROM RDB$RELATIONS", []) | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
ExUnit.start() | ||
|
||
defmodule TestHelpers do | ||
def opts() do | ||
database = :lists.flatten( | ||
:io_lib.format("/tmp/~p.fdb", [:erlang.system_time()])) | ||
[ | ||
hostname: "localhost", | ||
username: "sysdba", | ||
password: "masterkey", | ||
database: database, | ||
createdb: true, | ||
show_sensitive_data_on_connection_error: true | ||
] | ||
end | ||
end |