Finished multiple statement support

This commit is contained in:
Alexey Romanov
2011-01-24 15:48:48 +03:00
parent 9f57f6ae39
commit 11452c5b88
3 changed files with 55 additions and 13 deletions

View File

@@ -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;
} }

View File

@@ -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, ","),

View File

@@ -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