From 8894396719f5e8bbe47d0d83fb674ec53d9810d7 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 07:49:39 -0500 Subject: [PATCH 01/12] Fix failing text "missing spaces" --- test/sqerl_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index baff481..2a7021e 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -137,8 +137,8 @@ safe_test_() -> {select,distinct,name,{from,gymnast}}}}}) }, - {<<"SELECT name FROM developer WHERE name IN ((SELECT DISTINCT name FROM gymnast)" - "UNION (SELECT name FROM dancer WHERE ((name LIKE 'Mikhail%') OR (country = 'Russia')))" + {<<"SELECT name FROM developer WHERE name IN ((SELECT DISTINCT name FROM gymnast) " + "UNION (SELECT name FROM dancer WHERE ((name LIKE 'Mikhail%') OR (country = 'Russia'))) " "WHERE (name LIKE 'M%') ORDER BY name DESC LIMIT 5, 10)">>, ?_safe_test({select,name, {from,developer}, From 5290e8f2881ac8975742db1f574f43bb7dc3ff79 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 07:50:14 -0500 Subject: [PATCH 02/12] Add basic join test --- test/sqerl_tests.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 2a7021e..9de6f60 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -186,6 +186,9 @@ safe_test_() -> {<<"SELECT name FROM search_people(age := 18)">>, ?_safe_test({select,name,{from,{call,search_people,[{age, 18}]}}}) + }, + {<<"SELECT * FROM foo JOIN bar ON foo.bar_id = bar.id">>, + ?_safe_test({select,'*',{from,foo,{join,bar,[{'foo.bar_id','=','bar.id'}]}}}) } ] }. From 60011cb18eda3bc57960b9046ad6f5192856528a Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 15:58:29 -0500 Subject: [PATCH 03/12] Add join support --- src/sqerl.erl | 34 +++++++++++++++++++++++++++++++++- test/sqerl_tests.erl | 27 +++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/sqerl.erl b/src/sqerl.erl index c1d95d6..a8d58a4 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -204,7 +204,15 @@ select(Modifier, Fields, Tables, WhereExpr, Extras, Safe) -> [S1, convert(Modifier1), $\s] end, - ListFun = fun(Val) -> expr2(Val, Safe) end, + ListFun = fun({_, join, _, _}=Val) -> + join(Val, Safe); + ({_, {_, join}, _, _}=Val) -> + join(Val, Safe); + ({_, {_, _, join}, _, _}=Val) -> + join(Val, Safe); + (Val) -> + expr2(Val, Safe) + end, S3 = [S2, make_list(Fields, ListFun)], S4 = case Tables of undefined -> S3; @@ -221,6 +229,30 @@ select(Modifier, Fields, Tables, WhereExpr, Extras, Safe) -> Expr -> [S5, Expr] end. +join({Table, JoinType, Table2, JoinExpr}, Safe) -> + [ expr2(Table, Safe), + join(JoinType), + expr2(Table2, Safe), + <<" ON ">>, + make_list(JoinExpr, fun(Val) -> expr(Val, Safe) end) ]. + +join(join) -> + <<" JOIN ">>; +join({left, join}) -> + <<" LEFT JOIN ">>; +join({inner, join}) -> + <<" INNER JOIN ">>; +join({right, join}) -> + <<" RIGHT JOIN ">>; +join({left, outer, join}) -> + <<" LEFT OUTER JOIN ">>; +join({right, outer, join}) -> + <<" RIGHT OUTER JOIN ">>; +join({full, outer, join}) -> + <<" FULL OUTER JOIN ">>; +join({cross, join}) -> + <<" CROSS JOIN ">>. + where(undefined, _) -> []; where(Expr, true) when is_list(Expr); is_binary(Expr) -> throw({error, {unsafe_expression, Expr}}); diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 9de6f60..31998b3 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -187,8 +187,31 @@ safe_test_() -> {<<"SELECT name FROM search_people(age := 18)">>, ?_safe_test({select,name,{from,{call,search_people,[{age, 18}]}}}) }, - {<<"SELECT * FROM foo JOIN bar ON foo.bar_id = bar.id">>, - ?_safe_test({select,'*',{from,foo,{join,bar,[{'foo.bar_id','=','bar.id'}]}}}) + {<<"SELECT * FROM foo JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,join,bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo JOIN bar ON ((foo.bar_id = bar.id) AND (foo.bar_type = bar.type))">>, + ?_safe_test({select,'*',{from,{foo,join,bar,[ + {'and', [ + {'foo.bar_id','=','bar.id'}, + {'foo.bar_type','=','bar.type'} + ] + }]}}}) + }, + {<<"SELECT * FROM foo LEFT JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,{left,join},bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo INNER JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,{inner,join},bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo RIGHT JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,{right,join},bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo LEFT OUTER JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,{left, outer,join},bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo CROSS JOIN bar ON (foo.bar_id = bar.id)">>, + ?_safe_test({select,'*',{from,{foo,{cross,join},bar,{'foo.bar_id','=','bar.id'}}}}) } ] }. From 0145b03f28e44a0c529d88e470d1a95d9ca5922a Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 16:06:11 -0500 Subject: [PATCH 04/12] Refactor table make_list function --- src/sqerl.erl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/sqerl.erl b/src/sqerl.erl index a8d58a4..f7381b1 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -204,19 +204,11 @@ select(Modifier, Fields, Tables, WhereExpr, Extras, Safe) -> [S1, convert(Modifier1), $\s] end, - ListFun = fun({_, join, _, _}=Val) -> - join(Val, Safe); - ({_, {_, join}, _, _}=Val) -> - join(Val, Safe); - ({_, {_, _, join}, _, _}=Val) -> - join(Val, Safe); - (Val) -> - expr2(Val, Safe) - end, - S3 = [S2, make_list(Fields, ListFun)], + S3 = [S2, make_list(Fields, fun(Val) -> expr2(Val, Safe) end)], S4 = case Tables of undefined -> S3; - _Other -> [S3, <<" FROM ">>, make_list(Tables, ListFun)] + _Other -> [S3, <<" FROM ">>, + make_list(Tables, fun(Val) -> join(Val, Safe) end)] end, S5 = case where(WhereExpr, Safe) of @@ -234,7 +226,9 @@ join({Table, JoinType, Table2, JoinExpr}, Safe) -> join(JoinType), expr2(Table2, Safe), <<" ON ">>, - make_list(JoinExpr, fun(Val) -> expr(Val, Safe) end) ]. + make_list(JoinExpr, fun(Val) -> expr(Val, Safe) end) ]; +join(Table, Safe) -> + expr2(Table, Safe). join(join) -> <<" JOIN ">>; From 4aceacbf81ec4fe1368c4c661b06135d714823b2 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 16:51:27 -0500 Subject: [PATCH 05/12] Add join support for updates --- src/sqerl.erl | 9 +++++++-- test/sqerl_tests.erl | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sqerl.erl b/src/sqerl.erl index f7381b1..481adce 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -340,11 +340,16 @@ update(Table, Props, Safe) -> update(Table, Props, Where, Safe) when not is_list(Props) -> update(Table, [Props], Where, Safe); update(Table, Props, Where, Safe) -> - S1 = [<<"UPDATE ">>, convert(Table), <<" SET ">>], + S1 = case Table of + Table when is_tuple(Table) -> + join(Table, Safe); + _Other -> + convert(Table) + end, S2 = make_list(Props, fun({Field, Val}) -> [convert(Field), <<" = ">>, expr(Val, Safe)] end), - [S1, S2, where(Where, Safe)]. + [<<"UPDATE ">>, S1, <<" SET ">>, S2, where(Where, Safe)]. delete(Table, Safe) -> delete(Table, undefined, undefined, undefined, Safe). diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 31998b3..237cc00 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -44,6 +44,16 @@ safe_test_() -> {where,{'not',{a,'=',5}}}}) }, + {<<"UPDATE project JOIN client ON (project.client_id = client.id) SET foo = 5">>, + ?_safe_test({update, + {project,join,client,{'project.client_id','=','client.id'}},[{foo,5}]}) + }, + + {<<"UPDATE project INNER JOIN client ON (project.client_id = client.id) SET foo = 5">>, + ?_safe_test({update, + {project,{inner,join},client,{'project.client_id','=','client.id'}},[{foo,5}]}) + }, + {<<"DELETE FROM project">>, ?_safe_test({delete,project}) }, From 78fb47eb8f3bef3d36299aa80dbd6db0e5fb6199 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 16:51:58 -0500 Subject: [PATCH 06/12] Add join support for deletes --- src/sqerl.erl | 22 ++++++++++++++-------- test/sqerl_tests.erl | 5 +++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/sqerl.erl b/src/sqerl.erl index 481adce..592c765 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -358,18 +358,24 @@ delete(Table, Using, WhereExpr, Safe) -> delete(Table, Using, WhereExpr, undefined, Safe). delete(Table, Using, WhereExpr, Extras, Safe) -> - S1 = [<<"DELETE FROM ">>, convert(Table)], - S2 = if Using =:= undefined -> S1; - true -> [S1, <<" USING ">>, make_list(Using, fun convert/1)] + S1 = case Table of + Table when is_tuple(Table) -> + join(Table, Safe); + _Other -> + convert(Table) + end, + S2 = [<<"DELETE FROM ">>, S1], + S3 = if Using =:= undefined -> S2; + true -> [S2, <<" USING ">>, make_list(Using, fun convert/1)] end, - S3 = case where(WhereExpr, Safe) of - undefined -> S2; - WhereClause -> [S2, WhereClause] + S4 = case where(WhereExpr, Safe) of + undefined -> S3; + WhereClause -> [S3, WhereClause] end, if Extras =:= undefined -> - S3; + S4; true -> - [S3, extra_clause(Extras, Safe)] + [S4, extra_clause(Extras, Safe)] end. convert(Val) when is_atom(Val)-> diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 237cc00..80cb8e2 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -62,6 +62,11 @@ safe_test_() -> ?_safe_test({delete,project,{a,'=',5}}) }, + {<<"DELETE FROM project JOIN client ON (project.client_id = client.id) WHERE (client.a = 8)">>, + ?_safe_test({delete,{project,join,client, + {'project.client_id','=','client.id'}},{'client.a','=',8}}) + }, + {<<"DELETE FROM project WHERE (a = 5)">>, ?_safe_test({delete,{from,project},{where,{a,'=',5}}}) }, From 472aa5c821a335025a7cc1eb848ee7d93b032125 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 16:52:12 -0500 Subject: [PATCH 07/12] Clean extra space --- test/sqerl_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 80cb8e2..735a441 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -223,7 +223,7 @@ safe_test_() -> ?_safe_test({select,'*',{from,{foo,{right,join},bar,{'foo.bar_id','=','bar.id'}}}}) }, {<<"SELECT * FROM foo LEFT OUTER JOIN bar ON (foo.bar_id = bar.id)">>, - ?_safe_test({select,'*',{from,{foo,{left, outer,join},bar,{'foo.bar_id','=','bar.id'}}}}) + ?_safe_test({select,'*',{from,{foo,{left,outer,join},bar,{'foo.bar_id','=','bar.id'}}}}) }, {<<"SELECT * FROM foo CROSS JOIN bar ON (foo.bar_id = bar.id)">>, ?_safe_test({select,'*',{from,{foo,{cross,join},bar,{'foo.bar_id','=','bar.id'}}}}) From 8c114d688dcaf908e9f1cb6ecbfdac6272c810c7 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 19:48:35 -0500 Subject: [PATCH 08/12] Support multiple joins --- src/sqerl.erl | 13 +++++++++++-- test/sqerl_tests.erl | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sqerl.erl b/src/sqerl.erl index 592c765..e607a80 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -221,12 +221,21 @@ select(Modifier, Fields, Tables, WhereExpr, Extras, Safe) -> Expr -> [S5, Expr] end. -join({Table, JoinType, Table2, JoinExpr}, Safe) -> +join({Table, Join, Table2, JoinExpr}, Safe) -> [ expr2(Table, Safe), - join(JoinType), + join(Join), expr2(Table2, Safe), <<" ON ">>, make_list(JoinExpr, fun(Val) -> expr(Val, Safe) end) ]; +join({Table, Joins}, Safe) when is_list(Joins) -> + S1 = lists:map(fun({Join, Table2, JoinExpr}) -> + [ join(Join), + expr2(Table2, Safe), + <<" ON ">>, + make_list(JoinExpr, fun(Val) -> expr(Val, Safe)end) + ] + end, Joins), + [expr2(Table, Safe), S1]; join(Table, Safe) -> expr2(Table, Safe). diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 735a441..e24ec9b 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -227,6 +227,9 @@ safe_test_() -> }, {<<"SELECT * FROM foo CROSS JOIN bar ON (foo.bar_id = bar.id)">>, ?_safe_test({select,'*',{from,{foo,{cross,join},bar,{'foo.bar_id','=','bar.id'}}}}) + }, + {<<"SELECT * FROM foo JOIN bar ON (foo.bar_id = bar.id) JOIN baz ON (bar.baz_id = baz.id)">>, + ?_safe_test({select,'*',{from,{foo,[ {join,bar,{'foo.bar_id','=','bar.id'}},{join,baz,{'bar.baz_id','=','baz.id'}}]}}}) } ] }. From 8619f2d60e5358572f559f16636935e52d8fe4ef Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 20:02:15 -0500 Subject: [PATCH 09/12] Add test for table aliases with joins --- test/sqerl_tests.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index e24ec9b..1f909be 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -205,6 +205,9 @@ safe_test_() -> {<<"SELECT * FROM foo JOIN bar ON (foo.bar_id = bar.id)">>, ?_safe_test({select,'*',{from,{foo,join,bar,{'foo.bar_id','=','bar.id'}}}}) }, + {<<"SELECT * FROM foo AS f JOIN bar AS b ON (f.bar_id = b.id)">>, + ?_safe_test({select,'*',{from,{{foo,as,f},join,{bar,as,b},{'f.bar_id','=','b.id'}}}}) + }, {<<"SELECT * FROM foo JOIN bar ON ((foo.bar_id = bar.id) AND (foo.bar_type = bar.type))">>, ?_safe_test({select,'*',{from,{foo,join,bar,[ {'and', [ From b3006aca8ed54c1d58de2eea6eb8a8369e00c2d4 Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 20:02:38 -0500 Subject: [PATCH 10/12] Reformat long lined test --- test/sqerl_tests.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index 1f909be..a2c63a3 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -232,7 +232,8 @@ safe_test_() -> ?_safe_test({select,'*',{from,{foo,{cross,join},bar,{'foo.bar_id','=','bar.id'}}}}) }, {<<"SELECT * FROM foo JOIN bar ON (foo.bar_id = bar.id) JOIN baz ON (bar.baz_id = baz.id)">>, - ?_safe_test({select,'*',{from,{foo,[ {join,bar,{'foo.bar_id','=','bar.id'}},{join,baz,{'bar.baz_id','=','baz.id'}}]}}}) + ?_safe_test({select,'*',{from,{foo,[ {join,bar,{'foo.bar_id','=','bar.id'}}, + {join,baz,{'bar.baz_id','=','baz.id'}} ]}}}) } ] }. From 99306f9a2adc55470a2c0c6d3319400a76544a6e Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Thu, 11 Sep 2014 20:05:32 -0500 Subject: [PATCH 11/12] Ignore rebar files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1d3febb..fcb4d5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .eunit *.beam +.rebar/* From 2311d26570ff6d99337dd3b19df244266fc90f0c Mon Sep 17 00:00:00 2001 From: Aaron Spiegel Date: Fri, 12 Sep 2014 20:20:21 -0500 Subject: [PATCH 12/12] Add support for calling functions with functions as params --- src/sqerl.erl | 4 ++++ test/sqerl_tests.erl | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/sqerl.erl b/src/sqerl.erl index e607a80..eaf074d 100644 --- a/src/sqerl.erl +++ b/src/sqerl.erl @@ -474,6 +474,10 @@ expr2(undefined, _Safe) -> <<"NULL">>; expr2(Expr, _Safe) when is_atom(Expr) -> convert(Expr); expr2(Expr, Safe) -> expr(Expr, Safe). +param({call, FuncName, []}) -> + [convert(FuncName), <<"()">>]; +param({call, FuncName, Params}) -> + [convert(FuncName), $(, make_list(Params, fun param/1), $)]; param({Key, Value}) when is_atom(Key) -> [convert(Key), <<" := ">>, encode(Value)]; param(Key) when is_atom(Key) -> diff --git a/test/sqerl_tests.erl b/test/sqerl_tests.erl index a2c63a3..19832ca 100644 --- a/test/sqerl_tests.erl +++ b/test/sqerl_tests.erl @@ -109,6 +109,14 @@ safe_test_() -> ?_safe_test({select,{call,count,[name]},{from,developer}}) }, + {<<"SELECT count(name) AS c FROM developer">>, + ?_safe_test({select,{{call,count,[name]},as,c},{from,developer}}) + }, + + {<<"SELECT CONCAT('-- [', GROUP_CONCAT(comment.id), ']') AS comments FROM posts">>, + ?_safe_test({select,{{call,'CONCAT',["-- [",{call,'GROUP_CONCAT',['comment.id']},"]"]},as,comments},{from,posts}}) + }, + {<<"SELECT last_insert_id()">>, ?_safe_test({select,{call,last_insert_id,[]}}) },