Skip to content

Commit

Permalink
Dialyzer configs, and fix dialyzer calls
Browse files Browse the repository at this point in the history
  • Loading branch information
choptastic committed Sep 9, 2023
1 parent 124dbc5 commit 4b9feb4
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 85 deletions.
6 changes: 6 additions & 0 deletions rebar.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
%% vim: ts=4 sw=4 et ft=erlang
{cover_enabled, true}.

{erl_opts, [
debug_info
]}.

{deps, [
b64fast,
erlias,
Expand All @@ -14,8 +18,10 @@
{dialyzer, [
{plt_apps, all_deps},
{plt_extra_apps, [
syntax_tools,
mysql,
epgsql,
epgsql_decimal,
poolboy,
b64fast,
decimal,
Expand Down
20 changes: 13 additions & 7 deletions src/sql_bridge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,17 @@ db(DB) ->
% @doc starts the actual database driver, if necessary
start() ->
{ok, _} = application:ensure_all_started(sql_bridge),
ok = sql_bridge_alias:build_stringify(?STRINGIFY),
ok = sql_bridge_alias:build(?ALIAS),
build_stringify(),
build_alias(),
ok = ?ADAPTER:start(),
ok.

build_stringify() ->
ok = sql_bridge_alias:build_stringify(?STRINGIFY).

build_alias() ->
ok = sql_bridge_alias:build(?ALIAS).

-spec connect() -> db().
% @doc establishes a connection to the appropriate database.
connect() ->
Expand Down Expand Up @@ -473,24 +479,24 @@ fr(Q,ParamList) ->
fr(Q) ->
fr(Q,[]).

-spec fffr(Q :: sql(), ParamList :: [value()]) -> string() | integer() | not_found.
-spec fffr(Q :: sql(), ParamList :: [value()]) -> value() | not_found.
%% @doc Get First Field of First record
fffr(Q,ParamList) ->
case fr(Q,ParamList) of
not_found -> not_found;
[First|_] -> First
end.

-spec fffr(Q :: sql()) -> string() | integer() | not_found.
-spec fffr(Q :: sql()) -> value() | not_found.
fffr(Q) ->
fffr(Q,[]).

%% First Field List
-spec ffl(Q :: sql(), ParamList :: [value()]) -> [string() | integer()].
-spec ffl(Q :: sql(), ParamList :: [value()]) -> [value()].
ffl(Q,ParamList) ->
[First || [First | _ ] <- q(Q,ParamList)].

-spec ffl(Q :: sql()) -> [string() | integer()].
-spec ffl(Q :: sql()) -> [value()].
ffl(Q) ->
ffl(Q,[]).

Expand Down Expand Up @@ -638,7 +644,7 @@ encode64(Data) ->
binary_to_list(b64fast:encode64(term_to_binary(Data))).
%base64:encode_to_string(term_to_binary(Data)).

-spec decode64(T :: any()) -> string().
-spec decode64(T :: any()) -> term().
%% @doc Decodes a base64 string into the relevant erlang term.
decode64("") -> "";
decode64(undefined) -> "";
Expand Down
4 changes: 2 additions & 2 deletions src/sql_bridge_epgsql_codec_numeric.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ decode(Data, Type, State) ->
_ -> binary_to_float(decimal_conv:to_binary(Decimal, #{pretty=>false}))
end.

decode_text(Data, Type, State) ->
?MAIN_MOD:decode_text(Data, Type, State).
decode_text(Data, _Type, _State) ->
Data.
20 changes: 20 additions & 0 deletions src/sql_bridge_stringify.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
%% This module will be dynamically generated immediately, and will never
%% actually be run exactly as-is. Instead, the `sql_bridge_alias` module will
%% build a new one on the fly based on the configuration.
%%
%% This module as is, currently only exists to satisfy dialyzer for being an
%% unknown module.
%%
%% Further, it has an `-on_load` attribute to force the module to self-rebuild
%% in the event of a release upgrade so that the behavior changes as needed.

-module(sql_bridge_stringify).
-export([maybe_string/1]).
-on_load(rebuild/0).

-spec maybe_string(null | binary() | list()) -> undefined | binary() | list().
maybe_string(null) -> undefined;
maybe_string(B) -> B.

rebuild() ->
sql_bridge:build_stringify().
156 changes: 80 additions & 76 deletions src/sql_bridge_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).

%% This is just a shortcut to prevent dialyzer throwing errors for the
%% dynamically generated `db` module.
-define(DB, sql_bridge).

-define(P(X), (sql_bridge_utils:create_placeholder(X))).
-define(P1, ?P(1)).
-define(P2, ?P(2)).
Expand Down Expand Up @@ -62,7 +66,7 @@ gen_setup(Adapter, ReplacementType, Host, Port) ->
application:set_env(sql_bridge, replacement_token_style, ReplacementType),
application:set_env(sql_bridge, stringify_binaries, true),
sql_bridge:start(),
db:q("delete from fruit").
?DB:q("delete from fruit").


epgsql_cleanup(_) ->
Expand Down Expand Up @@ -99,8 +103,8 @@ trans_tests(_) ->
?_assertNot(test_trans(LookupPid, 70, rollback))
]}
]},
?_assertEqual(12, db:fffr("select count(*) from fruit")),
?_assertEqual(12, db:fffr(["select count(*) from fruit where quantity in (",db:encode_list([1,2,3,4,5,6,7,8,9,10,11,12]),")"]))
?_assertEqual(12, ?DB:fffr("select count(*) from fruit")),
?_assertEqual(12, ?DB:fffr(["select count(*) from fruit where quantity in (",?DB:encode_list([1,2,3,4,5,6,7,8,9,10,11,12]),")"]))
]}
].

Expand Down Expand Up @@ -149,23 +153,23 @@ test_trans(LookupPid, Quantity) ->
test_trans(LookupPid, Quantity, CommitOrRollback) ->
FruitName = "Fruit-" ++ integer_to_list(Quantity),
SleepModifier = Quantity * 10,
_AddedFruitid = db:trans(fun() ->
_AddedFruitid = ?DB:trans(fun() ->
FirstSleep = 1000 - SleepModifier,
SecondSleep = 1500 + SleepModifier,
ThirdSleep = 2000,
StartTime = os:timestamp(),
?TRANS_STATUS("Transaction Started. Sleeping for ~pms", [FirstSleep]),
timer:sleep(FirstSleep),
?TRANS_STATUS("Woke up. Verifying fruit table is empty"),
0=db:fffr("select count(*) from fruit"),
0=?DB:fffr("select count(*) from fruit"),
?TRANS_STATUS("Inserting ~s", [FruitName]),
Fruitid = db:qi(["insert into fruit(fruit, quantity) values(",?P1,",",?P2,")"], [FruitName, Quantity]),
Fruitid = ?DB:qi(["insert into fruit(fruit, quantity) values(",?P1,",",?P2,")"], [FruitName, Quantity]),
?TRANS_STATUS("Inserted (fruitid=~p). Registering with tracker process",[Fruitid]),
register_fruitid(LookupPid, Fruitid),
?TRANS_STATUS("Registered. Verifying that Friutid=~p exists in transaction.", [Fruitid]),
true=db:exists(fruit, Fruitid),
true=?DB:exists(fruit, Fruitid),
?TRANS_STATUS("fruitid=~p exists. Counting records in table (should only be 1)", [Fruitid]),
1=db:fffr("select count(*) from fruit"),
1=?DB:fffr("select count(*) from fruit"),
?TRANS_STATUS("Verified. Sleeping for ~pms", [SecondSleep]),
timer:sleep(SecondSleep),
?TRANS_STATUS("Woke up. Getting a random other fruit that was inserted in another transaction."),
Expand All @@ -174,7 +178,7 @@ test_trans(LookupPid, Quantity, CommitOrRollback) ->
true=is_integer(OtherTranFruitid),
true=(Fruitid=/=OtherTranFruitid),
?TRANS_STATUS("Verified. Verifying that fruitid=~p does not yet exists in this transaction.",[OtherTranFruitid]),
false=db:exists(fruit, OtherTranFruitid),
false=?DB:exists(fruit, OtherTranFruitid),
?TRANS_STATUS("Verified. Sleeping for ~pms.",[ThirdSleep]),
timer:sleep(ThirdSleep),
?TRANS_STATUS("Woke up. Now checking if we should crash or return."),
Expand All @@ -183,7 +187,7 @@ test_trans(LookupPid, Quantity, CommitOrRollback) ->
commit=CommitOrRollback,
Fruitid
end),
db:qexists(["select * from fruit where quantity=",?P1], [Quantity]).
?DB:qexists(["select * from fruit where quantity=",?P1], [Quantity]).

%sleep_random(Min, Max) ->
% Time = crypto:rand_uniform(Min, Max),
Expand All @@ -199,50 +203,50 @@ trans_status(StartTime, Tag, Msg, Args) ->

main_tests(_) ->
[
?_assertEqual([], db:q("select * from fruit")),
?_assertEqual([], db:tq("select * from fruit")),
?_assertEqual([], db:dq("select * from fruit")),
?_assertEqual([], db:mq("select * from fruit")),
?_assertEqual([], db:plq("select * from fruit")),
?_assertEqual(not_found, db:fr("select * from fruit")),
?_assertEqual(not_found, db:tfr("select * from fruit")),
?_assertEqual(not_found, db:mfr("select * from fruit")),
?_assertEqual(not_found, db:plfr("select * from fruit")),
?_assertEqual(not_found, db:dfr("select * from fruit")),

?_assertMatch([fruitid, fruit, description, quantity, picture, some_float], db:table_fields(fruit)),
?_assert(is_integer(db:qi(["insert into fruit(fruit, quantity, some_float) values(", ?P1, ",", ?P2, ",", ?P3,")"], ["apple", 5, 10.1]))),
?_assertEqual(undefined, db:fffr("select description from fruit where fruit='apple'")),
?_assertEqual(5, db:fffr(["select quantity from fruit where fruit=",?P1 ], [apple])),
?_assertEqual("apple", db:fffr(["select fruit from fruit where quantity=", ?P1], [5])),
?_assertEqual("apple", db:fffr(["select fruit from fruit where quantity=", ?P1], ["5"])),
?_assertEqual("apple", db:fffr(["select fruit from fruit where quantity=", ?P1], [<<"5">>])),
?_assert(is_integer(db:pl(fruit, [{fruitid, 0}, {fruit, <<"banana">>}, {quantity, 100}, {description, "long and yellow"}, {some_float, 6.1}]))),
?_assert(is_float(db:fffr("select sum(some_float) from fruit"))),
?_assertMatch("long and yellow", db:field(fruit, description, fruit, "banana")),

?_assertEqual([["apple", 5], ["banana", 100]], db:q("select fruit, quantity from fruit order by fruit")),
?_assertEqual(["apple", 5], db:fr("select fruit, quantity from fruit order by fruit")),

?_assertEqual([{"apple", 5}, {"banana", 100}], db:tq("select fruit, quantity from fruit order by fruit")),
?_assertEqual({"apple", 5}, db:tfr("select fruit, quantity from fruit order by fruit")),

?_assertEqual([[{fruit, "apple"}, {quantity, 5}], [{fruit, "banana"}, {quantity, 100}]], db:plq("select fruit, quantity from fruit order by fruit")),
?_assertEqual([{fruit, "apple"}, {quantity, 5}], db:plfr("select fruit, quantity from fruit order by fruit")),

?_assertEqual("apple", dict:fetch(fruit, db:dfr("select fruit, quantity from fruit order by fruit"))),

?_assertEqual([#{fruit=>"apple", quantity=>5}, #{fruit=>"banana", quantity=>100}], db:mq("select fruit, quantity from fruit order by fruit")),
?_assertEqual(#{fruit=>"apple", quantity=>5}, db:mfr("select fruit, quantity from fruit order by fruit")),
?_assertEqual(["apple", "banana"], db:ffl("select fruit from fruit order by fruit")),
?_assert(db:qexists(["select * from fruit where fruit=",?P1], [banana])),
?_assert(db:qexists(["select * from fruit where fruit=",?P1], ["apple"])),
?_assertNot(db:qexists(["select * from fruit where fruit=",?P1], [<<"watermelon">>])),
?_assertEqual([], ?DB:q("select * from fruit")),
?_assertEqual([], ?DB:tq("select * from fruit")),
?_assertEqual([], ?DB:dq("select * from fruit")),
?_assertEqual([], ?DB:mq("select * from fruit")),
?_assertEqual([], ?DB:plq("select * from fruit")),
?_assertEqual(not_found, ?DB:fr("select * from fruit")),
?_assertEqual(not_found, ?DB:tfr("select * from fruit")),
?_assertEqual(not_found, ?DB:mfr("select * from fruit")),
?_assertEqual(not_found, ?DB:plfr("select * from fruit")),
?_assertEqual(not_found, ?DB:dfr("select * from fruit")),

?_assertMatch([fruitid, fruit, description, quantity, picture, some_float], ?DB:table_fields(fruit)),
?_assert(is_integer(?DB:qi(["insert into fruit(fruit, quantity, some_float) values(", ?P1, ",", ?P2, ",", ?P3,")"], ["apple", 5, 10.1]))),
?_assertEqual(undefined, ?DB:fffr("select description from fruit where fruit='apple'")),
?_assertEqual(5, ?DB:fffr(["select quantity from fruit where fruit=",?P1 ], [apple])),
?_assertEqual("apple", ?DB:fffr(["select fruit from fruit where quantity=", ?P1], [5])),
?_assertEqual("apple", ?DB:fffr(["select fruit from fruit where quantity=", ?P1], ["5"])),
?_assertEqual("apple", ?DB:fffr(["select fruit from fruit where quantity=", ?P1], [<<"5">>])),
?_assert(is_integer(?DB:pl(fruit, [{fruitid, 0}, {fruit, <<"banana">>}, {quantity, 100}, {description, "long and yellow"}, {some_float, 6.1}]))),
?_assert(is_float(?DB:fffr("select sum(some_float) from fruit"))),
?_assertMatch("long and yellow", ?DB:field(fruit, description, fruit, "banana")),

?_assertEqual([["apple", 5], ["banana", 100]], ?DB:q("select fruit, quantity from fruit order by fruit")),
?_assertEqual(["apple", 5], ?DB:fr("select fruit, quantity from fruit order by fruit")),

?_assertEqual([{"apple", 5}, {"banana", 100}], ?DB:tq("select fruit, quantity from fruit order by fruit")),
?_assertEqual({"apple", 5}, ?DB:tfr("select fruit, quantity from fruit order by fruit")),

?_assertEqual([[{fruit, "apple"}, {quantity, 5}], [{fruit, "banana"}, {quantity, 100}]], ?DB:plq("select fruit, quantity from fruit order by fruit")),
?_assertEqual([{fruit, "apple"}, {quantity, 5}], ?DB:plfr("select fruit, quantity from fruit order by fruit")),

?_assertEqual("apple", dict:fetch(fruit, ?DB:dfr("select fruit, quantity from fruit order by fruit"))),

?_assertEqual([#{fruit=>"apple", quantity=>5}, #{fruit=>"banana", quantity=>100}], ?DB:mq("select fruit, quantity from fruit order by fruit")),
?_assertEqual(#{fruit=>"apple", quantity=>5}, ?DB:mfr("select fruit, quantity from fruit order by fruit")),
?_assertEqual(["apple", "banana"], ?DB:ffl("select fruit from fruit order by fruit")),
?_assert(?DB:qexists(["select * from fruit where fruit=",?P1], [banana])),
?_assert(?DB:qexists(["select * from fruit where fruit=",?P1], ["apple"])),
?_assertNot(?DB:qexists(["select * from fruit where fruit=",?P1], [<<"watermelon">>])),
?_assertEqual(#{fruit=>"orange", quantity=>5, description=>"oranges are orange"}, update_apple_to_orange()),
?_assertEqual("berry", test_insert_id()),
?_assertEqual(1, db:delete(fruit, fruit, "orange")),
?_assertEqual(1, ?DB:delete(fruit, fruit, "orange")),
?_assert(test_exists("banana")),
?_assertNot(db:exists(fruit, fruit, "banana-fake")),
?_assertNot(?DB:exists(fruit, fruit, "banana-fake")),
?_assertNot(test_id_delete()),
?_assert(test_string("ﻦﺤﻧ ﺫﺎﻬﺑﻮﻧ ﻒﻳ ﺡﺎﺟﺓ ﺈﻟﻯ ﻕﺍﺮﺑ ﺄﻜﺑﺭ")),
?_assert(test_string("我们将需要更大的船")),
Expand All @@ -252,10 +256,10 @@ main_tests(_) ->
?_assert(test_string("testy'pants")),
?_assert(test_string("'+\"!@#$%^&*()\\//\\//';[]<>./-=-=+")),
?_assert(test_encode_list(["'+\"", "!@#$'^&%", "//\\//\\", "blah","123","-=--=-!+'\"'''''''''''''''''"])),
?_assertEqual({some, crazy,"term"}, db:decode64(db:encode64({some, crazy,"term"}))),
?_assertMatch([_, _], db:q("select * from fruit " ++ db:limit_clause(2, 1))),
?_assertMatch([_, _], db:q("select * from fruit " ++ db:limit_clause(2, -1))),
?_assertMatch([_], db:q("select * from fruit " ++ db:limit_clause(-123, 5))),
?_assertEqual({some, crazy,"term"}, ?DB:decode64(?DB:encode64({some, crazy,"term"}))),
?_assertMatch([_, _], ?DB:q("select * from fruit " ++ ?DB:limit_clause(2, 1))),
?_assertMatch([_, _], ?DB:q("select * from fruit " ++ ?DB:limit_clause(2, -1))),
?_assertMatch([_], ?DB:q("select * from fruit " ++ ?DB:limit_clause(-123, 5))),
?_assertEqual(1.1, test_float(1.1)),
?_assertEqual(12345.5, test_float(12345.5)),
?_assertEqual(12.5, test_decimal(12.5)),
Expand All @@ -278,50 +282,50 @@ test_datetime(V) ->
test_in_out_other(my_datetime, V).

test_null() ->
ID = db:pl(other, [{otherid, 0}, {my_decimal, 123.5}]),
db:field(other, my_date, ID).
ID = ?DB:pl(other, [{otherid, 0}, {my_decimal, 123.5}]),
?DB:field(other, my_date, ID).

test_in_out_other(Field, V) ->
ID = db:pl(other, [{Field, V}]),
db:field(other, Field, ID).
ID = ?DB:pl(other, [{Field, V}]),
?DB:field(other, Field, ID).

test_float(Val) ->
Fruitid = db:pl(fruit, [{some_float, Val}]),
db:field(fruit, some_float, Fruitid).
Fruitid = ?DB:pl(fruit, [{some_float, Val}]),
?DB:field(fruit, some_float, Fruitid).

update_apple_to_orange() ->
Fruitid = db:fffr(["Select fruitid from fruit where fruit=",?P1], ["apple"]),
Fruitid = ?DB:fffr(["Select fruitid from fruit where fruit=",?P1], ["apple"]),
New = [
{fruitid, Fruitid},
{fruit, "orange"},
{description, "oranges are orange"}
],
db:pl(fruit, New),
db:mfr(["Select fruit, quantity, description from fruit where fruitid=", ?P1], [Fruitid]).
?DB:pl(fruit, New),
?DB:mfr(["Select fruit, quantity, description from fruit where fruitid=", ?P1], [Fruitid]).

test_insert_id() ->
Fruitid = db:qi("insert into fruit(fruit, quantity) values('berry', 200)"),
db:field(fruit, fruit, Fruitid).
Fruitid = ?DB:qi("insert into fruit(fruit, quantity) values('berry', 200)"),
?DB:field(fruit, fruit, Fruitid).

test_exists(Fruit) ->
Fruitid = db:fffr(["select fruitid from fruit where fruit=",?P1], [Fruit]),
db:exists(fruit, Fruitid).
Fruitid = ?DB:fffr(["select fruitid from fruit where fruit=",?P1], [Fruit]),
?DB:exists(fruit, Fruitid).

test_id_delete() ->
Fruitid = db:fffr(["select fruitid from fruit where fruit=",?P1], ["banana"]),
db:delete(fruit, Fruitid),
db:exists(fruit, Fruitid).
Fruitid = ?DB:fffr(["select fruitid from fruit where fruit=",?P1], ["banana"]),
?DB:delete(fruit, Fruitid),
?DB:exists(fruit, Fruitid).

test_string(Str) ->
Fruitid = db:pl(fruit, [{fruitid, 0}, {fruit, "new"}, {description, Str}]),
Str == db:field(fruit, description, Fruitid).
Fruitid = ?DB:pl(fruit, [{fruitid, 0}, {fruit, "new"}, {description, Str}]),
Str == ?DB:field(fruit, description, Fruitid).

test_encode_list(List) ->
Fruitids = lists:map(fun(Fruit) ->
db:pl(fruit, [{fruit, Fruit}])
?DB:pl(fruit, [{fruit, Fruit}])
end, List),
Fruitids = db:ffl(["select fruitid from fruit where fruitid in (",db:encode_list(Fruitids),") order by fruitid"]),
Fruitids = db:ffl(["select fruitid from fruit where fruit in (", db:encode_list(List), ") order by fruitid"]),
Fruitids = ?DB:ffl(["select fruitid from fruit where fruitid in (",?DB:encode_list(Fruitids),") order by fruitid"]),
Fruitids = ?DB:ffl(["select fruitid from fruit where fruit in (", ?DB:encode_list(List), ") order by fruitid"]),
true.


0 comments on commit 4b9feb4

Please sign in to comment.