Skip to content

Commit 4aa817f

Browse files
committed
Key Generation and uuid works for postgres
1 parent 887fd40 commit 4aa817f

10 files changed

Lines changed: 256 additions & 100 deletions

include/records.hrl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
-define(LOG(Msg), sql_bridge_utils:log(Msg)).
2-
-define(LOG(Msg, Args), sql_bridge_utils:log(Msg, Args)).
1+
-define(SQL_LOG(Msg), sql_bridge_utils:log(Msg)).
2+
-define(SQL_LOG(Msg, Args), sql_bridge_utils:log(Msg, Args)).
3+
-define(PH(X), sql_bridge_utils:create_placeholders(X)).

src/sql_bridge.erl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
-type return_type() :: dict | list | proplist | tuple | insert | update | map.
3434
-type proplist_or_map() :: tuple() | proplist() | map().
3535

36+
-type string_length() :: integer().
37+
-type integer_range() :: {integer(), integer()}.
38+
-type field_type() :: {uuid, undefined} |
39+
{text, string_length()} |
40+
{integer, integer_range()}.
41+
3642
-export_type([
3743
sql/0,
3844
db/0,
@@ -43,7 +49,8 @@
4349
affected_rows/0,
4450
proplist/0,
4551
return_type/0,
46-
return_value/0
52+
return_value/0,
53+
field_type/0
4754
]).
4855

4956
%% New API aliases
@@ -224,9 +231,9 @@ save_(Table,KeyField,PropList) when is_list(Table) ->
224231
false ->
225232
%% if it's not auto_increment, then we will auto_increment
226233
%% based on the settings
227-
{DB, Table2} = table_and_db(Table),
228-
{AutoKeyMod, AutoKeyFun} = sql_bridge_utils:auto_increment_function(),
229-
KeyVal = AutoKeyMod:AutoKeyFun(DB, Table2),
234+
{_DB, Table2} = table_and_db(Table),
235+
{AutoKeyMod, AutoKeyFun} = sql_bridge_utils:key_generator_function(),
236+
KeyVal = AutoKeyMod:AutoKeyFun(Table2, KeyField),
230237
PropList3 = [{KeyField, KeyVal} | PropList2],
231238
pli(Table, PropList3),
232239
KeyVal
@@ -570,6 +577,12 @@ field_exists(Table0, Field) ->
570577
where ">>,DBCol,<<"=">>,T1,<<" and table_name=">>,T2,<<" and column_name=">>,T3],
571578
qexists(SQL, [DB, Table, Field]).
572579

580+
581+
-spec field_type(table(), field()) -> field_type().
582+
field_type(Table0, Field) ->
583+
{DB, Table} = table_and_db(Table0),
584+
?ADAPTER:field_type(DB, Table, Field).
585+
573586
-spec qexists(Q :: sql()) -> boolean().
574587
%% @doc Existance query, just returns true if the query Q returns anything
575588
%% other than an empty set.

src/sql_bridge_adapter.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
-callback primary_key(DB :: sql_bridge:db(),
2020
Table :: sql_bridge:table()) -> sql_bridge:field().
2121

