Finished multiple statement support
This commit is contained in:
@@ -213,6 +213,16 @@ static inline int output_ok(sqlite3_drv_t *drv) {
|
|||||||
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
|
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int output_done(sqlite3_drv_t *drv) {
|
||||||
|
// Return {Port, ok}
|
||||||
|
ErlDrvTermData spec[] = {
|
||||||
|
ERL_DRV_PORT, driver_mk_port(drv->port),
|
||||||
|
ERL_DRV_ATOM, drv->atom_done,
|
||||||
|
ERL_DRV_TUPLE, 2
|
||||||
|
};
|
||||||
|
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
|
||||||
|
}
|
||||||
|
|
||||||
static inline async_sqlite3_command *make_async_command(
|
static inline async_sqlite3_command *make_async_command(
|
||||||
sqlite3_drv_t *drv, sqlite3_stmt *statement) {
|
sqlite3_drv_t *drv, sqlite3_stmt *statement) {
|
||||||
async_sqlite3_command *result =
|
async_sqlite3_command *result =
|
||||||
@@ -268,23 +278,24 @@ static int sql_exec_script(sqlite3_drv_t *drv, char *command, int command_size)
|
|||||||
const char *end = command + command_size;
|
const char *end = command + command_size;
|
||||||
sqlite3_stmt *statement;
|
sqlite3_stmt *statement;
|
||||||
|
|
||||||
// printf("SQL: %.*s\n", command_size, command);
|
|
||||||
|
|
||||||
while (rest < end) {
|
while (rest < end) {
|
||||||
result = sqlite3_prepare_v2(drv->db, command, end - command, &statement, &rest);
|
result = sqlite3_prepare_v2(drv->db, command, end - command, &statement, &rest);
|
||||||
command = (char *) rest; // won't actually mutate!
|
command = (char *) rest; // won't actually mutate!
|
||||||
if (result != SQLITE_OK) {
|
if (result != SQLITE_OK) {
|
||||||
return output_db_error(drv);
|
output_db_error(drv);
|
||||||
|
break;
|
||||||
} else if (statement == NULL) {
|
} else if (statement == NULL) {
|
||||||
return output_error(drv, SQLITE_MISUSE, "empty statement");
|
output_error(drv, SQLITE_MISUSE, "empty statement");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = sql_exec_statement(drv, statement);
|
result = sql_exec_statement(drv, statement);
|
||||||
if (result) {
|
if (result) {
|
||||||
// there was an error, bail out
|
// there was an error, bail out
|
||||||
return result;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
output_done(drv);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,13 +172,16 @@ 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()
|
%% @spec sql_exec_script(Db :: atom(), Sql :: iodata()) -> [sql_result()]
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Executes the Sql script (consisting of semicolon-separated statements)
|
%% Executes the Sql script (consisting of semicolon-separated statements)
|
||||||
%% directly on the Db database. Returns 'ok' if there were no errors.
|
%% directly on the Db database. Returns the list of their results (same as
|
||||||
|
%% if sql_exec/2 was called for all of them in order, but more efficient).
|
||||||
|
%% Note that any whitespace or comments after the last semicolon will be
|
||||||
|
%% considered an empty statement and produce the corresponding error.
|
||||||
%% @end
|
%% @end
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
-spec sql_exec_script(atom(), iodata()) -> sql_non_query_result().
|
-spec sql_exec_script(atom(), iodata()) -> [sql_result()].
|
||||||
sql_exec_script(Db, SQL) ->
|
sql_exec_script(Db, SQL) ->
|
||||||
gen_server:call(Db, {sql_exec_script, SQL}).
|
gen_server:call(Db, {sql_exec_script, SQL}).
|
||||||
|
|
||||||
@@ -862,7 +865,7 @@ exec(Port, {sql_bind_and_exec, SQL, Params}) ->
|
|||||||
wait_result(Port);
|
wait_result(Port);
|
||||||
exec(Port, {sql_exec_script, SQL}) ->
|
exec(Port, {sql_exec_script, SQL}) ->
|
||||||
port_control(Port, ?SQL_EXEC_SCRIPT, SQL),
|
port_control(Port, ?SQL_EXEC_SCRIPT, SQL),
|
||||||
wait_result(Port);
|
many_results_loop(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);
|
||||||
@@ -899,6 +902,17 @@ wait_result(Port) ->
|
|||||||
{error, -1, Reason}
|
{error, -1, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
many_results_loop(Port) ->
|
||||||
|
do_many_results_loop(Port, []).
|
||||||
|
|
||||||
|
do_many_results_loop(Port, Acc) ->
|
||||||
|
case wait_result(Port) of
|
||||||
|
done ->
|
||||||
|
lists:reverse(Acc);
|
||||||
|
Reply ->
|
||||||
|
do_many_results_loop(Port, [Reply | Acc])
|
||||||
|
end.
|
||||||
|
|
||||||
parse_table_info(Info) ->
|
parse_table_info(Info) ->
|
||||||
[_, Tail] = string:tokens(Info, "()"),
|
[_, Tail] = string:tokens(Info, "()"),
|
||||||
Cols = string:tokens(Tail, ","),
|
Cols = string:tokens(Tail, ","),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-define(FuncTest(Name), {??Name, fun Name/0}).
|
-define(FuncTest(Name), {??Name, fun Name/0}).
|
||||||
|
-define(WARN_ERROR_MESSAGE, io:format(user, "Error message should be shown...~n", [])).
|
||||||
|
|
||||||
drop_all_tables(Db) ->
|
drop_all_tables(Db) ->
|
||||||
Tables = sqlite3:list_tables(Db),
|
Tables = sqlite3:list_tables(Db),
|
||||||
@@ -61,7 +62,7 @@ basic_functionality() ->
|
|||||||
AbbyOnly = [{1, <<"abby">>, 20, 2000}],
|
AbbyOnly = [{1, <<"abby">>, 20, 2000}],
|
||||||
TableInfo = [{id, integer, [primary_key]}, {name, text, [not_null, unique]}, {age, integer}, {wage, integer}],
|
TableInfo = [{id, integer, [primary_key]}, {name, text, [not_null, unique]}, {age, integer}, {wage, integer}],
|
||||||
drop_all_tables(ct),
|
drop_all_tables(ct),
|
||||||
?debugMsg("Error message \"sqlite3 driver error: empty statement\" should be shown..."),
|
?WARN_ERROR_MESSAGE,
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{error, 21, "empty statement"},
|
{error, 21, "empty statement"},
|
||||||
sqlite3:sql_exec(ct, "-- Comment")),
|
sqlite3:sql_exec(ct, "-- Comment")),
|
||||||
@@ -81,7 +82,7 @@ basic_functionality() ->
|
|||||||
?assertEqual(
|
?assertEqual(
|
||||||
{rowid, 2},
|
{rowid, 2},
|
||||||
sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}])),
|
sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}])),
|
||||||
?debugMsg("Error message \"sqlite3 driver error: constraint failed\" should be shown..."),
|
?WARN_ERROR_MESSAGE,
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{error, 19, "constraint failed"},
|
{error, 19, "constraint failed"},
|
||||||
sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}])),
|
sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}])),
|
||||||
@@ -241,15 +242,31 @@ script_test() ->
|
|||||||
");",
|
");",
|
||||||
" ",
|
" ",
|
||||||
"-- Comment",
|
"-- Comment",
|
||||||
"",
|
|
||||||
"INSERT INTO person (id) VALUES (1);",
|
"INSERT INTO person (id) VALUES (1);",
|
||||||
"INSERT INTO person (id) VALUES (2);",
|
"INSERT INTO person (id) VALUES (2);",
|
||||||
" "
|
" "
|
||||||
], "\n"),
|
], "\n"),
|
||||||
?assertEqual(ok, sqlite3:sql_exec_script(script, Script)),
|
?WARN_ERROR_MESSAGE,
|
||||||
|
?assertEqual(
|
||||||
|
[ok, ok, ok, {error, 21, "empty statement"}],
|
||||||
|
sqlite3:sql_exec_script(script, Script)),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
[{columns,["id"]},{rows,[{1},{2}]}],
|
[{columns,["id"]},{rows,[{1},{2}]}],
|
||||||
sqlite3:read_all(script, person)),
|
sqlite3:read_all(script, person)),
|
||||||
|
BadScript = string:join(
|
||||||
|
["CREATE TABLE person2(",
|
||||||
|
"id INTEGER",
|
||||||
|
");",
|
||||||
|
" ",
|
||||||
|
"-- Comment",
|
||||||
|
"SYNTAX ERROR;",
|
||||||
|
"INSERT INTO person (id) VALUES (2);",
|
||||||
|
" "
|
||||||
|
], "\n"),
|
||||||
|
?WARN_ERROR_MESSAGE,
|
||||||
|
?assertEqual(
|
||||||
|
[ok, {error, 1, "near \"SYNTAX\": syntax error"}],
|
||||||
|
sqlite3:sql_exec_script(script, BadScript)),
|
||||||
sqlite3:close(script).
|
sqlite3:close(script).
|
||||||
|
|
||||||
% create, read, update, delete
|
% create, read, update, delete
|
||||||
|
|||||||
Reference in New Issue
Block a user