Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Always use record-NN in SQL adapters

Also, override column names with -column() attribute.
  • Loading branch information...
commit 851ef261774723dd44d4a9632ad3d1ddba5285c2 1 parent 2ed23f4
@evanmiller evanmiller authored
View
4 src/boss_db_mock_controller.erl
@@ -48,12 +48,12 @@ handle_call({save_record, Record}, _From, [{Dict, IdCounter}|OldState]) ->
Type = element(1, Record),
TypeString = atom_to_list(Type),
{Id, IdCounter1} = case Record:id() of
- id -> case boss_record_lib:keytype(Record) of
+ id -> case boss_sql_lib:keytype(Record) of
uuid -> {lists:concat([Type, "-", uuid:to_string(uuid:uuid4())]), IdCounter};
_ -> {lists:concat([Type, "-", IdCounter]), IdCounter + 1}
end;
ExistingId ->
- case boss_record_lib:keytype(Record) of
+ case boss_sql_lib:keytype(Record) of
uuid -> {ExistingId, IdCounter};
_ ->
[TypeString, IdNum] = string:tokens(ExistingId, "-"),
View
18 src/boss_record_compiler.erl
@@ -67,6 +67,7 @@ trick_out_forms(LeadingForms, Forms, ModuleName, Parameters, TokenInfo) ->
GeneratedForms =
attribute_names_forms(ModuleName, Parameters) ++
attribute_types_forms(ModuleName, TokenInfo) ++
+ attribute_columns_forms(ModuleName, Parameters, Attributes) ++
validate_types_forms(ModuleName) ++
validate_forms(ModuleName) ++
save_forms(ModuleName) ++
@@ -131,6 +132,23 @@ export_forms([], Acc) ->
export_forms([{Name, Arity}|Rest], Acc) ->
export_forms(Rest, [erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list([erl_syntax:arity_qualifier(erl_syntax:atom(Name), erl_syntax:integer(Arity))])])|Acc]).
+attribute_columns_forms(ModuleName, Parameters, Attributes) ->
+ DefinedColumns = proplists:get_value(Attributes, columns, []),
+ Function = erl_syntax:function(
+ erl_syntax:atom(attribute_columns),
+ [erl_syntax:clause([], none, [erl_syntax:list(lists:map( fun(P) ->
+ LC = parameter_to_colname(P),
+ AC = list_to_atom(LC),
+ Column = proplists:get_value(AC, DefinedColumns, LC),
+ erl_syntax:tuple([erl_syntax:atom(AC), erl_syntax:string(Column)])
+ end, Parameters))])]),
+
+ [erl_syntax:add_precomments([erl_syntax:comment(
+ ["% @spec attribute_columns() -> [{atom(), string()}]",
+ lists:concat(["% @doc A proplist of the database field names of each `", ModuleName, "' parameter."])])],
+ Function)].
+
+
attribute_types_forms(ModuleName, TypeInfo) ->
[erl_syntax:add_precomments([erl_syntax:comment(
["% @spec attribute_types() -> [{atom(), atom()}]",
View
15 src/boss_record_lib.erl
@@ -6,12 +6,12 @@
dummy_record/1,
attribute_names/1,
attribute_types/1,
- keytype/1,
+ attribute_columns/1,
convert_value_to_type/2,
- ensure_loaded/1]).
+ ensure_loaded/1
+ ]).
-define(MILLION, 1000000).
--define(DEFAULT_KEYTYPE, serial).
run_before_hooks(Record, true) ->
run_hooks(Record, element(1, Record), before_create);
@@ -54,12 +54,9 @@ attribute_types(Module) ->
DummyRecord = dummy_record(Module),
DummyRecord:attribute_types().
-keytype(Module) when is_atom(Module) ->
- proplists:get_value(id, attribute_types(Module), ?DEFAULT_KEYTYPE);
-keytype(Module) when is_list(Module) ->
- proplists:get_value(id, attribute_types(list_to_atom(Module)), ?DEFAULT_KEYTYPE);
-keytype(Record) when is_tuple(Record) andalso is_atom(element(1, Record)) ->
- proplists:get_value(id, Record:attribute_types(), ?DEFAULT_KEYTYPE).
+attribute_columns(Module) ->
+ DummyRecord = dummy_record(Module),
+ DummyRecord:attribute_columns().
ensure_loaded(Module) ->
case code:ensure_loaded(Module) of
View
64 src/boss_sql_lib.erl
@@ -0,0 +1,64 @@
+-module(boss_sql_lib).
+-export([keytype/1,
+ infer_type_from_id/1,
+ convert_id_condition_to_use_table_ids/1,
+ is_foreign_key/1,
+ type_to_table_name/1
+ ]).
+
+-define(DEFAULT_KEYTYPE, serial).
+
+keytype(Module) when is_atom(Module) ->
+ proplists:get_value(id, boss_record_lib:attribute_types(Module), ?DEFAULT_KEYTYPE);
+keytype(Module) when is_list(Module) ->
+ proplists:get_value(id, boss_record_lib:attribute_types(list_to_atom(Module)), ?DEFAULT_KEYTYPE);
+keytype(Record) when is_tuple(Record) andalso is_atom(element(1, Record)) ->
+ proplists:get_value(id, Record:attribute_types(), ?DEFAULT_KEYTYPE).
+
+infer_type_from_id(Id) when is_list(Id) ->
+ [Type, TableId] = re:split(Id, "-", [{return, list}, {parts, 2}]),
+ TypeAtom = list_to_atom(Type),
+ IdColumn = proplists:get_value(id, boss_record_lib:attribute_columns(TypeAtom)),
+ IdValue = case keytype(Type) of
+ uuid -> TableId;
+ serial -> list_to_integer(TableId)
+ end,
+ {TypeAtom, type_to_table_name(Type), IdColumn, IdValue}.
+
+type_to_table_name(Type) when is_atom(Type) ->
+ type_to_table_name(atom_to_list(Type));
+type_to_table_name(Type) when is_list(Type) ->
+ inflector:pluralize(Type).
+
+convert_id_condition_to_use_table_ids({Key, Op, Value}) when Op =:= 'equals'; Op =:= 'not_equals'; Op =:= 'gt';
+ Op =:= 'lt'; Op =:= 'ge'; Op =:= 'le' ->
+ {_Type, _TableName, _IdColumn, TableId} = infer_type_from_id(Value),
+ {Key, Op, TableId};
+convert_id_condition_to_use_table_ids({Key, Op, {Min, Max}}) when Op =:= 'in'; Op =:= 'not_in' ->
+ {_Type, _TableName, _IdColumn, TableId1} = infer_type_from_id(Min),
+ {_Type, _TableName, _IdColumn, TableId2} = infer_type_from_id(Max),
+ {Key, Op, {TableId1, TableId2}};
+convert_id_condition_to_use_table_ids({Key, Op, ValueList}) when is_list(ValueList) andalso (Op =:= 'in' orelse Op =:= 'not_in') ->
+ Value2 = lists:map(fun(V) ->
+ {_Type, _TableName, _IdColumn, TableId} = infer_type_from_id(V),
+ TableId
+ end, ValueList),
+ {Key, Op, Value2}.
+
+is_foreign_key(Key) when is_atom(Key) ->
+ KeyTokens = string:tokens(atom_to_list(Key), "_"),
+ LastToken = hd(lists:reverse(KeyTokens)),
+ case (length(KeyTokens) > 1 andalso LastToken == "id") of
+ true ->
+ Module = join(lists:reverse(tl(lists:reverse(KeyTokens))), "_"),
+ case code:is_loaded(list_to_atom(Module)) of
+ {file, _Loaded} -> true;
+ false -> false
+ end;
+ false -> false
+ end;
+is_foreign_key(_Key) -> false.
+
+join([], _) -> [];
+join([List|Lists], Separator) ->
+ lists:flatten([List | [[Separator,Next] || Next <- Lists]]).
View
110 src/db_adapters/boss_db_adapter_mysql.erl
@@ -25,8 +25,8 @@ terminate(Pid) ->
exit(Pid, normal).
find(Pid, Id) when is_list(Id) ->
- {Type, TableName, TableId} = infer_type_from_id(Id),
- Res = fetch(Pid, ["SELECT * FROM ", TableName, " WHERE id = ", pack_value(TableId)]),
+ {Type, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Id),
+ Res = fetch(Pid, ["SELECT * FROM ", TableName, " WHERE ", IdColumn, " = ", pack_value(TableId)]),
case Res of
{data, MysqlRes} ->
case mysql:get_result_rows(MysqlRes) of
@@ -70,8 +70,8 @@ find(Pid, Type, Conditions, Max, Skip, Sort, SortOrder) when is_atom(Type), is_l
end.
count(Pid, Type, Conditions) ->
- ConditionClause = build_conditions(Conditions),
- TableName = type_to_table_name(Type),
+ ConditionClause = build_conditions(Type, Conditions),
+ TableName = boss_sql_lib:type_to_table_name(Type),
Res = fetch(Pid, ["SELECT COUNT(*) AS count FROM ", TableName, " WHERE ", ConditionClause]),
case Res of
{data, MysqlRes} ->
@@ -106,8 +106,8 @@ incr(Pid, Id, Count) ->
end.
delete(Pid, Id) when is_list(Id) ->
- {_, TableName, TableId} = infer_type_from_id(Id),
- Res = fetch(Pid, ["DELETE FROM ", TableName, " WHERE id = ",
+ {_, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Id),
+ Res = fetch(Pid, ["DELETE FROM ", TableName, " WHERE ", IdColumn, " = ",
pack_value(TableId)]),
case Res of
{updated, _} ->
@@ -207,35 +207,28 @@ do_rollback(Pid,_)->
% internal
-infer_type_from_id(Id) when is_list(Id) ->
- [Type, TableId] = string:tokens(Id, "-"),
- {list_to_atom(Type), type_to_table_name(Type), list_to_integer(TableId)}.
-
-type_to_table_name(Type) when is_atom(Type) ->
- type_to_table_name(atom_to_list(Type));
-type_to_table_name(Type) when is_list(Type) ->
- inflector:pluralize(Type).
-
integer_to_id(Val, KeyString) ->
ModelName = string:substr(KeyString, 1, string:len(KeyString) - string:len("_id")),
ModelName ++ "-" ++ integer_to_list(Val).
activate_record(Record, Metadata, Type) ->
AttributeTypes = boss_record_lib:attribute_types(Type),
+ AttributeColumns = boss_record_lib:attribute_columns(Type),
apply(Type, new, lists:map(fun
(id) ->
- Index = keyindex(<<"id">>, 2, Metadata),
+ DBColumn = proplists:get_value('id', AttributeColumns),
+ Index = keyindex(list_to_binary(DBColumn), 2, Metadata),
atom_to_list(Type) ++ "-" ++ integer_to_list(lists:nth(Index, Record));
(Key) ->
- KeyString = atom_to_list(Key),
- Index = keyindex(list_to_binary(KeyString), 2, Metadata),
+ DBColumn = proplists:get_value(Key, AttributeColumns),
+ Index = keyindex(list_to_binary(DBColumn), 2, Metadata),
AttrType = proplists:get_value(Key, AttributeTypes),
case lists:nth(Index, Record) of
undefined -> undefined;
{datetime, DateTime} -> boss_record_lib:convert_value_to_type(DateTime, AttrType);
Val ->
- case lists:suffix("_id", KeyString) of
- true -> integer_to_id(Val, KeyString);
+ case boss_sql_lib:is_foreign_key(Key) of
+ true -> integer_to_id(Val, DBColumn);
false -> boss_record_lib:convert_value_to_type(Val, AttrType)
end
end
@@ -259,21 +252,24 @@ sort_order_sql(ascending) ->
build_insert_query(Record) ->
Type = element(1, Record),
- TableName = type_to_table_name(Type),
+ TableName = boss_sql_lib:type_to_table_name(Type),
+ AttributeColumns = Record:attribute_columns(),
{Attributes, Values} = lists:foldl(fun
- ({id, V}, {Attrs, Vals}) when is_integer(V) -> {[atom_to_list(id)|Attrs], [pack_value(V)|Vals]};
- ({id, _}, Acc) -> Acc;
({_, undefined}, Acc) -> Acc;
+ ({'id', V}, {Attrs, Vals}) ->
+ DBColumn = proplists:get_value('id', AttributeColumns),
+ {_, _, _, TableId} = boss_sql_lib:infer_type_from_id(V),
+ {[DBColumn|Attrs], [pack_value(TableId)|Vals]};
({A, V}, {Attrs, Vals}) ->
- AString = atom_to_list(A),
- Value = case lists:suffix("_id", AString) of
+ DBColumn = proplists:get_value(A, AttributeColumns),
+ Value = case boss_sql_lib:is_foreign_key(A) of
true ->
- {_, _, ForeignId} = infer_type_from_id(V),
+ {_, _, _, ForeignId} = boss_sql_lib:infer_type_from_id(V),
ForeignId;
false ->
V
end,
- {[AString|Attrs], [pack_value(Value)|Vals]}
+ {[DBColumn|Attrs], [pack_value(Value)|Vals]}
end, {[], []}, Record:attributes()),
["INSERT INTO ", TableName, " (",
string:join(Attributes, ", "),
@@ -283,64 +279,54 @@ build_insert_query(Record) ->
].
build_update_query(Record) ->
- {_, TableName, Id} = infer_type_from_id(Record:id()),
+ {_, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Record:id()),
+ AttributeColumns = Record:attribute_columns(),
Updates = lists:foldl(fun
({id, _}, Acc) -> Acc;
({A, V}, Acc) ->
- AString = atom_to_list(A),
- Value = case {lists:suffix("_id", AString), V =:= undefined} of
- {true, false} ->
- {_, _, ForeignId} = infer_type_from_id(V),
+ DBColumn = proplists:get_value(A, AttributeColumns),
+ Value = case {boss_sql_lib:is_foreign_key(A), V =/= undefined} of
+ {true, true} ->
+ {_, _, _, ForeignId} = boss_sql_lib:infer_type_from_id(V),
ForeignId;
_ ->
V
end,
- [AString ++ " = " ++ pack_value(Value)|Acc]
+ [DBColumn ++ " = " ++ pack_value(Value)|Acc]
end, [], Record:attributes()),
["UPDATE ", TableName, " SET ", string:join(Updates, ", "),
- " WHERE id = ", pack_value(Id)].
+ " WHERE ", IdColumn, " = ", pack_value(TableId)].
build_select_query(Type, Conditions, Max, Skip, Sort, SortOrder) ->
- TableName = type_to_table_name(Type),
+ TableName = boss_sql_lib:type_to_table_name(Type),
["SELECT * FROM ", TableName,
- " WHERE ", build_conditions(Conditions),
+ " WHERE ", build_conditions(Type, Conditions),
" ORDER BY ", atom_to_list(Sort), " ", sort_order_sql(SortOrder),
case Max of all -> ""; _ -> [" LIMIT ", integer_to_list(Max),
" OFFSET ", integer_to_list(Skip)] end
].
-join([], _) -> [];
-join([List|Lists], Separator) ->
- lists:flatten([List | [[Separator,Next] || Next <- Lists]]).
-
-is_foreign_key(Key) when is_atom(Key) ->
- KeyTokens = string:tokens(atom_to_list(Key), "_"),
- LastToken = hd(lists:reverse(KeyTokens)),
- case (length(KeyTokens) > 1 andalso LastToken == "id") of
- true ->
- Module = join(lists:reverse(tl(lists:reverse(KeyTokens))), "_"),
- case code:is_loaded(list_to_atom(Module)) of
- {file, _Loaded} -> true;
- false -> false
- end;
- false -> false
- end;
-is_foreign_key(_Key) -> false.
-
-build_conditions(Conditions) ->
- build_conditions1(Conditions, [" TRUE"]).
+build_conditions(Type, Conditions) ->
+ AttributeColumns = boss_record_lib:attribute_columns(Type),
+ Conditions2 = lists:map(fun
+ ({'id' = Key, Op, Value}) ->
+ Key2 = proplists:get_value(Key, AttributeColumns, Key),
+ boss_sql_lib:convert_id_condition_to_use_table_ids({Key2, Op, Value});
+ ({Key, Op, Value}) ->
+ Key2 = proplists:get_value(Key, AttributeColumns, Key),
+ case boss_sql_lib:is_foreign_key(Key) of
+ true -> boss_sql_lib:convert_id_condition_to_use_table_ids({Key2, Op, Value});
+ false -> {Key2, Op, Value}
+ end
+ end, Conditions),
+ build_conditions1(Conditions2, [" TRUE"]).
build_conditions1([], Acc) ->
Acc;
build_conditions1([{Key, 'equals', Value}|Rest], Acc) when Value == undefined ->
build_conditions1(Rest, add_cond(Acc, Key, "is", pack_value(Value)));
build_conditions1([{Key, 'equals', Value}|Rest], Acc) ->
- case is_foreign_key(Key) of
- true ->
- {_Type, _TableName, TableId} = infer_type_from_id(Value),
- build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(TableId)));
- false -> build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(Value)))
- end;
+ build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(Value)));
build_conditions1([{Key, 'not_equals', Value}|Rest], Acc) when Value == undefined ->
build_conditions1(Rest, add_cond(Acc, Key, "is not", pack_value(Value)));
build_conditions1([{Key, 'not_equals', Value}|Rest], Acc) ->
View
128 src/db_adapters/boss_db_adapter_pgsql.erl
@@ -23,8 +23,8 @@ terminate(Conn) ->
pgsql:close(Conn).
find(Conn, Id) when is_list(Id) ->
- {Type, TableName, TableId} = infer_type_from_id(Id),
- Res = pgsql:equery(Conn, ["SELECT * FROM ", TableName, " WHERE id = $1"], [TableId]),
+ {Type, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Id),
+ Res = pgsql:equery(Conn, ["SELECT * FROM ", TableName, " WHERE ", IdColumn, " = $1"], [TableId]),
case Res of
{ok, _Columns, []} ->
undefined;
@@ -90,8 +90,8 @@ incr(Conn, Id, Count) ->
end.
delete(Conn, Id) when is_list(Id) ->
- {_, TableName, TableId} = infer_type_from_id(Id),
- Res = pgsql:equery(Conn, ["DELETE FROM ", TableName, " WHERE id = $1"], [TableId]),
+ {_, TableName, IdColumn, TableId} = boss_sql_lib:infer_type_from_id(Id),
+ Res = pgsql:equery(Conn, ["DELETE FROM ", TableName, " WHERE ", IdColumn, " = $1"], [TableId]),
case Res of
{ok, _Count} ->
pgsql:equery(Conn, "DELETE FROM counters WHERE name = $1", [Id]),
@@ -149,16 +149,8 @@ id_value_to_string(Id) when is_integer(Id) -> integer_to_list(Id);
id_value_to_string(Id) when is_binary(Id) -> binary_to_list(Id);
id_value_to_string(Id) -> Id.
-infer_type_from_id(Id) when is_list(Id) ->
- [Type, TableId] = re:split(Id, "-", [{return, list}, {parts, 2}]),
- IdValue = case boss_record_lib:keytype(Type) of
- uuid -> TableId;
- serial -> list_to_integer(TableId)
- end,
- {list_to_atom(Type), type_to_table_name(Type), IdValue}.
-
maybe_populate_id_value(Record) ->
- case boss_record_lib:keytype(Record) of
+ case boss_sql_lib:keytype(Record) of
uuid -> Record:set(id, uuid:to_string(uuid:uuid4()));
_ -> Record
end.
@@ -174,20 +166,22 @@ integer_to_id(Val, KeyString) ->
activate_record(Record, Metadata, Type) ->
AttributeTypes = boss_record_lib:attribute_types(Type),
+ AttributeColumns = boss_record_lib:attribute_columns(Type),
apply(Type, new, lists:map(fun
(id) ->
- Index = keyindex(<<"id">>, 2, Metadata),
+ DBColumn = proplists:get_value('id', AttributeColumns),
+ Index = keyindex(list_to_binary(DBColumn), 2, Metadata),
atom_to_list(Type) ++ "-" ++ id_value_to_string(element(Index, Record));
(Key) ->
- KeyString = atom_to_list(Key),
- Index = keyindex(list_to_binary(KeyString), 2, Metadata),
+ DBColumn = proplists:get_value(Key, AttributeColumns),
+ Index = keyindex(list_to_binary(DBColumn), 2, Metadata),
AttrType = proplists:get_value(Key, AttributeTypes),
case element(Index, Record) of
undefined -> undefined;
null -> undefined;
Val ->
- case lists:suffix("_id", KeyString) of
- true -> integer_to_id(Val, KeyString);
+ case boss_sql_lib:is_foreign_key(Key) of
+ true -> integer_to_id(Val, DBColumn);
false -> boss_record_lib:convert_value_to_type(Val, AttrType)
end
end
@@ -212,21 +206,23 @@ sort_order_sql(ascending) ->
build_insert_query(Record) ->
Type = element(1, Record),
TableName = type_to_table_name(Type),
+ AttributeColumns = Record:attribute_columns(),
{Attributes, Values} = lists:foldl(fun
- ({id, V}, {Attrs, Vals}) when is_integer(V) -> {[atom_to_list(id)|Attrs], [pack_value(V)|Vals]};
- ({id, V}, {Attrs, Vals}) when is_list(V) -> {[atom_to_list(id)|Attrs], [pack_value(V)|Vals]};
- ({id, _}, Acc) -> Acc;
({_, undefined}, Acc) -> Acc;
+ ({'id', V}, {Attrs, Vals}) ->
+ DBColumn = proplists:get_value('id', AttributeColumns),
+ {_, _, _, TableId} = boss_sql_lib:infer_type_from_id(V),
+ {[DBColumn|Attrs], [pack_value(TableId)|Vals]};
({A, V}, {Attrs, Vals}) ->
- AString = atom_to_list(A),
- Value = case lists:suffix("_id", AString) of
+ DBColumn = proplists:get_value(A, AttributeColumns),
+ Value = case boss_sql_lib:is_foreign_key(A) of
true ->
- {_, _, ForeignId} = infer_type_from_id(V),
+ {_, _, _, ForeignId} = boss_sql_lib:infer_type_from_id(V),
ForeignId;
false ->
V
end,
- {[AString|Attrs], [pack_value(Value)|Vals]}
+ {[DBColumn|Attrs], [pack_value(Value)|Vals]}
end, {[], []}, Record:attributes()),
["INSERT INTO ", TableName, " (",
string:join(Attributes, ", "),
@@ -237,22 +233,23 @@ build_insert_query(Record) ->
].
build_update_query(Record) ->
- {_, TableName, Id} = infer_type_from_id(Record:id()),
+ {_, TableName, IdColumn, Id} = boss_sql_lib:infer_type_from_id(Record:id()),
+ AttributeColumns = Record:attribute_columns(),
Updates = lists:foldl(fun
({id, _}, Acc) -> Acc;
({A, V}, Acc) ->
- AString = atom_to_list(A),
- Value = case lists:suffix("_id", AString) of
- true ->
- {_, _, ForeignId} = infer_type_from_id(V),
+ DBColumn = proplists:get_value(A, AttributeColumns),
+ Value = case {boss_sql_lib:is_foreign_key(A), V =/= undefined} of
+ {true, true} ->
+ {_, _, _, ForeignId} = boss_sql_lib:infer_type_from_id(V),
ForeignId;
- false ->
+ _ ->
V
end,
- [AString ++ " = " ++ pack_value(Value)|Acc]
+ [DBColumn ++ " = " ++ pack_value(Value)|Acc]
end, [], Record:attributes()),
["UPDATE ", TableName, " SET ", string:join(Updates, ", "),
- " WHERE id = ", pack_value(Id)].
+ " WHERE ", IdColumn, " = ", pack_value(Id)].
build_select_query(Type, Conditions, Max, Skip, Sort, SortOrder) ->
TableName = type_to_table_name(Type),
@@ -263,52 +260,20 @@ build_select_query(Type, Conditions, Max, Skip, Sort, SortOrder) ->
" OFFSET ", integer_to_list(Skip)] end
].
-join([], _) -> [];
-join([List|Lists], Separator) ->
- lists:flatten([List | [[Separator,Next] || Next <- Lists]]).
-
-is_foreign_key(Key) when is_atom(Key) ->
- KeyTokens = string:tokens(atom_to_list(Key), "_"),
- LastToken = hd(lists:reverse(KeyTokens)),
- case (length(KeyTokens) > 1 andalso LastToken == "id") of
- true ->
- Module = join(lists:reverse(tl(lists:reverse(KeyTokens))), "_"),
- case code:is_loaded(list_to_atom(Module)) of
- {file, _Loaded} -> true;
- false -> false
- end;
- false -> false
- end;
-is_foreign_key(_Key) -> false.
-
-%% take the "type-" part out of id_values of "type-nnn"
-%% it allows to assert([boss_db:find("type-nnn")] == boss_db:find(type, [{id, equals, "type-nnn"}])
-%% while it forbids boss_db:find(type, [{id, equals, "othertype-nnn"}] where othertype is from the untrusted input
-%% without this patch, the second part returns:
-%% {error,{error,error,<<"22P02">>,
-%% <<"invalid input syntax for integer: \"type-1\"">>,
-%% [{position,<<"35">>}]}}
-de_type_id(Type, Conditions) ->
- lists:map(fun({id, Operator, Operand}) when is_list(Operand) ->
- {id, Operator, sanitize_value(Type, Operand)};
- ({id, Operator, Operand}) when is_binary(Operand) -> %% allow for binary operands, dunno if that occurs.
- {id, Operator, sanitize_value(Type, binary_to_list(Operand))};
- (Other) -> Other %% anything not an 'id' is passed on as-is to the DB.
- end, Conditions).
-
-%% Make sure the value that goes in the WHERE-clause is the number-part of the Type-Number record identifier.
-sanitize_value(Type, Value) ->
- TypeL = atom_to_list(Type), %% we must match the expected Type
- case string:tokens(Value, "-") of
- [Value] -> Value; %% just a string, no record-123 composite, pass it on
- [TypeL, IdValue] -> IdValue; %% take out the record- part and give the supposed number.
- %% don't let missing input validation go unnoticed, scream loudly:
- _Err -> throw({error, "Id value must be of the same type as the requested record.\nExpected type: \"" ++ TypeL ++ "\". Got value: \"" ++ Value ++ "\"."})
- end.
-
build_conditions(Type, Conditions) ->
- Conds = de_type_id(Type, Conditions),
- build_conditions1(Conds, [" TRUE"]).
+ AttributeColumns = boss_record_lib:attribute_columns(Type),
+ Conditions2 = lists:map(fun
+ ({'id' = Key, Op, Value}) ->
+ Key2 = proplists:get_value(Key, AttributeColumns, Key),
+ boss_sql_lib:convert_id_condition_to_use_table_ids({Key2, Op, Value});
+ ({Key, Op, Value}) ->
+ Key2 = proplists:get_value(Key, AttributeColumns, Key),
+ case boss_sql_lib:is_foreign_key(Key) of
+ true -> boss_sql_lib:convert_id_condition_to_use_table_ids({Key2, Op, Value});
+ false -> {Key2, Op, Value}
+ end
+ end, Conditions),
+ build_conditions1(Conditions2, [" TRUE"]).
build_conditions1([], Acc) ->
Acc;
@@ -316,12 +281,7 @@ build_conditions1([], Acc) ->
build_conditions1([{Key, 'equals', Value}|Rest], Acc) when Value == undefined ->
build_conditions1(Rest, add_cond(Acc, Key, "is", pack_value(Value)));
build_conditions1([{Key, 'equals', Value}|Rest], Acc) ->
- case is_foreign_key(Key) of
- true ->
- {_Type, _TableName, TableId} = infer_type_from_id(Value),
- build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(TableId)));
- false -> build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(Value)))
- end;
+ build_conditions1(Rest, add_cond(Acc, Key, "=", pack_value(Value)));
build_conditions1([{Key, 'not_equals', Value}|Rest], Acc) when Value == undefined ->
build_conditions1(Rest, add_cond(Acc, Key, "is not", pack_value(Value)));
build_conditions1([{Key, 'not_equals', Value}|Rest], Acc) ->
Please sign in to comment.
Something went wrong with that request. Please try again.