22-
-callback get_field_type(DB :: sql_bridge:db(),
22+
-callback field_type(DB :: sql_bridge:db(),
2323
Table :: sql_bridge:table(),
2424
Field :: sql_bridge:field()) -> undefined | {sql_bridge:field_type(), sql_bridge:field_details()}.
2525

src/sql_bridge_epgsql.erl

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
-behaviour(sql_bridge_adapter).
33
-include("compat.hrl").
44

5+
56
-export([start/0,
67
connect/5,
78
query/4,
@@ -14,7 +15,7 @@
1415
wrap_field/1,
1516
primary_key/2,
1617
is_auto_increment/3,
17-
get_field_type/3,
18+
field_type/3,
1819
autokey/2
1920
]).
2021

@@ -75,6 +76,7 @@ query_catched(Type, DB, Q, ParamList) ->
7576
ToRun = fun(Worker) ->
7677
%% calls sql_bridge_epgsql_worker:handle_call()
7778
InnerRes = gen_server:call(Worker, {equery, Q2, ParamList2}),
79+
%% TODO ,remove the lastval() thing - that's a mysql thing
7880
case {Type, InnerRes} of
7981
{insert, {ok, _Count}} ->
8082
InsertRes = gen_server:call(Worker, {equery, <<"select lastval();">>, []}),
@@ -237,14 +239,69 @@ escape_binary(<<>>, Acc) ->
237239
Acc.
238240

239241

240-
primary_key(_DB, _Table) ->
241-
undefined.
242-
243-
is_auto_increment(_DB, _Table, _Field) ->
244-
undefined.
242+
primary_key(DB, Table) ->
243+
[T1, T2] = sql_bridge_utils:create_placeholders(2),
244+
SQL = [<<"SELECT column_name
245+
from information_schema.key_column_usage
246+
where
247+
table_catalog = ">>,T1,
248+
<<" and table_name = ">>,T2],
249+
case sql_bridge:ffl(SQL, [DB, Table]) of
250+
[F] -> sql_bridge_utils:to_atom(F);
251+
_ ->
252+
undefined
253+
end.
254+
255+
256+
is_auto_increment(DB, Table, Field) ->
257+
[T1, T2, T3] = sql_bridge_utils:create_placeholders(3),
258+
SQL = [<<"SELECT
259+
column_name,
260+
data_type,
261+
column_default,
262+
is_identity,
263+
identity_generation
264+
FROM information_schema.columns
265+
WHERE table_catalog = ">>,T1,
266+
<<" and table_name = ">>,T2,
267+
<<" and column_name = ">>,T3,
268+
<<" and (">>,
269+
%% PGsql < 10
270+
<<"column_default LIKE 'nextval%' ">>,
271+
%%-- PGsql 10+
272+
<<"OR is_identity = 'YES'">>,
273+
<<");">>],
274+
case sql_bridge:fffr(SQL, [DB, Table, Field]) of
275+
not_found -> false;
276+
_ -> true
277+
end.
245278

246-
get_field_type(_DB, _Table, _Field) ->
247-
undefined.
279+
-spec field_type(sql_bridge:db(), sql_bridge:table(), sql_bridge:field()) -> sql_bridge:field_type().
280+
field_type(DB, Table, Field) ->
281+
[T1, T2, T3] = sql_bridge_utils:create_placeholders(3),
282+
SQL = [
283+
<<"select udt_name, character_maximum_length
284+
from information_schema.columns
285+
where table_catalog=">>,T1,
286+
<<" and table_name=">>,T2,
287+
<<" and column_name=">>,T3
288+
],
289+
Res = sql_bridge:tfr(SQL, [DB, Table, Field]),
290+
io:format("Result: ~p~n",[Res]),
291+
case Res of
292+
{Type, Len} when Type=="varchar";
293+
Type=="bpchar";
294+
Type=="bytea";
295+
Type=="text" ->
296+
{text, Len};
297+
{Type, _} when Type=="int2";
298+
Type=="int4";
299+
Type=="int8" ->
300+
AType = list_to_atom(Type),
301+
{integer, sql_bridge_utils:int_ranges(AType)};
302+
{Type, _} when Type=="uuid" ->
303+
{uuid, undefined}
304+
end.
248305

249306
autokey(_DB, _Table) ->
250307
undefined.

src/sql_bridge_key_generator.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-module(sql_bridge_key_generator).
2+
3+
-callback generate(sql_bridge:table()) -> sql_bridge:value().

src/sql_bridge_mysql_otp.erl

Lines changed: 59 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
primary_key/2,
1616
is_auto_increment/3,
1717
%primary_key_with_auto_increment_and_type/2,
18-
auto_increment/2,
19-
get_field_type/3,
18+
%auto_increment/2,
19+
field_type/3,
2020
maybe_replace_tokens/2
2121
]).
2222

@@ -255,56 +255,50 @@ encode(Val) ->
255255
% _ -> undefined
256256
% end.
257257

