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"}}}, diff --git a/src/boss_db.erl b/src/boss_db.erl index e2a31450..3107c12d 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,10 +66,12 @@ -define(DEFAULT_TIMEOUT, (30 * 1000)). -define(POOLNAME, boss_db_pool). +-define(DEFAULT_PAGE_SIZE, 10). 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 @@ -257,6 +260,26 @@ 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 = 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) -> 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()).