Added EUnit tests
This commit is contained in:
@@ -485,9 +485,13 @@ handle_call({table_info, Tbl}, _From, #state{port = Port} = State) ->
|
|||||||
SQL = io_lib:format("select sql from sqlite_master where tbl_name = '~p' and type='table';", [Tbl]),
|
SQL = io_lib:format("select sql from sqlite_master where tbl_name = '~p' and type='table';", [Tbl]),
|
||||||
Data = exec(Port, {sql_exec, SQL}),
|
Data = exec(Port, {sql_exec, SQL}),
|
||||||
TableSql = proplists:get_value(rows, Data),
|
TableSql = proplists:get_value(rows, Data),
|
||||||
[{Info}] = TableSql,
|
case TableSql of
|
||||||
ColumnList = parse_table_info(binary_to_list(Info)),
|
[{Info}] ->
|
||||||
{reply, ColumnList, State};
|
ColumnList = parse_table_info(binary_to_list(Info)),
|
||||||
|
{reply, ColumnList, State};
|
||||||
|
[] ->
|
||||||
|
{reply, table_does_not_exist, State}
|
||||||
|
end;
|
||||||
handle_call({create_function, FunctionName, Function}, _From, #state{port = Port} = State) ->
|
handle_call({create_function, FunctionName, Function}, _From, #state{port = Port} = State) ->
|
||||||
% make sure we only get table info.
|
% make sure we only get table info.
|
||||||
% SQL Injection warning
|
% SQL Injection warning
|
||||||
@@ -630,15 +634,29 @@ build_table_info([[ColName, ColType | Constraints] | Tl], Acc) ->
|
|||||||
|
|
||||||
%% TODO conflict-clause parsing
|
%% TODO conflict-clause parsing
|
||||||
build_constraints([]) -> [];
|
build_constraints([]) -> [];
|
||||||
build_constraints(["PRIMARY", "KEY", "ASC" | Tail]) -> [primary_key | build_constraints(Tail)];
|
build_constraints(["PRIMARY", "KEY" | Tail]) ->
|
||||||
build_constraints(["PRIMARY", "KEY", "DESC" | Tail]) -> [{primary_key, desc} | build_constraints(Tail)];
|
{Constraint, Rest} = build_primary_key_constraint(Tail),
|
||||||
build_constraints(["PRIMARY", "KEY" | Tail]) -> [primary_key | build_constraints(Tail)];
|
[Constraint | build_constraints(Rest)];
|
||||||
build_constraints(["UNIQUE" | Tail]) -> [unique | build_constraints(Tail)];
|
build_constraints(["UNIQUE" | Tail]) -> [unique | build_constraints(Tail)];
|
||||||
build_constraints(["NOT", "NULL" | Tail]) -> [not_null | build_constraints(Tail)];
|
build_constraints(["NOT", "NULL" | Tail]) -> [not_null | build_constraints(Tail)];
|
||||||
build_constraints(["DEFAULT", DefaultValue | Tail]) -> [{default, sqlite3_lib:sql_to_value(DefaultValue)} | build_constraints(Tail)].
|
build_constraints(["DEFAULT", DefaultValue | Tail]) -> [{default, sqlite3_lib:sql_to_value(DefaultValue)} | build_constraints(Tail)].
|
||||||
% build_constraints(["CHECK", Check | Tail]) -> ...
|
% build_constraints(["CHECK", Check | Tail]) -> ...
|
||||||
% build_constraints(["REFERENCES", Check | Tail]) -> ...
|
% build_constraints(["REFERENCES", Check | Tail]) -> ...
|
||||||
|
|
||||||
|
build_primary_key_constraint(Tokens) -> build_primary_key_constraint(Tokens, []).
|
||||||
|
|
||||||
|
build_primary_key_constraint(["ASC" | Rest], Acc) ->
|
||||||
|
build_primary_key_constraint(Rest, [asc | Acc]);
|
||||||
|
build_primary_key_constraint(["DESC" | Rest], Acc) ->
|
||||||
|
build_primary_key_constraint(Rest, [desc | Acc]);
|
||||||
|
build_primary_key_constraint(["AUTOINCREMENT" | Rest], Acc) ->
|
||||||
|
build_primary_key_constraint(Rest, [autoincrement | Acc]);
|
||||||
|
build_primary_key_constraint(Tail, []) ->
|
||||||
|
{primary_key, Tail};
|
||||||
|
build_primary_key_constraint(Tail, Acc) ->
|
||||||
|
{{primary_key, lists:reverse(Acc)}, Tail}.
|
||||||
|
|
||||||
|
|
||||||
conflict_clause(["ON", "CONFLICT", ResolutionString | Tail]) ->
|
conflict_clause(["ON", "CONFLICT", ResolutionString | Tail]) ->
|
||||||
Resolution = case ResolutionString of
|
Resolution = case ResolutionString of
|
||||||
"ROLLBACK" -> rollback;
|
"ROLLBACK" -> rollback;
|
||||||
@@ -658,3 +676,11 @@ conflict_clause(NoOnConflictClause) ->
|
|||||||
%% and io:iolist().
|
%% and io:iolist().
|
||||||
%% @end
|
%% @end
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Tests
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-ifdef(TEST).
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|||||||
@@ -116,14 +116,14 @@ sql_to_value(String) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% @spec write_value_sql(Value :: [term()]) -> iolist()
|
%% @spec write_value_sql(Value :: [sql_value()]) -> iolist()
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Creates the values portion of the sql stmt.
|
%% Creates the values portion of the sql stmt.
|
||||||
%% @end
|
%% @end
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
-spec write_value_sql(any()) -> iolist().
|
-spec write_value_sql(sql_value()) -> iolist().
|
||||||
write_value_sql(Values) ->
|
write_value_sql(Values) ->
|
||||||
map_intersperse(fun value_to_sql/1, Values, ",").
|
map_intersperse(fun value_to_sql/1, Values, ", ").
|
||||||
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
@@ -133,7 +133,7 @@ write_value_sql(Values) ->
|
|||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
-spec write_col_sql([atom()]) -> iolist().
|
-spec write_col_sql([atom()]) -> iolist().
|
||||||
write_col_sql(Cols) ->
|
write_col_sql(Cols) ->
|
||||||
map_intersperse(fun atom_to_list/1, Cols, ",").
|
map_intersperse(fun atom_to_list/1, Cols, ", ").
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% @spec escape(IoData :: iodata()) -> iodata()
|
%% @spec escape(IoData :: iodata()) -> iodata()
|
||||||
@@ -355,3 +355,18 @@ indexed_column_sql(ColumnName) -> atom_to_list(ColumnName).
|
|||||||
%% and io:iolist().
|
%% and io:iolist().
|
||||||
%% @end
|
%% @end
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Tests
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-ifdef(TEST).
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(FLAT(X), iolist_to_binary(X)).
|
||||||
|
|
||||||
|
quote_test() ->
|
||||||
|
?assertEqual(<<"'abc'">>, ?FLAT(value_to_sql("abc"))),
|
||||||
|
?assertEqual(<<"'a''b''''c'">>, ?FLAT(value_to_sql("a'b''c"))).
|
||||||
|
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|||||||
@@ -17,27 +17,91 @@
|
|||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
create_table_test() ->
|
drop_all_tables(Db) ->
|
||||||
file:delete("ct.db"),
|
Tables = sqlite3:list_tables(Db),
|
||||||
sqlite3:open(ct),
|
[sqlite3:drop_table(Db, Table) || Table <- Tables],
|
||||||
sqlite3:create_table(ct, user, [{id, integer, [primary_key]}, {name, text}, {age, integer}, {wage, integer}]),
|
Tables.
|
||||||
[user] = sqlite3:list_tables(ct),
|
|
||||||
[{id, integer, [primary_key]}, {name, text}, {age, integer}, {wage, integer}] = sqlite3:table_info(ct, user),
|
|
||||||
{id, Id1} = sqlite3:write(ct, user, [{name, "abby"}, {age, 20}, {wage, 2000}]),
|
|
||||||
Id1 = 1,
|
|
||||||
{id, Id2} = sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}]),
|
|
||||||
Id2 = 2,
|
|
||||||
[{columns, Columns}, {rows, Rows1}] = sqlite3:sql_exec(ct, "select * from user;"),
|
|
||||||
Columns = ["id", "name", "age", "wage"],
|
|
||||||
Rows1 = [{1, <<"abby">>, 20, 2000}, {2, <<"marge">>, 30, 2000}],
|
|
||||||
[{columns, Columns}, {rows, Rows2}] = sqlite3:read(ct, user, {name, "abby"}),
|
|
||||||
Rows2 = [{1, <<"abby">>, 20, 2000}],
|
|
||||||
[{columns, Columns}, {rows, Rows1}] = sqlite3:read(ct, user, {wage, 2000}),
|
|
||||||
sqlite3:delete(ct, user, {name, "abby"}),
|
|
||||||
sqlite3:drop_table(ct, user),
|
|
||||||
%sqlite3:delete_db(ct)
|
|
||||||
sqlite3:close(ct).
|
|
||||||
|
|
||||||
|
drop_table_if_exists(Db, Table) ->
|
||||||
|
case lists:member(Table, sqlite3:list_tables(Db)) of
|
||||||
|
true -> sqlite3:drop_table(Db, Table);
|
||||||
|
false -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
rows(SqlExecReply) ->
|
||||||
|
[{columns, _Columns}, {rows, Rows}] = SqlExecReply,
|
||||||
|
Rows.
|
||||||
|
|
||||||
|
basic_functionality_test() ->
|
||||||
|
Columns = ["id", "name", "age", "wage"],
|
||||||
|
AllRows = [{1, <<"abby">>, 20, 2000}, {2, <<"marge">>, 30, 2000}],
|
||||||
|
AbbyOnly = [{1, <<"abby">>, 20, 2000}],
|
||||||
|
sqlite3:open(ct),
|
||||||
|
drop_all_tables(ct),
|
||||||
|
?assertEqual(
|
||||||
|
[],
|
||||||
|
sqlite3:list_tables(ct)),
|
||||||
|
{ok, TableId} = sqlite3:create_table(ct, user, [{id, integer, [primary_key]}, {name, text}, {age, integer}, {wage, integer}]),
|
||||||
|
?assertEqual(
|
||||||
|
[user],
|
||||||
|
sqlite3:list_tables(ct)),
|
||||||
|
?assertEqual(
|
||||||
|
[{id, integer, [primary_key]}, {name, text}, {age, integer}, {wage, integer}],
|
||||||
|
sqlite3:table_info(ct, user)),
|
||||||
|
?assertEqual(
|
||||||
|
{id, 1},
|
||||||
|
sqlite3:write(ct, user, [{name, "abby"}, {age, 20}, {wage, 2000}])),
|
||||||
|
?assertEqual(
|
||||||
|
{id, 2},
|
||||||
|
sqlite3:write(ct, user, [{name, "marge"}, {age, 30}, {wage, 2000}])),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, Columns}, {rows, AllRows}],
|
||||||
|
sqlite3:sql_exec(ct, "select * from user;")),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, Columns}, {rows, AbbyOnly}],
|
||||||
|
sqlite3:read(ct, user, {name, "abby"})),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, Columns}, {rows, AllRows}],
|
||||||
|
sqlite3:read(ct, user, {wage, 2000})),
|
||||||
|
?assertEqual(
|
||||||
|
{ok, TableId},
|
||||||
|
sqlite3:delete(ct, user, {name, "marge"})),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, Columns}, {rows, AbbyOnly}],
|
||||||
|
sqlite3:sql_exec(ct, "select * from user;")),
|
||||||
|
?assertEqual(
|
||||||
|
{ok, TableId},
|
||||||
|
sqlite3:drop_table(ct, user)),
|
||||||
|
sqlite3:close(ct).
|
||||||
|
|
||||||
|
select_many_records_test() ->
|
||||||
|
sqlite3:open(ct),
|
||||||
|
drop_table_if_exists(ct, many_records),
|
||||||
|
sqlite3:create_table(ct, many_records, [{id, integer}, {name, text}]),
|
||||||
|
lists:foreach(fun(X) -> sqlite3:write(ct, many_records, [{id, X}, {name, "bar"}]) end, lists:seq(1, 1024)),
|
||||||
|
Columns = ["id", "name"],
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, Columns}, {rows, [{1, <<"bar">>}]}],
|
||||||
|
sqlite3:read(ct, many_records, {id, 1})),
|
||||||
|
?assertEqual(
|
||||||
|
10,
|
||||||
|
length(rows(sqlite3:sql_exec(ct, "select * from many_records limit 10;")))),
|
||||||
|
?assertEqual(
|
||||||
|
100,
|
||||||
|
length(rows(sqlite3:sql_exec(ct, "select * from many_records limit 100;")))),
|
||||||
|
?assertEqual(
|
||||||
|
1000,
|
||||||
|
length(rows(sqlite3:sql_exec(ct, "select * from many_records limit 1000;")))),
|
||||||
|
?assertEqual(
|
||||||
|
1024,
|
||||||
|
length(rows(sqlite3:sql_exec(ct, "select * from many_records;")))),
|
||||||
|
sqlite3:close(ct).
|
||||||
|
|
||||||
|
nonexistent_table_info_test() ->
|
||||||
|
sqlite3:open(ct),
|
||||||
|
?assertEqual(table_does_not_exist, sqlite3:table_info(ct, nonexistent)),
|
||||||
|
sqlite3:close(ct).
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
% create, read, update, delete
|
% create, read, update, delete
|
||||||
|
|||||||
Reference in New Issue
Block a user