Working version of sqlite
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
OTP_DIR=/opt/local/lib/erlang/lib/erl_interface-3.5.5.3
|
OTP_DIR=/opt/local/lib/erlang/lib/erl_interface-3.5.5.3
|
||||||
LIB=-lsqlite3 -lerl_interface -lei
|
LIB=-lsqlite3 -lerl_interface -lei
|
||||||
FLAGS=-I$(OTP_DIR)/include -L$(OTP_DIR)/lib
|
FLAGS=-I$(OTP_DIR)/include -L$(OTP_DIR)/lib
|
||||||
GCC=/opt/local/bin/gcc-apple-4.2
|
#GCC=/opt/local/bin/gcc-apple-4.2
|
||||||
|
GCC=/usr/bin/gcc
|
||||||
|
|
||||||
all: erl_comm.c sqlite_port.c
|
all: erl_comm.c sqlite_port.c
|
||||||
$(GCC) -Wall -o sqlite_port erl_comm.c sqlite_port.c $(FLAGS) $(LIB)
|
$(GCC) -Wall -o sqlite_port erl_comm.c sqlite_port.c $(FLAGS) $(LIB)
|
||||||
|
|||||||
@@ -7,18 +7,61 @@
|
|||||||
#include "erl_interface.h"
|
#include "erl_interface.h"
|
||||||
#include "ei.h"
|
#include "ei.h"
|
||||||
|
|
||||||
|
static FILE *log;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ETERM *result;
|
||||||
|
} Result;
|
||||||
|
|
||||||
|
static Result r;
|
||||||
|
|
||||||
|
void send_error(char *err_msg);
|
||||||
|
void send_result();
|
||||||
|
void send_ok();
|
||||||
|
|
||||||
|
static int callback(void *notUsed, int argc, char **argv, char **azColName) {
|
||||||
|
ETERM **record_list;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (r.result == 0) {
|
||||||
|
r.result = erl_mk_empty_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
record_list = malloc(argc * sizeof(ETERM *));
|
||||||
|
|
||||||
|
fprintf(log, "runs %d\n", argc);
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
fprintf(log, "%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
|
||||||
|
if (argv[i]) {
|
||||||
|
record_list[i] = erl_mk_string(argv[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
record_list[i] = erl_mk_empty_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(log, "\n");
|
||||||
|
fflush(log);
|
||||||
|
|
||||||
|
r.result = erl_cons(erl_mk_tuple(record_list, argc), r.result);
|
||||||
|
|
||||||
|
free(record_list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
ETERM *tuplep, *intp;
|
ETERM *tuplep;
|
||||||
ETERM *fnp, *argp;
|
ETERM *fnp, *argp;
|
||||||
int res;
|
byte buf[1024];
|
||||||
byte buf[100];
|
|
||||||
long allocated, freed;
|
|
||||||
|
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
char *zErrMsg = 0;
|
char *zErrMsg = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
log = fopen("/tmp/sqlite_port.log", "a+");
|
||||||
|
fprintf(log, "******start log (%s)******\n", argv[1]);
|
||||||
|
fflush(log);
|
||||||
|
|
||||||
rc = sqlite3_open(argv[1], &db);
|
rc = sqlite3_open(argv[1], &db);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
@@ -33,42 +76,89 @@ int main(int argc, char **argv)
|
|||||||
argp = erl_element(2, tuplep);
|
argp = erl_element(2, tuplep);
|
||||||
|
|
||||||
if (strncmp((const char *)ERL_ATOM_PTR(fnp), "close", 5) == 0) {
|
if (strncmp((const char *)ERL_ATOM_PTR(fnp), "close", 5) == 0) {
|
||||||
|
fprintf(log, "closing sqlite3_close\n");
|
||||||
|
fflush(log);
|
||||||
|
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
break;
|
break;
|
||||||
} else if (strncmp((const char *)ERL_ATOM_PTR(fnp), "bar", 3) == 0) {
|
} else if (strncmp((const char *)ERL_ATOM_PTR(fnp), "sql_exec", 8) == 0) {
|
||||||
res = ERL_INT_VALUE(argp);
|
fprintf(log, "calling sqlite3_exec %s\n", erl_iolist_to_string(argp));
|
||||||
|
|
||||||
|
r.result = 0;
|
||||||
|
|
||||||
|
rc = sqlite3_exec(db, erl_iolist_to_string(argp), callback, 0, &zErrMsg);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
send_error(zErrMsg);
|
||||||
|
sqlite3_free(zErrMsg);
|
||||||
|
}
|
||||||
|
else if (r.result != 0) {
|
||||||
|
send_result();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not an error and no results. still need to return something
|
||||||
|
send_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
intp = erl_mk_int(res);
|
fflush(log);
|
||||||
erl_encode(intp, buf);
|
}
|
||||||
write_cmd(buf, erl_term_len(intp));
|
|
||||||
|
|
||||||
erl_free_compound(tuplep);
|
erl_free_compound(tuplep);
|
||||||
erl_free_term(fnp);
|
erl_free_term(fnp);
|
||||||
erl_free_term(argp);
|
erl_free_term(argp);
|
||||||
erl_free_term(intp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(log, "******end log******\n");
|
||||||
|
fclose(log);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void send_error(char *err_msg) {
|
||||||
|
ETERM **tup_list;
|
||||||
|
ETERM *result;
|
||||||
|
byte buf[1024];
|
||||||
|
|
||||||
|
tup_list = malloc(sizeof(ETERM *) * 2);
|
||||||
|
|
||||||
static int callback(void *notUsed, int argc, char **argv, char **azColName) {
|
tup_list[0] = erl_mk_atom("sql_error");
|
||||||
int i;
|
tup_list[1] = erl_mk_string(err_msg);
|
||||||
printf("runs %d\n", argc);
|
result = erl_mk_tuple(tup_list, 2);
|
||||||
for (i = 1; i < argc; i++) {
|
|
||||||
printf("%s = %s\n", azColName[i], argv[1] ? argv[1] : "NULL");
|
bzero(buf, 1024);
|
||||||
}
|
erl_encode(result, buf);
|
||||||
printf("\n");
|
write_cmd(buf, erl_term_len(result));
|
||||||
return 0;
|
|
||||||
|
fprintf(log, "SQL Error: %s\n", err_msg);
|
||||||
|
|
||||||
|
erl_free_term(tup_list[0]);
|
||||||
|
erl_free_term(tup_list[1]);
|
||||||
|
free(tup_list);
|
||||||
|
erl_free_compound(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void send_result() {
|
||||||
rc = sqlite3_exec(db, "SELECT * from t1;", callback, 0, &zErrMsg);
|
byte buf[2048];
|
||||||
if (rc != SQLITE_OK) {
|
|
||||||
fprintf(stderr, "SQL Error: %s\n", zErrMsg);
|
bzero(buf, 2048);
|
||||||
sqlite3_free(zErrMsg);
|
erl_encode(r.result, buf);
|
||||||
|
write_cmd(buf, erl_term_len(r.result));
|
||||||
|
|
||||||
|
fprintf(log, "returning at len %d\n", erl_term_len(r.result));
|
||||||
|
|
||||||
|
erl_free_compound(r.result);
|
||||||
|
r.result = 0;
|
||||||
}
|
}
|
||||||
sqlite3_close(db);
|
|
||||||
*/
|
void send_ok() {
|
||||||
|
ETERM *result;
|
||||||
|
byte buf[48];
|
||||||
|
|
||||||
|
result = erl_mk_atom("ok");
|
||||||
|
bzero(buf, 48);
|
||||||
|
erl_encode(result, buf);
|
||||||
|
write_cmd(buf, erl_term_len(result));
|
||||||
|
|
||||||
|
fprintf(log, "returning ok at len %d\n", erl_term_len(result));
|
||||||
|
|
||||||
|
erl_free_term(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/1]).
|
-export([open/1, open/2]).
|
||||||
|
-export([start_link/1, start_link/2]).
|
||||||
-export([stop/1, close/1]).
|
-export([stop/1, close/1]).
|
||||||
-export([sql_exec/2]).
|
-export([sql_exec/2]).
|
||||||
|
|
||||||
@@ -36,16 +37,23 @@ start_link(Db) ->
|
|||||||
%% {db, DbFile :: String()}
|
%% {db, DbFile :: String()}
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
start_link(Db, Options) ->
|
start_link(Db, Options) ->
|
||||||
gen_server:start_link({local, Db}, ?MODULE, [], Options).
|
gen_server:start_link({local, Db}, ?MODULE, Options, []).
|
||||||
|
|
||||||
|
open(Db) ->
|
||||||
|
?MODULE:start_link(Db).
|
||||||
|
|
||||||
|
open(Db, Options) ->
|
||||||
|
?MODULE:start_link(Db, Options).
|
||||||
|
|
||||||
close(Db) ->
|
close(Db) ->
|
||||||
gen_server:call(Db, close).
|
gen_server:call(Db, close).
|
||||||
|
|
||||||
stop(Db) ->
|
stop(Db) ->
|
||||||
close(Db).
|
?MODULE:close(Db).
|
||||||
|
|
||||||
sql_exec(Db, SQL) ->
|
sql_exec(Db, SQL) ->
|
||||||
gen_server:call(Db, {sql_exec, SQL}).
|
gen_server:call(Db, {sql_exec, SQL}).
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
@@ -58,6 +66,83 @@ sql_exec(Db, SQL) ->
|
|||||||
%% Description: Initiates the server
|
%% Description: Initiates the server
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
init(Options) ->
|
init(Options) ->
|
||||||
Dbase = proplist:get_value(db, Options),
|
Dbase = proplists:get_value(db, Options),
|
||||||
Port = open_port({spawn, create_cmd(Dbase)
|
Port = open_port({spawn, create_cmd(Dbase)}, [{packet, 2}, binary]),
|
||||||
|
{ok, #state{port = Port, ops = Options}}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||||
|
%% {reply, Reply, State, Timeout} |
|
||||||
|
%% {noreply, State} |
|
||||||
|
%% {noreply, State, Timeout} |
|
||||||
|
%% {stop, Reason, Reply, State} |
|
||||||
|
%% {stop, Reason, State}
|
||||||
|
%% Description: Handling call messages
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
handle_call(close, _From, State) ->
|
||||||
|
Reply = ok,
|
||||||
|
{stop, normal, Reply, State};
|
||||||
|
handle_call({sql_exec, SQL}, _From, #state{port = Port} = State) ->
|
||||||
|
port_command(Port, term_to_binary({sql_exec, SQL})),
|
||||||
|
Reply = receive
|
||||||
|
{Port, {data, Data}} when is_binary(Data) ->
|
||||||
|
List = binary_to_term(Data),
|
||||||
|
if is_list(List) ->
|
||||||
|
lists:reverse(List);
|
||||||
|
true -> List
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
Reply = ok,
|
||||||
|
{reply, Reply, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||||
|
%% {noreply, State, Timeout} |
|
||||||
|
%% {stop, Reason, State}
|
||||||
|
%% Description: Handling cast messages
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||||
|
%% {noreply, State, Timeout} |
|
||||||
|
%% {stop, Reason, State}
|
||||||
|
%% Description: Handling all non call/cast messages
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Function: terminate(Reason, State) -> void()
|
||||||
|
%% Description: This function is called by a gen_server when it is about to
|
||||||
|
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||||
|
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||||
|
%% The return value is ignored.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
terminate(normal, #state{port = Port}) ->
|
||||||
|
port_command(Port, term_to_binary({close, nop})),
|
||||||
|
port_close(Port),
|
||||||
|
ok;
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||||
|
%% Description: Convert process state when code is changed
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%%% Internal functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
create_cmd(Dbase) ->
|
||||||
"sqlite_port " ++ Dbase.
|
"sqlite_port " ++ Dbase.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user