258-
auto_increment(DB, Table) ->
259-
Field = primary_key(DB, Table),
260-
io:format("Primary Key: ~p -> ~p~n", [Table, Field]),
261-
ID = case get_field_type(DB, Table, Field) of
262-
{uuid, _} ->
263-
auto_increment_uuid();
264-
{text, MaxLength} ->
265-
auto_increment_string(MaxLength);
266-
{integer, {Min, Max}} ->
267-
auto_increment_integer(Min, Max)
268-
end,
269-
io:format("Auto-Generated ID: ~p~n",[ID]),
270-
FullTable = sql_bridge_utils:to_string(DB) ++ "." ++ sql_bridge_utils:to_string(Table),
271-
case sql_bridge:exists(FullTable, Field, ID) of
272-
true -> auto_increment(DB, Table);
273-
false -> ID
274-
end.
275-
276-
auto_increment_uuid() ->
277-
uuid:uuid_to_string(uuid:get_v4_urandom()).
278-
279-
auto_increment_string(MaxLength) ->
280-
[rand_char() || _ <- lists:seq(1, MaxLength)].
281-
282-
rand_char() ->
283-
case rand:uniform(62) of
284-
10 -> $0;
285-
X when X < 10 ->
286-
X + $0;
287-
X when X =< 36 ->
288-
X - 11 + $a;
289-
X ->
290-
X - 37 + $A
291-
end.
292-
293-
auto_increment_integer(Min, Max) ->
294-
Diff = Max - Min,
295-
rand:uniform(Diff) + Min.
296-
297-
298-
-type field_type() :: text | uuid | integer.
299-
-type string_length() :: integer().
300-
-type integer_range() :: {integer(), integer()}.
301-
-type field_details() :: undefined | string_length() | integer_range().
302-
303-
304-
-spec get_field_type(DB :: sql_bridge:db(),
258+
%%auto_increment(DB, Table) ->
259+
%% Field = primary_key(DB, Table),
260+
%% io:format("Primary Key: ~p -> ~p~n", [Table, Field]),
261+
%% ID = case get_field_type(DB, Table, Field) of
262+
%% {uuid, _} ->
263+
%% auto_increment_uuid();
264+
%% {text, MaxLength} ->
265+
%% auto_increment_string(MaxLength);
266+
%% {integer, {Min, Max}} ->
267+
%% auto_increment_integer(Min, Max)
268+
%% end,
269+
%% io:format("Auto-Generated ID: ~p~n",[ID]),
270+
%% FullTable = sql_bridge_utils:to_string(DB) ++ "." ++ sql_bridge_utils:to_string(Table),
271+
%% case sql_bridge:exists(FullTable, Field, ID) of
272+
%% true -> auto_increment(DB, Table);
273+
%% false -> ID
274+
%% end.
275+
%%
276+
%%auto_increment_uuid() ->
277+
%% uuid:uuid_to_string(uuid:get_v4_urandom()).
278+
%%
279+
%%auto_increment_string(MaxLength) ->
280+
%% [rand_char() || _ <- lists:seq(1, MaxLength)].
281+
%%
282+
%%rand_char() ->
283+
%% case rand:uniform(62) of
284+
%% 10 -> $0;
285+
%% X when X < 10 ->
286+
%% X + $0;
287+
%% X when X =< 36 ->
288+
%% X - 11 + $a;
289+
%% X ->
290+
%% X - 37 + $A
291+
%% end.
292+
%%
293+
%%auto_increment_integer(Min, Max) ->
294+
%% Diff = Max - Min,
295+
%% rand:uniform(Diff) + Min.
296+
297+
-spec field_type(DB :: sql_bridge:db(),
305298
Table :: sql_bridge:table(),
306-
Field :: sql_bridge:field()) -> undefined | {field_type(), field_details()}.
307-
get_field_type(DB, Table, Field) ->
299+
Field :: sql_bridge:field()) -> undefined | sql_bridge:field_type().
300+
301+
field_type(DB, Table, Field) ->
308302
[T1, T2, T3] = sql_bridge_utils:create_placeholders(3),
309303
SQL = [<<"select column_type from information_schema.columns ">>,
310304
<<" where table_schema=">>, T1,
@@ -348,19 +342,20 @@ parse_field_type(FieldType) ->
348342
end.
349343

350344
int_range(TypeUnsigned) ->
351-
case TypeUnsigned of
352-
{<<"tinyint">>, <<"unsigned">>} -> {0, 255};
353-
{<<"smallint">>, <<"unsigned">>} -> {0, 65535};
354-
{<<"mediumint">>, <<"unsigned">>} -> {0, 16777215};
355-
{<<"int">>, <<"unsigned">>} -> {0, 4294967295};
356-
{<<"bigint">>, <<"unsigned">>} -> {0, 18446744073709551615};
357-
358-
{<<"tinyint">>, _} -> {-128, 127};
359-
{<<"smallint">>, _} -> {-32768, 32767};
360-
{<<"mediumint">>, _} -> {-8388608, 8388607};
361-
{<<"int">>, _} -> {-2147483648, 2147483647};
362-
{<<"bigint">>, _} -> {-9223372036854775808, 9223372036854775807}
363-
end.
345+
AType = case TypeUnsigned of
346+
{<<"tinyint">>, <<"unsigned">>} -> uint1;
347+
{<<"smallint">>, <<"unsigned">>} -> uint2;
348+
{<<"mediumint">>, <<"unsigned">>} -> uint3;
349+
{<<"int">>, <<"unsigned">>} -> uint4;
350+
{<<"bigint">>, <<"unsigned">>} -> uint8;
351+
352+
{<<"tinyint">>, _} -> int1;
353+
{<<"smallint">>, _} -> int2;
354+
{<<"mediumint">>, _} -> int3;
355+
{<<"int">>, _} -> int4;
356+
{<<"bigint">>, _} -> int8
357+
end,
358+
sql_bridge_utils:int_ranges(AType).
364359

365360

366361
primary_key(DB, Table) ->
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
-module(sql_bridge_random_key_generator).
2+
-export([generate/1, generate/2]).
3+
-behavior(sql_bridge_key_generator).
4+
5+
generate(Table) ->
6+
Field = sql_bridge:primary_key(Table),
7+
generate(Table, Field).
8+
9+
generate(Table, Field) ->
10+
io:format("(generate) Primary Key: ~p -> ~p~n", [Table, Field]),
11+
ID = case sql_bridge:field_type(Table, Field) of
12+
{uuid, _} ->
13+
auto_increment_uuid();
14+
{text, MaxLength} ->
15+
auto_increment_string(MaxLength);
16+
{integer, {Min, Max}} ->
17+
auto_increment_integer(Min, Max)
18+
end,
19+
io:format("Auto-Generated ID: ~p~n",[ID]),
20+
case sql_bridge:exists(Table, Field, ID) of
21+
true -> generate(Table, Field);
22+
false -> ID
23+
end.
24+
25+
auto_increment_uuid() ->
26+
uuid:uuid_to_string(uuid:get_v4_urandom()).
27+
28+
auto_increment_string(MaxLength) ->
29+
[rand_char() || _ <- lists:seq(1, MaxLength)].
30+
31+
rand_char() ->
32+
case rand:uniform(62) of
33+
10 -> $0;
34+
X when X < 10 ->
35+
X + $0;
36+
X when X =< 36 ->
37+
X - 11 + $a;
38+
X ->
39+
X - 37 + $A
40+
end.
41+
42+
auto_increment_integer(Min, Max) ->
43+
Diff = Max - Min,
44+
rand:uniform(Diff) + Min.

src/sql_bridge_test.erl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ mysql_otp_trans_test_() ->
3737
fun trans_tests/1
3838
}.
3939

