LoginSignup
0
0

More than 3 years have passed since last update.

【13日目】トランザクションサポートデータベース実装

Posted at

まえがき

先日に続き、updateとdeleteの実装を説明します。説明の都合上、deleteを先に説明します。

delete

データの削除の実装を説明する。クライアントからは「このテーブル(TableName)のこのカラム(ColName)がこの値(Val)になっているデータを削除してちょうだい」というメッセージ{delete, TableName, ColName, Val}が飛んでくる想定である。

query_exec.erl
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
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タグのついたデータを挿入するだけである。
例えば、フルーツテーブル(名前と値段の列を持つ)から名前が「みかん」となっている行を削除するとする。
image.png

ローカルカラムインデックスには下記のようなデータを登録する。(削除するデータのオブジェクト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}が飛んでくる想定である。

query_exec.erl
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の挙動を説明する予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0