まえがき
先日に続き、updateとdeleteの実装を説明します。説明の都合上、deleteを先に説明します。
delete
データの削除の実装を説明する。クライアントからは「このテーブル(TableName)のこのカラム(ColName)がこの値(Val)になっているデータを削除してちょうだい」というメッセージ{delete, TableName, ColName, Val}が飛んでくる想定である。
handle_call({exec_query, {delete, TableName, ColName, Val}}, _From, State)->
%% トランザクションが許可されている場合のみ次に進める
case ask_transaction(State) of
transaction_not_found ->
{reply, transaction_not_found, State};
ok ->
QueryId = generate_query_id(),
QueryIdList = get_query_id_list(State),
%% オブジェクトIDを取得する
OidList = select_object_id_list(State, TableName, ColName, Val, QueryIdList),
lists:map(fun(Oid) -> local_delete_data(State, QueryId, TableName, Oid, select_data(State, TableName, Oid, QueryIdList)) end,
OidList),
{reply, ok, State#state{queryId = QueryIdList ++ [QueryId]}}
end;
QueryIdは今まで通りこのクエリのIDである。
QueryIdListは今までローカル領域に積み上がってきたクエリのIDリストである。コミットする前のデータの削除も可能にするために、今まで積み上げてきたデータを持ってくるという意図である。
OidListは削除するデータのオブジェクトIDである。
取得してきたオブジェクトID一つずつ、local_delete_dataでdelタグのついたデータを積み上げていく。
local_delete_data(State, QueryId, TableName, Oid, Val) ->
LKvstore = get_local_kvstore(State),
LColumnIndex = get_local_column_index(State),
ColList = simple_db_server:get_column_list(TableName),
if
ColList =:= not_found -> not_found;
true ->
lists:map(fun({ColName, ColVal}) ->
ets:insert(LColumnIndex, {QueryId, del, TableName, ColName, ColVal, Oid}) end,
lists:zip(ColList, Val)),
ets:insert(LKvstore, {QueryId, del, TableName, Oid, Val})
end.
local_delete_dataはローカルカラムインデックスと、ローカルキーバリューストアにdelタグのついたデータを挿入するだけである。
例えば、フルーツテーブル(名前と値段の列を持つ)から名前が「みかん」となっている行を削除するとする。
ローカルカラムインデックスには下記のようなデータを登録する。(削除するデータのオブジェクトIDはAとする。)
- {0002, del, Fruit, name, みかん, A}
- {0002, del, Fruit, price, 100円, A}
ローカルキーバリューストアには下記のようなデータを登録する。
- {0002, del, Fruit, A, [みかん, 100円]}
トランザクションをコミットしたときに、ローカルキーバリューストアのdelタグのついたデータを見て、共有カラムインデックスから物理削除し、ローカルキーバリューストアのdelタグのついたデータを見て、共有キーバリューストアから物理削除する。
update
データの更新の実装を説明する。クライアントからは「このテーブル(TableName)のこのカラム(ColName)がこの値(Val)になっているデータをこんな風に(SetQuery)に更新してちょうだい」というメッセージ{update, TableName, SetQuery, ColName, Val}が飛んでくる想定である。
handle_call({exec_query, {update, TableName, SetQuery, ColName, Val}}, _From, State)->
%% トランザクションが許可されている場合のみ次に進める
case ask_transaction(State) of
transaction_not_found ->
{reply, transaction_not_found, State};
ok ->
QueryId = generate_query_id(),
LKvstore = get_local_kvstore(State),
LColumnIndex = get_local_column_index(State),
QueryIdList = get_query_id_list(State),
ColumnList = simple_db_server:get_column_list(TableName),
SetQueryConverted = simple_db_server:convert_set_query(SetQuery, ColumnList),
OidList = select_object_id_list(State, TableName, ColName, Val, QueryIdList),
F = fun(Oid) ->
%% OldVal -> [banana, 100]
%% NewVal -> [apple, 100]
OldVal = select_data(State, TableName, Oid, QueryIdList),
OldValWithCol = lists:zip(simple_db_server:get_column_list(TableName),OldVal),
NewVal = simple_db_server:build_new_val(OldVal, SetQueryConverted),
%% 更新前値をローカルデータから削除
ets:insert(LKvstore, {QueryId, del, TableName, Oid, OldVal}),
%% 更新後値をローカルデータに挿入
ets:insert(LKvstore, {QueryId, ins, TableName, Oid, NewVal}),
%% 更新対象のカラムごとにカラムインデックスを更新する
%% ex. {name, apple}
FF = fun({ColumnN, NewColVal}) ->
{_Key, OldColVal} = lists:keyfind(ColumnN, 1, OldValWithCol),
ets:insert(LColumnIndex, {QueryId, del, TableName, ColumnN, OldColVal, Oid}),
ets:insert(LColumnIndex, {QueryId, ins, TableName, ColumnN, NewColVal, Oid})
end,
lists:map(FF, SetQuery)
end,
lists:map(F, OidList),
{reply, ok, State#state{queryId = QueryIdList ++ [QueryId]}}
end;
OldValは更新前の値、NewValは更新後の値を代入して、
OldValを削除し、NewValを挿入するというのが大雑把な実装方針です。
OldValは今まで通り、共有データを取ってきて、ローカルデータをマージする。
NewValは、OldValをSetQueryで更新して作成する。
あとがき
4つのDMLの実装について説明しました。
次回は、tx_mngの挙動を説明する予定です。