40-
%epgsql_trans_test_() ->
41-
% {setup,
42-
% fun() -> gen_setup(sql_bridge_epgsql, postgres, ?PG_HOST, 5432) end,
43-
% fun epgsql_cleanup/1,
44-
% fun main_tests/1
45-
% }.
46-
%
47-
%epgsql_test_() ->
48-
% {setup,
49-
% %% We are intentionally testing mysql-style token replacements on pgsql
50-
% %% (so 'mysql' is not a typo here)
51-
% fun() -> gen_setup(sql_bridge_epgsql, mysql, ?PG_HOST, 5432) end,
52-
% fun epgsql_cleanup/1,
53-
% fun trans_tests/1
54-
% }.
40+
epgsql_trans_test_() ->
41+
{setup,
42+
fun() -> gen_setup(sql_bridge_epgsql, postgres, ?PG_HOST, 5432) end,
43+
fun epgsql_cleanup/1,
44+
fun main_tests/1
45+
}.
46+
47+
epgsql_test_() ->
48+
{setup,
49+
%% We are intentionally testing mysql-style token replacements on pgsql
50+
%% (so 'mysql' is not a typo here)
51+
fun() -> gen_setup(sql_bridge_epgsql, mysql, ?PG_HOST, 5432) end,
52+
fun epgsql_cleanup/1,
53+
fun trans_tests/1
54+
}.
5555

5656

5757
gen_setup(Adapter, ReplacementType, Host, Port) ->

0 commit comments

Comments
 (0)