Support script execution (no error messages currently)
This commit is contained in:
@@ -162,6 +162,9 @@ static int control(
|
|||||||
case CMD_PREPARED_COLUMNS:
|
case CMD_PREPARED_COLUMNS:
|
||||||
prepared_columns(driver_data, buf, len);
|
prepared_columns(driver_data, buf, len);
|
||||||
break;
|
break;
|
||||||
|
case CMD_SQL_EXEC_SCRIPT:
|
||||||
|
sql_exec_script(driver_data, buf, len);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
unknown(driver_data, buf, len);
|
unknown(driver_data, buf, len);
|
||||||
}
|
}
|
||||||
@@ -193,7 +196,7 @@ static inline int output_error(
|
|||||||
int term_count;
|
int term_count;
|
||||||
return_error(drv, error_code, error, &dataset, &term_count);
|
return_error(drv, error_code, error, &dataset, &term_count);
|
||||||
driver_output_term(drv->port, dataset, term_count);
|
driver_output_term(drv->port, dataset, term_count);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int output_db_error(sqlite3_drv_t *drv) {
|
static inline int output_db_error(sqlite3_drv_t *drv) {
|
||||||
@@ -259,6 +262,32 @@ static int sql_exec(sqlite3_drv_t *drv, char *command, int command_size) {
|
|||||||
return sql_exec_statement(drv, statement);
|
return sql_exec_statement(drv, statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sql_exec_script(sqlite3_drv_t *drv, char *command, int command_size) {
|
||||||
|
int result;
|
||||||
|
const char *rest = command;
|
||||||
|
const char *end = command + command_size;
|
||||||
|
sqlite3_stmt *statement;
|
||||||
|
|
||||||
|
// printf("SQL: %.*s\n", command_size, command);
|
||||||
|
|
||||||
|
while (rest < end) {
|
||||||
|
result = sqlite3_prepare_v2(drv->db, command, end - command, &statement, &rest);
|
||||||
|
command = (char *) rest; // won't actually mutate!
|
||||||
|
if (result != SQLITE_OK) {
|
||||||
|
return output_db_error(drv);
|
||||||
|
} else if (statement == NULL) {
|
||||||
|
return output_error(drv, SQLITE_MISUSE, "empty statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sql_exec_statement(drv, statement);
|
||||||
|
if (result) {
|
||||||
|
// there was an error, bail out
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int decode_and_bind_param(
|
static inline int decode_and_bind_param(
|
||||||
sqlite3_drv_t *drv, char *buffer, int *p_index,
|
sqlite3_drv_t *drv, char *buffer, int *p_index,
|
||||||
sqlite3_stmt *statement, int param_index, int *p_type, int *p_size) {
|
sqlite3_stmt *statement, int param_index, int *p_type, int *p_size) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#define CMD_PREPARED_CLEAR_BINDINGS 9
|
#define CMD_PREPARED_CLEAR_BINDINGS 9
|
||||||
#define CMD_PREPARED_FINALIZE 10
|
#define CMD_PREPARED_FINALIZE 10
|
||||||
#define CMD_PREPARED_COLUMNS 11
|
#define CMD_PREPARED_COLUMNS 11
|
||||||
|
#define CMD_SQL_EXEC_SCRIPT 12
|
||||||
|
|
||||||
// Number of bytes for each key
|
// Number of bytes for each key
|
||||||
// (160 bits for SHA1 hash)
|
// (160 bits for SHA1 hash)
|
||||||
@@ -84,6 +85,7 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf,
|
|||||||
int len, char **rbuf, int rlen);
|
int len, char **rbuf, int rlen);
|
||||||
static int sql_exec(sqlite3_drv_t *drv, char *buf, int len);
|
static int sql_exec(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
static int sql_bind_and_exec(sqlite3_drv_t *drv, char *buf, int len);
|
static int sql_bind_and_exec(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
|
static int sql_exec_script(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
static int prepare(sqlite3_drv_t *drv, char *buf, int len);
|
static int prepare(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
static int prepared_bind(sqlite3_drv_t *drv, char *buf, int len);
|
static int prepared_bind(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
static int prepared_step(sqlite3_drv_t *drv, char *buf, int len);
|
static int prepared_step(sqlite3_drv_t *drv, char *buf, int len);
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
-export([open/1, open/2]).
|
-export([open/1, open/2]).
|
||||||
-export([start_link/1, start_link/2]).
|
-export([start_link/1, start_link/2]).
|
||||||
-export([stop/0, close/1]).
|
-export([stop/0, close/1]).
|
||||||
-export([sql_exec/1, sql_exec/2, sql_exec/3]).
|
-export([sql_exec/1, sql_exec/2, sql_exec/3,
|
||||||
|
sql_exec_script/2]).
|
||||||
-export([prepare/2, bind/3, next/2, reset/2, clear_bindings/2, finalize/2,
|
-export([prepare/2, bind/3, next/2, reset/2, clear_bindings/2, finalize/2,
|
||||||
columns/2]).
|
columns/2]).
|
||||||
-export([create_table/2, create_table/3, create_table/4]).
|
-export([create_table/2, create_table/3, create_table/4]).
|
||||||
@@ -170,6 +171,17 @@ sql_exec(Db, SQL) ->
|
|||||||
sql_exec(Db, SQL, Params) ->
|
sql_exec(Db, SQL, Params) ->
|
||||||
gen_server:call(Db, {sql_bind_and_exec, SQL, Params}).
|
gen_server:call(Db, {sql_bind_and_exec, SQL, Params}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% @spec sql_exec_script(Db :: atom(), Sql :: iodata()) -> sql_non_query_result()
|
||||||
|
%% @doc
|
||||||
|
%% Executes the Sql script (consisting of semicolon-separated statements)
|
||||||
|
%% directly on the Db database. Returns 'ok' if there were no errors.
|
||||||
|
%% @end
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-spec sql_exec_script(atom(), iodata()) -> sql_non_query_result().
|
||||||
|
sql_exec_script(Db, SQL) ->
|
||||||
|
gen_server:call(Db, {sql_exec_script, SQL}).
|
||||||
|
|
||||||
-spec prepare(atom(), iodata()) -> {ok, reference()} | sqlite_error().
|
-spec prepare(atom(), iodata()) -> {ok, reference()} | sqlite_error().
|
||||||
prepare(Db, SQL) ->
|
prepare(Db, SQL) ->
|
||||||
gen_server:call(Db, {prepare, SQL}).
|
gen_server:call(Db, {prepare, SQL}).
|
||||||
@@ -643,6 +655,9 @@ handle_call({sql_exec, SQL}, _From, State) ->
|
|||||||
handle_call({sql_bind_and_exec, SQL, Params}, _From, State) ->
|
handle_call({sql_bind_and_exec, SQL, Params}, _From, State) ->
|
||||||
Reply = do_sql_bind_and_exec(SQL, Params, State),
|
Reply = do_sql_bind_and_exec(SQL, Params, State),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
|
handle_call({sql_exec_script, SQL}, _From, State) ->
|
||||||
|
Reply = do_sql_exec_script(SQL, State),
|
||||||
|
{reply, Reply, State};
|
||||||
handle_call({create_table, Tbl, Columns}, _From, State) ->
|
handle_call({create_table, Tbl, Columns}, _From, State) ->
|
||||||
SQL = sqlite3_lib:create_table_sql(Tbl, Columns),
|
SQL = sqlite3_lib:create_table_sql(Tbl, Columns),
|
||||||
do_handle_call_sql_exec(SQL, State);
|
do_handle_call_sql_exec(SQL, State);
|
||||||
@@ -813,6 +828,7 @@ get_priv_dir() ->
|
|||||||
-define(PREPARED_CLEAR_BINDINGS, 9).
|
-define(PREPARED_CLEAR_BINDINGS, 9).
|
||||||
-define(PREPARED_FINALIZE, 10).
|
-define(PREPARED_FINALIZE, 10).
|
||||||
-define(PREPARED_COLUMNS, 11).
|
-define(PREPARED_COLUMNS, 11).
|
||||||
|
-define(SQL_EXEC_SCRIPT, 12).
|
||||||
|
|
||||||
create_port_cmd(DbFile) ->
|
create_port_cmd(DbFile) ->
|
||||||
atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile.
|
atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile.
|
||||||
@@ -829,6 +845,10 @@ do_sql_bind_and_exec(SQL, Params, #state{port = Port}) ->
|
|||||||
?dbg("SQL: ~s; Parameters: ~p~n", [SQL, Params]),
|
?dbg("SQL: ~s; Parameters: ~p~n", [SQL, Params]),
|
||||||
exec(Port, {sql_bind_and_exec, SQL, Params}).
|
exec(Port, {sql_bind_and_exec, SQL, Params}).
|
||||||
|
|
||||||
|
do_sql_exec_script(SQL, #state{port = Port}) ->
|
||||||
|
?dbg("SQL: ~s~n", [SQL]),
|
||||||
|
exec(Port, {sql_exec_script, SQL}).
|
||||||
|
|
||||||
exec(_Port, {create_function, _FunctionName, _Function}) ->
|
exec(_Port, {create_function, _FunctionName, _Function}) ->
|
||||||
error_logger:error_report([{application, sqlite3}, "NOT IMPL YET"]);
|
error_logger:error_report([{application, sqlite3}, "NOT IMPL YET"]);
|
||||||
%port_control(Port, ?SQL_CREATE_FUNCTION, list_to_binary(Cmd)),
|
%port_control(Port, ?SQL_CREATE_FUNCTION, list_to_binary(Cmd)),
|
||||||
@@ -840,6 +860,9 @@ exec(Port, {sql_bind_and_exec, SQL, Params}) ->
|
|||||||
Bin = term_to_binary({iolist_to_binary(SQL), Params}),
|
Bin = term_to_binary({iolist_to_binary(SQL), Params}),
|
||||||
port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin),
|
port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin),
|
||||||
wait_result(Port);
|
wait_result(Port);
|
||||||
|
exec(Port, {sql_exec_script, SQL}) ->
|
||||||
|
port_control(Port, ?SQL_EXEC_SCRIPT, SQL),
|
||||||
|
wait_result(Port);
|
||||||
exec(Port, {prepare, SQL}) ->
|
exec(Port, {prepare, SQL}) ->
|
||||||
port_control(Port, ?PREPARE, SQL),
|
port_control(Port, ?PREPARE, SQL),
|
||||||
wait_result(Port);
|
wait_result(Port);
|
||||||
|
|||||||
@@ -233,6 +233,25 @@ prepared_test() ->
|
|||||||
?assertEqual(ok, sqlite3:finalize(prepared, Ref2)),
|
?assertEqual(ok, sqlite3:finalize(prepared, Ref2)),
|
||||||
sqlite3:close(prepared).
|
sqlite3:close(prepared).
|
||||||
|
|
||||||
|
script_test() ->
|
||||||
|
sqlite3:open(script, [in_memory]),
|
||||||
|
Script = string:join(
|
||||||
|
["CREATE TABLE person(",
|
||||||
|
"id INTEGER",
|
||||||
|
");",
|
||||||
|
" ",
|
||||||
|
"-- Comment",
|
||||||
|
"",
|
||||||
|
"INSERT INTO person (id) VALUES (1);",
|
||||||
|
"INSERT INTO person (id) VALUES (2);",
|
||||||
|
" "
|
||||||
|
], "\n"),
|
||||||
|
?assertEqual(ok, sqlite3:sql_exec_script(script, Script)),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns,["id"]},{rows,[{1},{2}]}],
|
||||||
|
sqlite3:read_all(script, person)),
|
||||||
|
sqlite3:close(script).
|
||||||
|
|
||||||
% create, read, update, delete
|
% create, read, update, delete
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|||||||
Reference in New Issue
Block a user