From 71989497f40ebe3ba6590f99f4aaa9fe267e735c Mon Sep 17 00:00:00 2001 From: Zachary Kessin Date: Sun, 26 Jan 2014 13:23:41 +0200 Subject: [PATCH 1/4] bump lager version --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 1b2ef2b2..b95e0426 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ {parse_transform, import_as} ]}. {deps, [ - {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "2.0.1"}}}, + {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "2.0.2"}}}, {erlando, ".*", {git, "git://github.com/travelping/erlando.git", "HEAD"}}, {aleppo, ".*", {git, "git://github.com/ChicagoBoss/aleppo.git", {tag, "bef139e4c7"}}}, {bson, ".*", {git, "git://github.com/mongodb/bson-erlang.git", {tag, "6d3cc910ea"}}}, From d096a68ed86491afea5d257bce46a99b9fc73116 Mon Sep 17 00:00:00 2001 From: "David N. Welton" Date: Mon, 27 Jan 2014 11:39:55 +0100 Subject: [PATCH 2/4] Added paginate function to help users paginate through database results. --- src/boss_db.erl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/boss_db.erl b/src/boss_db.erl index e2a31450..810769ff 100644 --- a/src/boss_db.erl +++ b/src/boss_db.erl @@ -21,6 +21,7 @@ find_last/4, count/1, count/2, + paginate/3, count/3, counter/1, counter/2, @@ -65,6 +66,7 @@ -define(DEFAULT_TIMEOUT, (30 * 1000)). -define(POOLNAME, boss_db_pool). +-define(DEFAULT_PAGE_SIZE, 10). start(Options) -> AdapterName = proplists:get_value(adapter, Options, mock), @@ -257,6 +259,23 @@ counter(Key) -> counter(Key, Timeout) -> db_call({counter, Key}, Timeout). +%% @spec paginate( Model::atom(), Conditions, Opts ) -> Value | {error, Reason} +%% @doc Paginate through the results from boss_db:find. Use `Opts' {page, +%% PageNum} and {page_size, PageSize} to control which page to fetch, +%% and how many results per page. Page size defaults to 10. +paginate(Model, Conditions, Opts) -> + CleanOpts = proplists:delete(offset, proplists:delete(limit, Opts)), + Page = proplists:get_value(page, Opts, 1), + PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), + OptList = proplists:delete(page_size, proplists:delete(page, CleanOpts)) ++ + [{offset, PageSize * (Page - 1)}, {limit, PageSize}], + Total = boss_db:count(Model, Conditions), + TotalPages = (Total div PageSize) + (case Total rem PageSize of + 0 -> 0; + _ -> 1 + end), + {Page, TotalPages, boss_db:find(Model, Conditions, OptList)}. + %% @spec incr( Id::string() ) -> integer() %% @doc Treat the record associated with `Id' as a counter and atomically increment its value by 1. incr(Key) -> From ad4cff6b7e2a1873fc5913009dc2bdebfa77bb6b Mon Sep 17 00:00:00 2001 From: Zachary Kessin Date: Mon, 27 Jan 2014 15:08:19 +0200 Subject: [PATCH 3/4] Fixed a bunch of escaping issues, we now escape column names in inserts and updates and escaping is done right on input strings fix #164 --- src/boss_db.erl | 1 + src/db_adapters/boss_db_adapter_mysql.erl | 26 ++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/boss_db.erl b/src/boss_db.erl index e2a31450..1ec6273f 100644 --- a/src/boss_db.erl +++ b/src/boss_db.erl @@ -69,6 +69,7 @@ start(Options) -> AdapterName = proplists:get_value(adapter, Options, mock), Adapter = list_to_atom(lists:concat(["boss_db_adapter_", AdapterName])), + lager:info("Start Database Adapter ~p options ~p", [Adapter, Options]), Adapter:start(Options), lists:foldr(fun(ShardOptions, Acc) -> case proplists:get_value(db_shard_models, ShardOptions, []) of diff --git a/src/db_adapters/boss_db_adapter_mysql.erl b/src/db_adapters/boss_db_adapter_mysql.erl index 9e86c3b4..710c58ae 100644 --- a/src/db_adapters/boss_db_adapter_mysql.erl +++ b/src/db_adapters/boss_db_adapter_mysql.erl @@ -12,13 +12,13 @@ stop() -> ok. init(Options) -> - DBHost = proplists:get_value(db_host, Options, "localhost"), - DBPort = proplists:get_value(db_port, Options, 3306), - DBUsername = proplists:get_value(db_username, Options, "guest"), - DBPassword = proplists:get_value(db_password, Options, ""), - DBDatabase = proplists:get_value(db_database, Options, "test"), + DBHost = proplists:get_value(db_host, Options, "localhost"), + DBPort = proplists:get_value(db_port, Options, 3306), + DBUsername = proplists:get_value(db_username, Options, "guest"), + DBPassword = proplists:get_value(db_password, Options, ""), + DBDatabase = proplists:get_value(db_database, Options, "test"), DBIdentifier = proplists:get_value(db_shard_id, Options, boss_pool), - Encoding = utf8, + Encoding = utf8, mysql_conn:start_link(DBHost, DBPort, DBUsername, DBPassword, DBDatabase, fun(_, _, _, _) -> ok end, Encoding, DBIdentifier). @@ -35,7 +35,7 @@ find(Pid, Id) when is_list(Id) -> [Row] -> Columns = mysql:get_result_field_info(MysqlRes), case boss_record_lib:ensure_loaded(Type) of - true -> activate_record(Row, Columns, Type); + true -> activate_record(Row, Columns, Type); false -> {error, {module_not_loaded, Type}} end end; @@ -274,7 +274,7 @@ build_insert_query(Record) -> {[DBColumn|Attrs], [pack_value(TableId)|Vals]}; ({A, V}, {Attrs, Vals}) -> DBColumn = proplists:get_value(A, AttributeColumns), - Value = case boss_sql_lib:is_foreign_key(Type, A) of + Value = case boss_sql_lib:is_foreign_key(Type, A) of true -> {_, _, _, ForeignId} = boss_sql_lib:infer_type_from_id(V), ForeignId; @@ -284,11 +284,13 @@ build_insert_query(Record) -> {[DBColumn|Attrs], [pack_value(Value)|Vals]} end, {[], []}, Record:attributes()), ["INSERT INTO ", TableName, " (", - string:join(Attributes, ", "), + string:join(escape_attr(Attributes), ", "), ") values (", string:join(Values, ", "), ")" ]. +escape_attr(Attrs) -> + [["`", Attr, "`"] || Attr <- Attrs]. build_update_query(Record) -> {Type, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Record:id()), @@ -306,7 +308,7 @@ build_update_query(Record) -> _ -> V end, - [DBColumn ++ " = " ++ pack_value(Value)|Acc] + ["`"++DBColumn ++ "` = " ++ pack_value(Value)|Acc] end, [], Record:attributes()), ["UPDATE ", TableName, " SET ", string:join(Updates, ", "), " WHERE ", IdColumn, " = ", pack_value(TableId)]. @@ -397,7 +399,6 @@ pack_match_not(Key) -> pack_boolean_query(Values, Op) -> "('" ++ string:join(lists:map(fun(Val) -> Op ++ escape_sql(Val) end, Values), " ") ++ "' IN BOOLEAN MODE)". - pack_set(Values) -> "(" ++ string:join(lists:map(fun pack_value/1, Values), ", ") ++ ")". @@ -430,7 +431,7 @@ pack_value(undefined) -> pack_value(V) when is_binary(V) -> pack_value(binary_to_list(V)); pack_value(V) when is_list(V) -> - "'" ++ escape_sql(V) ++ "'"; + mysql:encode(V); pack_value({_, _, _} = Val) -> pack_date(Val); pack_value({{_, _, _}, {_, _, _}} = Val) -> @@ -445,5 +446,6 @@ pack_value(false) -> "FALSE". fetch(Pid, Query) -> + lager:info("Query ~s", [iolist_to_binary(Query)]), mysql_conn:fetch(Pid, [Query], self()). From 78fc2334e7b9dd6b5bc2666bd127ee6f009de76b Mon Sep 17 00:00:00 2001 From: Zachary Kessin Date: Mon, 27 Jan 2014 17:03:29 +0200 Subject: [PATCH 4/4] minor refactor --- src/boss_db.erl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/boss_db.erl b/src/boss_db.erl index f5fcfd3f..3107c12d 100644 --- a/src/boss_db.erl +++ b/src/boss_db.erl @@ -265,18 +265,21 @@ counter(Key, Timeout) -> %% PageNum} and {page_size, PageSize} to control which page to fetch, %% and how many results per page. Page size defaults to 10. paginate(Model, Conditions, Opts) -> - CleanOpts = proplists:delete(offset, proplists:delete(limit, Opts)), - Page = proplists:get_value(page, Opts, 1), - PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), - OptList = proplists:delete(page_size, proplists:delete(page, CleanOpts)) ++ + CleanOpts = proplists:delete(offset, proplists:delete(limit, Opts)), + Page = proplists:get_value(page, Opts, 1), + PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), + OptList = proplists:delete(page_size, proplists:delete(page, CleanOpts)) ++ [{offset, PageSize * (Page - 1)}, {limit, PageSize}], - Total = boss_db:count(Model, Conditions), - TotalPages = (Total div PageSize) + (case Total rem PageSize of - 0 -> 0; - _ -> 1 - end), + Total = boss_db:count(Model, Conditions), + TotalPages = compute_total_pages(PageSize, Total), {Page, TotalPages, boss_db:find(Model, Conditions, OptList)}. +compute_total_pages(PageSize, Total) -> + Total div PageSize + case Total rem PageSize of + 0 -> 0; + _ -> 1 + end. + %% @spec incr( Id::string() ) -> integer() %% @doc Treat the record associated with `Id' as a counter and atomically increment its value by 1. incr(Key) ->