From 9cc2d958684b7c567018f9d4ae6567b96f36199a Mon Sep 17 00:00:00 2001 From: Maas-Maarten Zeeman Date: Sat, 5 Nov 2011 23:43:53 +0100 Subject: [PATCH] Exploring a pluggable generic sql database api --- src/gen_db.erl | 41 +++++++++++++++++++++++++++++++++++++++++ src/sqlite.erl | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/gen_db.erl create mode 100644 src/sqlite.erl diff --git a/src/gen_db.erl b/src/gen_db.erl new file mode 100644 index 0000000..779a0af --- /dev/null +++ b/src/gen_db.erl @@ -0,0 +1,41 @@ +%% +%% Generic database interface. Sort of... +%% +%% Inspired by python's db api +%% + +-module(gen_db). + +-export([behaviour_info/1]). + +-export([open/2, execute/2, execute/3, close/1]). + +behaviour_info(callbacks) -> + [{handle_open, 1}, + {handle_execute, 3}, + {handle_close, 1}]; +behaviour_info(_Other) -> + undefined. + +-record(gen_connection, {module, connection}). + +%% @doc Open a connection to a new database +%% +open(Module, ModuleArgs) -> + {ok, Conn} = Module:handle_open(ModuleArgs), + {ok, #gen_connection{module=Module, connection=Conn}}. + +%% @doc Prepare and execute a database operation +%% +execute(Operation, Connection) -> + execute(Operation, [], Connection). + +%% @doc Prepare and execute a database operation +%% +execute(Operation, Args, #gen_connection{module=Module, connection=Connection}) -> + Module:handle_execute(Operation, Args, Connection). + +%% @doc Close a database connection. +%% +close(#gen_connection{module=Module, connection=Connection}) -> + Module:handle_close(Connection). diff --git a/src/sqlite.erl b/src/sqlite.erl new file mode 100644 index 0000000..6e9eaec --- /dev/null +++ b/src/sqlite.erl @@ -0,0 +1,45 @@ +-module(sqlite). + +-behaviour(gen_db). + +-export([handle_open/1, handle_execute/3, handle_close/1]). + +%% @doc Open a database connection +%% +handle_open(DatabaseName) -> + esqlite:open(DatabaseName). + +%% @doc Execute a query and return the results +%% +handle_execute(Operation, Args, Connection) -> + {ok, Stmt} = esqlite:prepare(Connection, Operation), + ok = esqlite:bind(Stmt, Args), + Answer = execute(Stmt), + %% TODO Finalize the statement. + Answer. + + +%% @doc Close the connection +%% +handle_close(Connection) -> + esqlite:close(Connection). + +%% @doc +%% +execute(Statement) -> + execute(Statement, [], 0). + +%% @doc +%% +execute(_Statement, _Acc, Tries) when Tries > 5 -> + throw(too_many_tries); +execute(Statement, Acc, Tries) -> + case esqlite:step(Statement) of + '$done' -> + lists:reverse(Acc); + '$busy' -> + timer:sleep(100), %% This is a bit lame... there is a trigger api for this. + execute(Statement, Acc, Tries + 1); + V when is_tuple(V) -> + execute(Statement, [V | Acc], 0) + end.