File 0955-Fix-for-delete_object-at-read-on-a-set-table.patch of Package erlang

From b1dd0efb565f3eac1e3dbc4b18c2001ad53ea2ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Magnus=20Fr=C3=B6berg?= <magnus@klarna.com>
Date: Thu, 11 Jun 2020 16:14:26 +0200
Subject: [PATCH] Fix for delete_object at read on a set table On a set table,
 if the delete_object doesnt delete (due to no match) at commit return
 whatever is (or is not) stored in source table as a result of a read in the
 same transaction as the delete_object. And, if a delete already has been done
 on Oid in the same transaction do not record a delete_object operation on the
 same Oid. Added delete_object tests on set table and made the existing tests
 checking more.

---
 lib/mnesia/src/mnesia.erl                    |  22 ++--
 lib/mnesia/test/mnesia_trans_access_test.erl | 102 +++++++++++++++++--
 2 files changed, 110 insertions(+), 14 deletions(-)

diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 3f1b173e37..2efe739442 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -788,7 +788,8 @@ do_delete_object(Tid, Ts, Tab, Val, LockKind) ->
 		      ?ets_match_delete(Store, {Oid, Val, '_'}),
 		      ?ets_insert(Store, {Oid, Val, delete_object});
 		  _ ->
-		      case ?ets_match_object(Store, {Oid, '_', write}) of
+		      case ?ets_match_object(Store, {Oid, '_', write}) ++
+                          ?ets_match_object(Store, {Oid, '_', delete}) of
 			      [] ->
 			          ?ets_match_delete(Store, {Oid, Val, '_'}),
 			          ?ets_insert(Store, {Oid, Val, delete_object});
@@ -1221,19 +1222,28 @@ add_written(Written, Tab, ObjsFun, LockKind) ->
 	    add_written_to_bag(Written, ObjsFun(), []);
         _ when LockKind == read;
                LockKind == write ->
-	    add_written_to_set(Written);
+	    add_written_to_set(Written, ObjsFun);
 	_   ->
-            _ = ObjsFun(),  % Fall back to request new lock and read from source
-	    add_written_to_set(Written)
+            %% Fall back to request new lock and read from source
+	    add_written_to_set(Written, ObjsFun())
     end.
 
-add_written_to_set(Ws) ->
+add_written_to_set(Ws, ObjsOrFun) ->
     case lists:last(Ws) of
 	{_, _, delete} -> [];
 	{_, Val, write} -> [Val];
-	{_, _, delete_object} -> []
+	{Oid, _, delete_object} ->
+            %% May be several 'delete_object' in Ws; need to check if any
+            %% deleted Val exists in source table; if not return whatever
+            %% is/is not in the source table (ie as the Val is only deleted
+            %% if matched at commit this needs to be reflected here)
+            [Val || Val <- get_objs(ObjsOrFun),
+                    not lists:member({Oid, Val, delete_object}, Ws)]
     end.
 
+get_objs(ObjsFun) when is_function(ObjsFun) -> ObjsFun();
+get_objs(Objs) when is_list(Objs)           -> Objs.
+
 add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
     add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
 add_written_to_bag([], Objs, Ack) ->
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index 723a85fd2c..f488134391 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -63,7 +64,7 @@ end_per_testcase(Func, Conf) ->
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 all() ->
-    [write, read, wread, delete, delete_object,
+    [write, read, wread, delete, delete_object_bag, delete_object_set,
      match_object, select, select14, all_keys, transaction,
      {group, nested_activities}, {group, index_tabs},
      {group, index_lifecycle}].
@@ -221,14 +222,26 @@ delete(Config) when is_list(Config) ->
 
 %% Delete matching record
 
-delete_object(suite) -> [];
-delete_object(Config) when is_list(Config) ->
+delete_object_bag(suite) -> [];
+delete_object_bag(Config) when is_list(Config) ->
     [Node1] = Nodes = ?acquire_nodes(1, Config),
+    ?match(ok, delete_object(Node1, bag)),
+    ?verify_mnesia(Nodes, []).
+
+delete_object_set(suite) -> [];
+delete_object_set(Config) when is_list(Config) ->
+    [Node1] = Nodes = ?acquire_nodes(1, Config),
+    ?match(ok, delete_object(Node1, set)),
+    ?verify_mnesia(Nodes, []).
+
+delete_object(Node1, Type) ->
     Tab = delete_object,
-    Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+    Schema = [{name, Tab}, {type, Type},
+              {attributes, [k, v]}, {ram_copies, [Node1]}],
     ?match({atomic, ok},  mnesia:create_table(Schema)),
 
     OneRec = {Tab, 1, 2},
+    OtherRec = {Tab, 1, 3},
     ?match({aborted, {bad_type, _}},
 	   mnesia:transaction(fun() -> mnesia:delete_object([]) end)),
     ?match({aborted, {bad_type, _}},
@@ -237,16 +250,88 @@ delete_object(Config) when is_list(Config) ->
 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab, 1}) end)),
     ?match({atomic, ok},
 	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+
+    %% Delete already existing object
     ?match({atomic, ok},
 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
     ?match({atomic, ok},
-	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+	   mnesia:transaction(fun() ->
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok = mnesia:delete_object(OneRec),
+                                      [] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([], mnesia:dirty_read(Tab, 1)),
+
+    %% Delete already existing object (written twice)
     ?match({atomic, ok},
 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
     ?match({atomic, ok},
 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
     ?match({atomic, ok},
-	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+	   mnesia:transaction(fun() ->
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok = mnesia:delete_object(OneRec),
+                                      [] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([], mnesia:dirty_read(Tab, 1)),
+
+    %% Delete object written in same transaction
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() ->
+                                      [] = mnesia:read(Tab, 1),
+                                      ok = mnesia:write(OneRec),
+                                      ok = mnesia:delete_object(OneRec),
+                                      [] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([], mnesia:dirty_read(Tab, 1)),
+
+    %% Delete other object than written in same transaction
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() ->
+                                      [] = mnesia:read(Tab, 1),
+                                      ok = mnesia:write(OneRec),
+                                      ok = mnesia:delete_object(OtherRec),
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([OneRec], mnesia:dirty_read(Tab, 1)),
+
+    %% Delete other object than already existing
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() ->
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok = mnesia:delete_object(OtherRec),
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([OneRec], mnesia:dirty_read(Tab, 1)),
+
+    %% Delete object in combination with delete
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() ->
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok = mnesia:delete({Tab, 1}),
+                                      ok = mnesia:delete_object(OtherRec),
+                                      [] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([], mnesia:dirty_read(Tab, 1)),
+
+    %% Several delete_object in same transaction (last on non existing record)
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+    ?match({atomic, ok},
+	   mnesia:transaction(fun() ->
+                                      [OneRec] = mnesia:read(Tab, 1),
+                                      ok = mnesia:delete_object(OneRec),
+                                      ok = mnesia:delete_object(OtherRec),
+                                      [] = mnesia:read(Tab, 1),
+                                      ok
+                              end)),
+    ?match([], mnesia:dirty_read(Tab, 1)),
 
     ?match({'EXIT', {aborted, no_transaction}},  mnesia:delete_object(OneRec)),
 
@@ -255,7 +340,8 @@ delete_object(Config) when is_list(Config) ->
     ?match({aborted, {bad_type, Tab, _}},
 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['$5']}, 21}) end)),
 
-    ?verify_mnesia(Nodes, []).
+    ?match({atomic, ok},  mnesia:delete_table(Tab)),
+    ok.
 
 %% Read matching records
 
-- 
2.26.2

openSUSE Build Service is sponsored by