Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang
erma
erma-1.3-git.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File erma-1.3-git.patch of Package erma
diff --git a/include/erma.hrl b/include/erma.hrl index 37ce683..0dea8ed 100644 --- a/include/erma.hrl +++ b/include/erma.hrl @@ -4,7 +4,8 @@ {date, calendar:date()} | {time, calendar:time()} | {datetime, calendar:datetime()} | - {pl, term()}. % placeholder + {pl, term()} | % placeholder + function_call(). -type select() :: select | select_distinct. @@ -12,7 +13,12 @@ -type agg_fun() :: atom(). --type field() :: name() | {name(), as, name()} | {raw, name()} | {agg_fun(), name()} | {agg_fun(), name(), as, name()}. +-type field() :: name() | + {name(), as, name()} | + {raw, name()} | + {agg_fun(), name()} | + {agg_fun(), name(), as, name()} | + function_call(). -type joins() :: {joins, [join()]}. -type join() :: {join_type(), join_tables()} | @@ -25,6 +31,8 @@ -type where_action() :: '=' | '<>' | '<' | lt | '>' | gt | '>=' | '<=' | like. +-type where_key() :: name() | function_call(). + -type where_value() :: value() | select_query(). -type where_condition() :: {name(), where_value()} | @@ -48,6 +56,8 @@ -type returning() :: {returning, id} | {returning, [name()]}. +-type function_call() :: {function, name(), [value()]}. + -type select_query() :: {select(), [field()], table_name()} | {select(), [field()], table_name(), [joins() | where() | group() | having() | order() | limit()]}. diff --git a/src/erma.erl b/src/erma.erl index 02a9be3..600963e 100644 --- a/src/erma.erl +++ b/src/erma.erl @@ -1,7 +1,9 @@ -module(erma). -export([build/1, build/2, append/2, resolve_placeholders/1, resolve_placeholders/2]). --import(erma_utils, [prepare_table_name/2, prepare_name/2, prepare_value/1, prepare_limit/1]). +-import(erma_utils, [ + prepare_table_name/2, prepare_name/2, prepare_value/2, prepare_limit/1, prepare_function/3 +]). -include("erma.hrl"). @@ -133,7 +135,7 @@ build_insert({_, Table, Names, Rows, Entities}, #{database := Database}) -> [" (", N2, ")"] end, Rows2 = lists:map(fun(V1) -> - V2 = lists:map(fun erma_utils:prepare_value/1, V1), + V2 = lists:map(fun(Value) -> erma_utils:prepare_value(Value, Database) end, V1), ["(", string:join(V2, ", "), ")"] end, Rows), Rows3 = string:join(Rows2, ", "), @@ -146,7 +148,7 @@ build_insert({_, Table, Names, Rows, Entities}, #{database := Database}) -> -spec build_update(update_query(), erma_options()) -> sql(). build_update({_, Table, KV, Entities}, #{database := Database}) -> Values = lists:map(fun({K, V}) -> - [prepare_name(K, Database), " = ", prepare_value(V)] + [prepare_name(K, Database), " = ", prepare_value(V, Database)] end, KV), Values2 = string:join(Values, ", "), unicode:characters_to_binary(["UPDATE ", prepare_table_name(Table, Database), @@ -174,6 +176,7 @@ build_fields(Fields, Database) -> ({AggFun, Name}) -> [string:to_upper(atom_to_list(AggFun)), "(", prepare_name(Name, Database), ")"]; + ({function, Name, Arguments}) -> prepare_function(Name, Arguments, Database); (Name) -> prepare_name(Name, Database) end, Fields), @@ -272,71 +275,82 @@ build_where_condition({'and', WConditions}, Database) -> [] -> []; _ -> ["(", string:join(W, " AND "), ")"] end; +build_where_condition({function, Name, Arguments}, Database) -> + prepare_function(Name, Arguments, Database); build_where_condition({Key, '=', Value}, Database) -> - [prepare_name(Key, Database), " = ", build_where_value(Value)]; + [build_where_key(Key, Database), " = ", build_where_value(Value, Database)]; build_where_condition({Key, '<>', Value}, Database) -> - [prepare_name(Key, Database), " <> ", build_where_value(Value)]; + [build_where_key(Key, Database), " <> ", build_where_value(Value, Database)]; build_where_condition({Key, '>', Value}, Database) -> - [prepare_name(Key, Database), " > ", build_where_value(Value)]; + [build_where_key(Key, Database), " > ", build_where_value(Value, Database)]; build_where_condition({Key, gt, Value}, Database) -> - [prepare_name(Key, Database), " > ", build_where_value(Value)]; + [build_where_key(Key, Database), " > ", build_where_value(Value, Database)]; build_where_condition({Key, '<', Value}, Database) -> - [prepare_name(Key, Database), " < ", build_where_value(Value)]; + [build_where_key(Key, Database), " < ", build_where_value(Value, Database)]; build_where_condition({Key, lt, Value}, Database) -> - [prepare_name(Key, Database), " < ", build_where_value(Value)]; + [build_where_key(Key, Database), " < ", build_where_value(Value, Database)]; build_where_condition({Key, '>=', Value}, Database) -> - [prepare_name(Key, Database), " >= ", build_where_value(Value)]; + [build_where_key(Key, Database), " >= ", build_where_value(Value, Database)]; build_where_condition({Key, '<=', Value}, Database) -> - [prepare_name(Key, Database), " <= ", build_where_value(Value)]; + [build_where_key(Key, Database), " <= ", build_where_value(Value, Database)]; build_where_condition({Key, true}, Database) -> - [prepare_name(Key, Database), " = true"]; + [build_where_key(Key, Database), " = true"]; build_where_condition({Key, false}, Database) -> - [prepare_name(Key, Database), " = false"]; + [build_where_key(Key, Database), " = false"]; build_where_condition({Key, like, Value}, Database) when is_list(Value) -> - [prepare_name(Key, Database), " LIKE ", build_where_value(Value)]; + [build_where_key(Key, Database), " LIKE ", build_where_value(Value, Database)]; build_where_condition({Key, in, []}, Database) -> - [prepare_name(Key, Database), " IN (NULL)"]; + [build_where_key(Key, Database), " IN (NULL)"]; build_where_condition({Key, in, Values}, Database) when is_list(Values) -> - V = lists:map(fun build_where_value/1, Values), - [prepare_name(Key, Database), " IN (", string:join(V, ", "), ")"]; + V = lists:map(fun(Value) -> build_where_value(Value, Database) end, Values), + [build_where_key(Key, Database), " IN (", string:join(V, ", "), ")"]; build_where_condition({Key, in, SubQuery}, Database) when is_tuple(SubQuery) -> - [prepare_name(Key, Database), " IN ", build_where_value(SubQuery)]; + [build_where_key(Key, Database), " IN ", build_where_value(SubQuery, Database)]; build_where_condition({Key, not_in, []}, Database) -> - [prepare_name(Key, Database), " NOT IN (NULL)"]; + [build_where_key(Key, Database), " NOT IN (NULL)"]; build_where_condition({Key, not_in, Values}, Database) when is_list(Values) -> - V = lists:map(fun build_where_value/1, Values), - [prepare_name(Key, Database), " NOT IN (", string:join(V, ", "), ")"]; + V = lists:map(fun(Value) -> build_where_value(Value, Database) end, Values), + [build_where_key(Key, Database), " NOT IN (", string:join(V, ", "), ")"]; build_where_condition({Key, not_in, SubQuery}, Database) when is_tuple(SubQuery) -> - [prepare_name(Key, Database), " NOT IN ", build_where_value(SubQuery)]; + [build_where_key(Key, Database), " NOT IN ", build_where_value(SubQuery, Database)]; build_where_condition({Key, is, Is}, Database) -> - build_is(prepare_name(Key, Database), Is, Database); + build_is(build_where_key(Key, Database), Is, Database); build_where_condition({Key, between, Value1, Value2}, Database) -> - [prepare_name(Key, Database), " BETWEEN ", build_where_value(Value1), " AND ", build_where_value(Value2)]; + [build_where_key(Key, Database), " BETWEEN ", build_where_value(Value1, Database), " AND ", build_where_value(Value2, Database)]; build_where_condition({Key, Value}, Database) -> - [prepare_name(Key, Database), " = ", build_where_value(Value)]. + [build_where_key(Key, Database), " = ", build_where_value(Value, Database)]; +build_where_condition({Key, Op, Value}, Database) when is_atom(Op) -> + [build_where_key(Key, Database), erlang:atom_to_list(Op), build_where_value(Value, Database)]. build_is(PrepKey, null, _) -> [PrepKey, " IS NULL"]; build_is(PrepKey, not_null, _) -> [PrepKey, " IS NOT NULL"]; -build_is(PrepKey, {distinct_from, Val}, postgresql) -> +build_is(PrepKey, {distinct_from, Val}, postgresql=Database) -> %% use {not, {Key, is, {distinct_from, Val}}} as equivalent for `IS NOT DISTINCT FROM` - [PrepKey, " IS DISTINCT FROM ", build_where_value(Val)]; -build_is(PrepKey, {distinct_from, Val}, mysql) -> - [PrepKey, " <=> ", build_where_value(Val)]; -build_is(PrepKey, Bool, _) -> + [PrepKey, " IS DISTINCT FROM ", build_where_value(Val, Database)]; +build_is(PrepKey, {distinct_from, Val}, mysql=Database) -> + [PrepKey, " <=> ", build_where_value(Val, Database)]; +build_is(PrepKey, Bool, Database) -> %% use {not, {Key, is, true}} as equivalent for `IS NOT TRUE` - [PrepKey, " IS ", build_where_value(Bool)]. + [PrepKey, " IS ", build_where_value(Bool, Database)]. +-spec build_where_key(where_key(), database()) -> iolist(). +build_where_key({function, Name, Arguments}, Database) -> prepare_function(Name, Arguments, Database); +build_where_key(Key, Database) -> prepare_name(Key, Database). --spec build_where_value(value() | select_query()) -> iolist(). -build_where_value({select, _, _} = Query) -> ["(", build(Query), ")"]; -build_where_value({select, _, _, _} = Query) -> ["(", build(Query), ")"]; -build_where_value({select_distinct, _, _} = Query) -> ["(", build(Query), ")"]; -build_where_value({select_distinct, _, _, _} = Query) -> ["(", build(Query), ")"]; -build_where_value(Value) -> prepare_value(Value). +-spec build_where_value(where_value(), database()) -> iolist(). +build_where_value({select, _, _} = Query, Database) -> + ["(", build(Query, #{database => Database}), ")"]; +build_where_value({select, _, _, _} = Query, Database) -> + ["(", build(Query, #{database => Database}), ")"]; +build_where_value({select_distinct, _, _} = Query, Database) -> + ["(", build(Query, #{database => Database}), ")"]; +build_where_value({select_distinct, _, _, _} = Query, Database) -> + ["(", build(Query, #{database => Database}), ")"]; +build_where_value(Value, Database) -> prepare_value(Value, Database). -spec build_group(list(), database()) -> iolist(). diff --git a/src/erma_utils.erl b/src/erma_utils.erl index b916119..b75e5c9 100644 --- a/src/erma_utils.erl +++ b/src/erma_utils.erl @@ -4,7 +4,8 @@ valid_name/1, prepare_table_name/2, prepare_name/2, - prepare_value/1, + prepare_function/3, + prepare_value/2, prepare_limit/1, format_date/1, format_time/1, format_datetime/1]). -include("erma.hrl"). @@ -37,7 +38,7 @@ prepare_table_name(Name, Database) -> prepare_name(Name, Database). -spec prepare_name(name() | [name()], database()) -> iolist(). -prepare_name({raw, Raw}, Database) -> Raw; +prepare_name({raw, Raw}, _Database) -> Raw; prepare_name(Name, Database) when is_atom(Name) -> prepare_name(atom_to_list(Name), Database); prepare_name(Name, Database) when is_binary(Name) -> prepare_name(unicode:characters_to_list(Name), Database); prepare_name(Name0, Database) -> @@ -59,11 +60,21 @@ prepare_name(Name0, Database) -> end). +-spec prepare_function(name(), [value()], database()) -> iolist(). +prepare_function(Name, Arguments0, Database) -> + Arguments1 = lists:map(fun(Arg) -> prepare_argument(Arg, Database) end, Arguments0), + [prepare_name(Name, Database), "(", string:join(Arguments1, ", "), ")"]. + + -spec escape_char(database()) -> iolist(). escape_char(postgresql) -> "\""; escape_char(mysql) -> "`". +-spec prepare_value(value(), database()) -> iolist(). +prepare_value({function, Name, Arguments}, Database) -> prepare_function(Name, Arguments, Database); +prepare_value(Value, _Database) -> prepare_value(Value). + -spec prepare_value(value()) -> iolist(). prepare_value({pl, _} = V) -> throw({not_resolved_placeholder, V}); prepare_value({date, D}) -> ["'", erma_utils:format_date(D), "'"]; @@ -109,10 +120,18 @@ format_datetime({Date, Time}) -> %%% inner functions --spec add_zero(integer()) -> string(). -add_zero(Num) when Num > 9 -> integer_to_list(Num); -add_zero(Num) -> [$0 | integer_to_list(Num)]. +prepare_argument({function, Name, Arguments}, Database) -> + prepare_function(Name, Arguments, Database); +prepare_argument(Argument, _) when is_list(Argument) -> Argument; % column name +prepare_argument(Argument, _) -> prepare_value(Argument). +-spec add_zero(number()) -> string(). +add_zero(Num) when is_integer(Num) andalso Num > 9 -> integer_to_list(Num); +add_zero(Num) when is_integer(Num) -> [$0 | integer_to_list(Num)]; +add_zero(Num) when is_float(Num) andalso Num >= 10.0 -> + float_to_list(Num, [{decimals, 6}, compact]); +add_zero(Num) when is_float(Num) -> + [$0 | float_to_list(Num, [{decimals, 6}, compact])]. -spec valid_char(integer()) -> boolean(). valid_char(Char) -> diff --git a/test/delete_tests.erl b/test/delete_tests.erl index 4e992da..4cab1b4 100644 --- a/test/delete_tests.erl +++ b/test/delete_tests.erl @@ -22,5 +22,17 @@ delete_test_() -> {delete, users, [{where, [{"id", 3}]}]}, %% <<"DELETE FROM users WHERE id = 3">> + }, + { + %% + {delete, users, [{where, [{{function, calculate_rating, ["created_at", "score"]}, '>', 100}]}]}, + %% + <<"DELETE FROM users WHERE calculate_rating(created_at, score) > 100">> + }, + { + %% + {delete, users, [{where, [{function, isnull, ["name"]}]}]}, + %% + <<"DELETE FROM users WHERE \"isnull\"(name)">> } ]). diff --git a/test/insert_tests.erl b/test/insert_tests.erl index e573b99..48fe9f1 100644 --- a/test/insert_tests.erl +++ b/test/insert_tests.erl @@ -50,5 +50,11 @@ insert_test_() -> {insert_rows, "users", [], [[1, "Bob", "Dou", 25], [2, "Bill", "Foo", 31]]}, %% <<"INSERT INTO users VALUES (1, 'Bob', 'Dou', 25), (2, 'Bill', 'Foo', 31)">> + }, + { + %% + {insert_rows, "users", [], [[1, "Bob", "Dou", {function, now, []}], [2, "Bill", "Foo", {function, now, []}]]}, + %% + <<"INSERT INTO users VALUES (1, 'Bob', 'Dou', now()), (2, 'Bill', 'Foo', now())">> } ]). diff --git a/test/select_tests.erl b/test/select_tests.erl index c200c1d..05d7b6a 100644 --- a/test/select_tests.erl +++ b/test/select_tests.erl @@ -177,5 +177,80 @@ select_test_() -> <<"SELECT SUM(id) ", "FROM \"user\" ", "WHERE fname <> 'Bob'">> + }, + { + %% + {select, [{sum, "id"}], user}, + %% + <<"SELECT SUM(id) FROM \"user\"">> + }, + { + %% + {select, [{sum, "id", as, "total"}], user}, + %% + <<"SELECT SUM(id) AS total FROM \"user\"">> + }, + { + %% + {select, [{function, now, []}], user}, + %% + <<"SELECT now() FROM \"user\"">> + }, + { + %% + {select, [{function, to_char, ["created_at", <<"HH24:MI:SS">>]}], user}, + %% + <<"SELECT to_char(created_at, 'HH24:MI:SS') FROM \"user\"">> + }, + { + %% + {select, [{function, age, [{function, now, []}, "created_at"]}], user}, + %% + <<"SELECT age(now(), created_at) FROM \"user\"">> + }, + { + %% + {select, ["id"], user, + [{where, [{"created_at", '<', {function, now, []}}]}]}, + %% + <<"SELECT id ", + "FROM \"user\" ", + "WHERE created_at < now()">> + }, + { + %% + {select, ["id"], user, + [{where, [{{function, calculate_rating, ["created_at", "score"]}, '>', 100}]}]}, + %% + <<"SELECT id ", + "FROM \"user\" ", + "WHERE calculate_rating(created_at, score) > 100">> + }, + { + %% + {select, ["id"], user, + [{where, [{{function, calculate_rating, [{function, now, []}, "score"]}, '>', 100}]}]}, + %% + <<"SELECT id ", + "FROM \"user\" ", + "WHERE calculate_rating(now(), score) > 100">> + }, + { + %% + {select, ["id"], user, + [{where, [{{function, calculate_rating, ["user.created_at", "user.score"]}, '>', 100}]}]}, + %% + <<"SELECT id ", + "FROM \"user\" ", + "WHERE calculate_rating(user.created_at, user.score) > 100">> + }, + { + %% + {select, ["id"], user, + [{where, [{function, isnull, ["name"]}]}]}, + %% + <<"SELECT id ", + "FROM \"user\" ", + "WHERE \"isnull\"(name)">> } ]). diff --git a/test/update_tests.erl b/test/update_tests.erl index 722e6ab..6bfa160 100644 --- a/test/update_tests.erl +++ b/test/update_tests.erl @@ -46,5 +46,32 @@ update_test_() -> {update, "users", [{"first", "Chris"}, {"last", "?"}], [{where, [{"id", "?"}]}]}, %% <<"UPDATE users SET \"first\" = 'Chris', \"last\" = ? WHERE id = ?">> + }, + { + %% + {update, "users", [{"logged_out", {function, now, []}}], [{where, [{"id", 3}]}]}, + %% + <<"UPDATE users SET logged_out = now() WHERE id = 3">> + }, + { + %% + {update, "users", [{"rating", {function, calculate_rating, [{function, now, []}, "score"]}}], [{where, [{"id", 3}]}]}, + %% + <<"UPDATE users SET rating = calculate_rating(now(), score) WHERE id = 3">> + }, + { + %% + {update, "users", + [{"status", 2}], + [{where, [{{function, calculate_rating, ["created_at", "score"]}, '>', 100}]}] + }, + %% + <<"UPDATE users SET \"status\" = 2 WHERE calculate_rating(created_at, score) > 100">> + }, + { + %% + {update, "users", [{"first", "Chris"}], [{where, [{function, isnull, ["first"]}]}]}, + %% + <<"UPDATE users SET \"first\" = 'Chris' WHERE \"isnull\"(first)">> } ]). diff --git a/test/where_tests.erl b/test/where_tests.erl index 19d9f58..a37d03a 100644 --- a/test/where_tests.erl +++ b/test/where_tests.erl @@ -170,5 +170,20 @@ where_test_() -> %% <<"SELECT * FROM test WHERE id IS NOT NULL AND col1 IS NULL " "AND col2 IS DISTINCT FROM 'wasd' AND col3 IS true">> + }, + + %% tests for custom operators, see PostgreSQL manual for more examples: + %% https://www.postgresql.org/docs/10/functions-array.html + { + %% + {select, [], "test", [{where, [{"ids", '&&', "{1, 2, 3}" }]}]}, + %% + <<"SELECT * FROM test WHERE ids&&'{1, 2, 3}'">> + }, + { + %% + {select, [], "test", [{where, [{"ids", '||', 10 }]}]}, + %% + <<"SELECT * FROM test WHERE ids||10">> } ]).
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor