diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_relational_engine.ml | 5942 |
1 files changed, 3461 insertions, 2481 deletions
diff --git a/test/test_relational_engine.ml b/test/test_relational_engine.ml index 619bc27..1d8f1ca 100644 --- a/test/test_relational_engine.ml +++ b/test/test_relational_engine.ml @@ -58,84 +58,78 @@ let%test_unit "merkle: root hash changes on insert" = assert (root1 <> root2) let%test_unit "merkle: same elements produce same root hash" = - let tree1 = Merkle.empty - |> Merkle.insert "a" - |> Merkle.insert "b" in - let tree2 = Merkle.empty - |> Merkle.insert "b" - |> Merkle.insert "a" in + let tree1 = Merkle.empty |> Merkle.insert "a" |> Merkle.insert "b" in + let tree2 = Merkle.empty |> Merkle.insert "b" |> Merkle.insert "a" in assert (Merkle.root_hash tree1 = Merkle.root_hash tree2) let%test_unit "storage: create and close" = match Management.Physical.Memory.create () with | Error _ -> assert false - | Ok storage -> - Management.Physical.Memory.close storage + | Ok storage -> Management.Physical.Memory.close storage let%test_unit "storage: store and load attribute" = match Management.Physical.Memory.create () with | Error _ -> assert false | Ok storage -> - let value = Obj.magic 42 in - (match Management.Physical.Memory.store_attribute storage value with - | Error _ -> assert false - | Ok hash -> - (match Management.Physical.Memory.load_attribute storage hash with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded) -> - assert ((Obj.magic loaded : int) = 42))); - Management.Physical.Memory.close storage + let value = Obj.magic 42 in + (match Management.Physical.Memory.store_attribute storage value with + | Error _ -> assert false + | Ok hash -> ( + match Management.Physical.Memory.load_attribute storage hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded) -> assert ((Obj.magic loaded : int) = 42))); + Management.Physical.Memory.close storage let%test_unit "storage: store and load raw bytes" = match Management.Physical.Memory.create () with | Error _ -> assert false | Ok storage -> - let hash = "test_hash" in - let data = Bytes.of_string "hello world" in - (match Management.Physical.Memory.store_raw storage hash data with - | Error _ -> assert false - | Ok () -> - (match Management.Physical.Memory.load_raw storage hash with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded) -> - assert (Bytes.equal loaded data))); - Management.Physical.Memory.close storage + let hash = "test_hash" in + let data = Bytes.of_string "hello world" in + (match Management.Physical.Memory.store_raw storage hash data with + | Error _ -> assert false + | Ok () -> ( + match Management.Physical.Memory.load_raw storage hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded) -> assert (Bytes.equal loaded data))); + Management.Physical.Memory.close storage let%test_unit "storage: exists check" = match Management.Physical.Memory.create () with | Error _ -> assert false | Ok storage -> - let hash = "exists_test" in - let data = Bytes.of_string "data" in - (match Management.Physical.Memory.exists storage hash with - | Error _ -> assert false - | Ok exists -> assert (not exists)); - (match Management.Physical.Memory.store_raw storage hash data with - | Error _ -> assert false - | Ok () -> - (match Management.Physical.Memory.exists storage hash with - | Error _ -> assert false - | Ok exists -> assert exists)); - Management.Physical.Memory.close storage + let hash = "exists_test" in + let data = Bytes.of_string "data" in + (match Management.Physical.Memory.exists storage hash with + | Error _ -> assert false + | Ok exists -> assert (not exists)); + (match Management.Physical.Memory.store_raw storage hash data with + | Error _ -> assert false + | Ok () -> ( + match Management.Physical.Memory.exists storage hash with + | Error _ -> assert false + | Ok exists -> assert exists)); + Management.Physical.Memory.close storage let%test_unit "storage: transaction commit" = match Management.Physical.Memory.create () with | Error _ -> assert false | Ok storage -> - let result = Management.Physical.Memory.with_transaction storage (fun () -> - let hash = "tx_test" in - let data = Bytes.of_string "tx_data" in - Management.Physical.Memory.store_raw storage hash data - ) in - (match result with - | Error _ -> assert false - | Ok () -> - (match Management.Physical.Memory.exists storage "tx_test" with - | Error _ -> assert false - | Ok exists -> assert exists)); - Management.Physical.Memory.close storage + let result = + Management.Physical.Memory.with_transaction storage (fun () -> + let hash = "tx_test" in + let data = Bytes.of_string "tx_data" in + Management.Physical.Memory.store_raw storage hash data) + in + (match result with + | Error _ -> assert false + | Ok () -> ( + match Management.Physical.Memory.exists storage "tx_test" with + | Error _ -> assert false + | Ok exists -> assert exists)); + Management.Physical.Memory.close storage let%test_unit "database: create empty" = let db = Management.Database.empty ~name:"test_db" in @@ -147,17 +141,13 @@ let%test_unit "database: create empty" = let%test_unit "database: add relation" = let db = Management.Database.empty ~name:"test_db" in (* Create a minimal relation for testing *) - let relation = Relation.make - ~hash:(Some "rel_hash_1") - ~name:"users" - ~schema:Schema.empty - ~tree:(Some Merkle.empty) - ~constraints:None - ~cardinality:(Conventions.Cardinality.Finite 0) - ~generator:None - ~membership_criteria:(fun _ -> true) - ~provenance:Relation.Provenance.Undefined - ~lineage:(Relation.Lineage.Base "users") + let relation = + Relation.make ~hash:(Some "rel_hash_1") ~name:"users" ~schema:Schema.empty + ~tree:(Some Merkle.empty) ~constraints:None + ~cardinality:(Conventions.Cardinality.Finite 0) ~generator:None + ~membership_criteria:(fun _ -> true) + ~provenance:Relation.Provenance.Undefined + ~lineage:(Relation.Lineage.Base "users") in let db = Management.Database.add_relation db ~relation in assert (Management.Database.has_relation db "users"); @@ -166,17 +156,13 @@ let%test_unit "database: add relation" = let%test_unit "database: remove relation" = let db = Management.Database.empty ~name:"test_db" in - let relation = Relation.make - ~hash:(Some "hash1") - ~name:"users" - ~schema:Schema.empty - ~tree:(Some Merkle.empty) - ~constraints:None - ~cardinality:(Conventions.Cardinality.Finite 0) - ~generator:None - ~membership_criteria:(fun _ -> true) - ~provenance:Relation.Provenance.Undefined - ~lineage:(Relation.Lineage.Base "users") + let relation = + Relation.make ~hash:(Some "hash1") ~name:"users" ~schema:Schema.empty + ~tree:(Some Merkle.empty) ~constraints:None + ~cardinality:(Conventions.Cardinality.Finite 0) ~generator:None + ~membership_criteria:(fun _ -> true) + ~provenance:Relation.Provenance.Undefined + ~lineage:(Relation.Lineage.Base "users") in let db = Management.Database.add_relation db ~relation in assert (Management.Database.has_relation db "users"); @@ -185,17 +171,13 @@ let%test_unit "database: remove relation" = let%test_unit "database: update relation" = let db = Management.Database.empty ~name:"test_db" in - let relation = Relation.make - ~hash:(Some "hash1") - ~name:"users" - ~schema:Schema.empty - ~tree:(Some Merkle.empty) - ~constraints:None - ~cardinality:(Conventions.Cardinality.Finite 0) - ~generator:None - ~membership_criteria:(fun _ -> true) - ~provenance:Relation.Provenance.Undefined - ~lineage:(Relation.Lineage.Base "users") + let relation = + Relation.make ~hash:(Some "hash1") ~name:"users" ~schema:Schema.empty + ~tree:(Some Merkle.empty) ~constraints:None + ~cardinality:(Conventions.Cardinality.Finite 0) ~generator:None + ~membership_criteria:(fun _ -> true) + ~provenance:Relation.Provenance.Undefined + ~lineage:(Relation.Lineage.Base "users") in let db = Management.Database.add_relation db ~relation in let old_db_hash = db.hash in @@ -207,29 +189,21 @@ let%test_unit "database: update relation" = let%test_unit "database: get relation names" = let db = Management.Database.empty ~name:"test_db" in - let rel1 = Relation.make - ~hash:(Some "h1") - ~name:"users" - ~schema:Schema.empty - ~tree:(Some Merkle.empty) - ~constraints:None - ~cardinality:(Conventions.Cardinality.Finite 0) - ~generator:None - ~membership_criteria:(fun _ -> true) - ~provenance:Relation.Provenance.Undefined - ~lineage:(Relation.Lineage.Base "users") - in - let rel2 = Relation.make - ~hash:(Some "h2") - ~name:"orders" - ~schema:Schema.empty - ~tree:(Some Merkle.empty) - ~constraints:None - ~cardinality:(Conventions.Cardinality.Finite 0) - ~generator:None - ~membership_criteria:(fun _ -> true) - ~provenance:Relation.Provenance.Undefined - ~lineage:(Relation.Lineage.Base "orders") + let rel1 = + Relation.make ~hash:(Some "h1") ~name:"users" ~schema:Schema.empty + ~tree:(Some Merkle.empty) ~constraints:None + ~cardinality:(Conventions.Cardinality.Finite 0) ~generator:None + ~membership_criteria:(fun _ -> true) + ~provenance:Relation.Provenance.Undefined + ~lineage:(Relation.Lineage.Base "users") + in + let rel2 = + Relation.make ~hash:(Some "h2") ~name:"orders" ~schema:Schema.empty + ~tree:(Some Merkle.empty) ~constraints:None + ~cardinality:(Conventions.Cardinality.Finite 0) ~generator:None + ~membership_criteria:(fun _ -> true) + ~provenance:Relation.Provenance.Undefined + ~lineage:(Relation.Lineage.Base "orders") in let db = Management.Database.add_relation db ~relation:rel1 in let db = Management.Database.add_relation db ~relation:rel2 in @@ -243,780 +217,1039 @@ let with_storage f = match Management.Physical.Memory.create () with | Error _ -> assert false | Ok storage -> - let result = f storage in - Management.Physical.Memory.close storage; - result + let result = f storage in + Management.Physical.Memory.close storage; + result let%test_unit "manipulation: create database" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"my_db" with - | Error _ -> assert false - | Ok db -> assert (db.name = "my_db")) + match Manipulation.Memory.create_database storage ~name:"my_db" with + | Error _ -> assert false + | Ok db -> assert (db.name = "my_db")) let%test_unit "manipulation: create relation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "name" "string" - |> Schema.add "age" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - assert (relation.name = "users"); - assert (relation.schema = schema); - assert (Management.Database.has_relation db "users")) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = + Schema.empty |> Schema.add "name" "string" |> Schema.add "age" "integer" + in + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> + assert (relation.name = "users"); + assert (relation.schema = schema); + assert (Management.Database.has_relation db "users")) let%test_unit "manipulation: create relation already exists" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false - | Ok (db, _) -> - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error (Manipulation.RelationAlreadyExists _) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> ( + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error (Manipulation.RelationAlreadyExists _) -> () + | _ -> assert false)) let%test_unit "manipulation: retract relation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false - | Ok (db, _) -> - assert (Management.Database.has_relation db "users"); - match Manipulation.Memory.retract_relation storage db ~name:"users" with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with | Error _ -> assert false - | Ok db -> - assert (not (Management.Database.has_relation db "users"))) + | Ok (db, _) -> ( + assert (Management.Database.has_relation db "users"); + match + Manipulation.Memory.retract_relation storage db ~name:"users" + with + | Error _ -> assert false + | Ok db -> assert (not (Management.Database.has_relation db "users")))) let%test_unit "manipulation: create tuple with storage" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "name" "string" in - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "name" { Attribute.value = Obj.magic "Alice" } in - let tuple : Tuple.materialized = { relation = "users"; attributes = attrs } in - match Manipulation.Memory.create_tuple storage db relation tuple with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "name" "string" in + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with | Error _ -> assert false - | Ok (_db, new_relation, _hash) -> - assert (Manipulation.Memory.tuple_count new_relation = 1)) + | Ok (db, relation) -> ( + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "name" + { Attribute.value = Obj.magic "Alice" } + in + let tuple : Tuple.materialized = + { relation = "users"; attributes = attrs } + in + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok (_db, new_relation, _hash) -> + assert (Manipulation.Memory.tuple_count new_relation = 1))) let%test_unit "manipulation: create and load tuple" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "value" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"numbers" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "value" { Attribute.value = Obj.magic 42 } in - let tuple : Tuple.materialized = { relation = "numbers"; attributes = attrs } in - match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok (_db, _relation, tuple_hash) -> - (* Now load the tuple back from storage *) - match Manipulation.Memory.load_tuple storage tuple_hash with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded_tuple) -> - assert (loaded_tuple.relation = "numbers"); - let loaded_value = Tuple.AttributeMap.find "value" loaded_tuple.attributes in - assert ((Obj.magic loaded_value.Attribute.value : int) = 42)) + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "value" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"numbers" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> ( + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "value" { Attribute.value = Obj.magic 42 } + in + let tuple : Tuple.materialized = + { relation = "numbers"; attributes = attrs } + in + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok (_db, _relation, tuple_hash) -> ( + (* Now load the tuple back from storage *) + match Manipulation.Memory.load_tuple storage tuple_hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded_tuple) -> + assert (loaded_tuple.relation = "numbers"); + let loaded_value = + Tuple.AttributeMap.find "value" loaded_tuple.attributes + in + assert ((Obj.magic loaded_value.Attribute.value : int) = 42)))) let%test_unit "manipulation: create multiple tuples with storage" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "id" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"items" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let make_tuple id = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.magic id } in - ({ Tuple.relation = "items"; attributes = attrs } : Tuple.materialized) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db in - let tuples = [make_tuple 1; make_tuple 2; make_tuple 3] in - match Manipulation.Memory.create_tuples storage db relation tuples with + let schema = Schema.empty |> Schema.add "id" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"items" ~schema + with | Error _ -> assert false - | Ok (_db, new_relation, hashes) -> - assert (Manipulation.Memory.tuple_count new_relation = 3); - assert (List.length hashes = 3)) + | Ok (db, relation) -> ( + let make_tuple id = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.magic id } + in + ({ Tuple.relation = "items"; attributes = attrs } + : Tuple.materialized) + in + let tuples = [ make_tuple 1; make_tuple 2; make_tuple 3 ] in + match + Manipulation.Memory.create_tuples storage db relation tuples + with + | Error _ -> assert false + | Ok (_db, new_relation, hashes) -> + assert (Manipulation.Memory.tuple_count new_relation = 3); + assert (List.length hashes = 3))) let%test_unit "manipulation: load multiple tuples" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "n" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"test" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let make_tuple n = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } in - ({ Tuple.relation = "test"; attributes = attrs } : Tuple.materialized) - in - match Manipulation.Memory.create_tuples storage db relation [make_tuple 10; make_tuple 20] with - | Error _ -> assert false - | Ok (_db, _relation, hashes) -> - match Manipulation.Memory.load_tuples storage hashes with - | Error _ -> assert false - | Ok loaded -> - assert (List.length loaded = 2); - let values = List.map (fun t -> - let attr = Tuple.AttributeMap.find "n" t.Tuple.attributes in - (Obj.magic attr.Attribute.value : int) - ) loaded in - assert (List.mem 10 values); - assert (List.mem 20 values)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "n" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"test" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> ( + let make_tuple n = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } + in + ({ Tuple.relation = "test"; attributes = attrs } + : Tuple.materialized) + in + match + Manipulation.Memory.create_tuples storage db relation + [ make_tuple 10; make_tuple 20 ] + with + | Error _ -> assert false + | Ok (_db, _relation, hashes) -> ( + match Manipulation.Memory.load_tuples storage hashes with + | Error _ -> assert false + | Ok loaded -> + assert (List.length loaded = 2); + let values = + List.map + (fun t -> + let attr = + Tuple.AttributeMap.find "n" t.Tuple.attributes + in + (Obj.magic attr.Attribute.value : int)) + loaded + in + assert (List.mem 10 values); + assert (List.mem 20 values)))) let%test_unit "manipulation: retract tuple" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "value" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"numbers" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "value" { Attribute.value = Obj.magic 42 } in - let tuple : Tuple.materialized = { relation = "numbers"; attributes = attrs } in - match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok (db, relation, tuple_hash) -> - assert (Manipulation.Memory.tuple_count relation = 1); - match Manipulation.Memory.retract_tuple storage db relation ~tuple_hash with - | Error _ -> assert false - | Ok (_db, relation) -> - assert (Manipulation.Memory.tuple_count relation = 0); - (* Tuple data still exists in storage (append-only) *) - match Manipulation.Memory.load_tuple storage tuple_hash with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "value" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"numbers" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> ( + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "value" { Attribute.value = Obj.magic 42 } + in + let tuple : Tuple.materialized = + { relation = "numbers"; attributes = attrs } + in + match Manipulation.Memory.create_tuple storage db relation tuple with | Error _ -> assert false - | Ok None -> assert false - | Ok (Some _) -> () (* Data is still there *)) + | Ok (db, relation, tuple_hash) -> ( + assert (Manipulation.Memory.tuple_count relation = 1); + match + Manipulation.Memory.retract_tuple storage db relation + ~tuple_hash + with + | Error _ -> assert false + | Ok (_db, relation) -> ( + assert (Manipulation.Memory.tuple_count relation = 0); + (* Tuple data still exists in storage (append-only) *) + match Manipulation.Memory.load_tuple storage tuple_hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some _) -> () (* Data is still there *))))) let%test_unit "manipulation: tuple hashes" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - match Manipulation.Memory.create_relation storage db ~name:"test" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let make_tuple n = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } in - ({ Tuple.relation = "test"; attributes = attrs } : Tuple.materialized) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db in - match Manipulation.Memory.create_tuples storage db relation [make_tuple 1; make_tuple 2] with + let schema = Schema.empty in + match + Manipulation.Memory.create_relation storage db ~name:"test" ~schema + with | Error _ -> assert false - | Ok (_db, relation, _) -> - let hashes = Manipulation.Memory.tuple_hashes relation in - assert (List.length hashes = 2)) + | Ok (db, relation) -> ( + let make_tuple n = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } + in + ({ Tuple.relation = "test"; attributes = attrs } + : Tuple.materialized) + in + match + Manipulation.Memory.create_tuples storage db relation + [ make_tuple 1; make_tuple 2 ] + with + | Error _ -> assert false + | Ok (_db, relation, _) -> + let hashes = Manipulation.Memory.tuple_hashes relation in + assert (List.length hashes = 2))) let%test_unit "manipulation: clear relation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - match Manipulation.Memory.create_relation storage db ~name:"test" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let make_tuple n = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } in - ({ Tuple.relation = "test"; attributes = attrs } : Tuple.materialized) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db in - match Manipulation.Memory.create_tuples storage db relation [make_tuple 1; make_tuple 2; make_tuple 3] with + let schema = Schema.empty in + match + Manipulation.Memory.create_relation storage db ~name:"test" ~schema + with | Error _ -> assert false - | Ok (db, relation, _) -> - assert (Manipulation.Memory.tuple_count relation = 3); - match Manipulation.Memory.clear_relation storage db relation with - | Error _ -> assert false - | Ok (_db, relation) -> - assert (Manipulation.Memory.tuple_count relation = 0)) + | Ok (db, relation) -> ( + let make_tuple n = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "n" { Attribute.value = Obj.magic n } + in + ({ Tuple.relation = "test"; attributes = attrs } + : Tuple.materialized) + in + match + Manipulation.Memory.create_tuples storage db relation + [ make_tuple 1; make_tuple 2; make_tuple 3 ] + with + | Error _ -> assert false + | Ok (db, relation, _) -> ( + assert (Manipulation.Memory.tuple_count relation = 3); + match Manipulation.Memory.clear_relation storage db relation with + | Error _ -> assert false + | Ok (_db, relation) -> + assert (Manipulation.Memory.tuple_count relation = 0)))) let%test_unit "manipulation: duplicate tuple rejected" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "x" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"test" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } in - let tuple : Tuple.materialized = { relation = "test"; attributes = attrs } in - match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok (db, relation, _) -> - (* Try to insert the same tuple again *) - match Manipulation.Memory.create_tuple storage db relation tuple with - | Error (Manipulation.DuplicateTuple _) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "x" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"test" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> ( + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } + in + let tuple : Tuple.materialized = + { relation = "test"; attributes = attrs } + in + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok (db, relation, _) -> ( + (* Try to insert the same tuple again *) + match + Manipulation.Memory.create_tuple storage db relation tuple + with + | Error (Manipulation.DuplicateTuple _) -> () + | _ -> assert false))) let%test_unit "manipulation: tuple_exists check" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - match Manipulation.Memory.create_relation storage db ~name:"test" ~schema with - | Error _ -> assert false - | Ok (db, relation) -> - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "v" { Attribute.value = Obj.magic 99 } in - let tuple : Tuple.materialized = { relation = "test"; attributes = attrs } in - let tuple_hash = Manipulation.hash_tuple tuple in - assert (not (Manipulation.Memory.tuple_exists relation tuple_hash)); - match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok (_db, relation, hash) -> - assert (hash = tuple_hash); - assert (Manipulation.Memory.tuple_exists relation tuple_hash)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in + match + Manipulation.Memory.create_relation storage db ~name:"test" ~schema + with + | Error _ -> assert false + | Ok (db, relation) -> ( + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "v" { Attribute.value = Obj.magic 99 } + in + let tuple : Tuple.materialized = + { relation = "test"; attributes = attrs } + in + let tuple_hash = Manipulation.hash_tuple tuple in + assert (not (Manipulation.Memory.tuple_exists relation tuple_hash)); + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok (_db, relation, hash) -> + assert (hash = tuple_hash); + assert (Manipulation.Memory.tuple_exists relation tuple_hash))) let%test_unit "manipulation: hash_tuple deterministic" = - let attrs = Tuple.AttributeMap.empty + let attrs = + Tuple.AttributeMap.empty |> Tuple.AttributeMap.add "a" { Attribute.value = Obj.magic 1 } - |> Tuple.AttributeMap.add "b" { Attribute.value = Obj.magic 2 } in + |> Tuple.AttributeMap.add "b" { Attribute.value = Obj.magic 2 } + in let tuple : Tuple.materialized = { relation = "test"; attributes = attrs } in let hash1 = Manipulation.hash_tuple tuple in let hash2 = Manipulation.hash_tuple tuple in assert (hash1 = hash2) let%test_unit "manipulation: different tuples different hashes" = - let tuple1 : Tuple.materialized = { - relation = "test"; - attributes = Tuple.AttributeMap.singleton "x" { Attribute.value = Obj.magic 1 } - } in - let tuple2 : Tuple.materialized = { - relation = "test"; - attributes = Tuple.AttributeMap.singleton "x" { Attribute.value = Obj.magic 2 } - } in + let tuple1 : Tuple.materialized = + { + relation = "test"; + attributes = + Tuple.AttributeMap.singleton "x" { Attribute.value = Obj.magic 1 }; + } + in + let tuple2 : Tuple.materialized = + { + relation = "test"; + attributes = + Tuple.AttributeMap.singleton "x" { Attribute.value = Obj.magic 2 }; + } + in let hash1 = Manipulation.hash_tuple tuple1 in let hash2 = Manipulation.hash_tuple tuple2 in assert (hash1 <> hash2) let%test_unit "manipulation: get_relation from database" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "id" "integer" in - match Manipulation.Memory.create_relation storage db ~name:"items" ~schema with - | Error _ -> assert false - | Ok (db, _relation) -> - (* Now get the relation from the database *) - match Manipulation.Memory.get_relation db ~name:"items" with - | None -> assert false - | Some rel -> - assert (rel.name = "items"); - assert (rel.schema = schema)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "id" "integer" in + match + Manipulation.Memory.create_relation storage db ~name:"items" ~schema + with + | Error _ -> assert false + | Ok (db, _relation) -> ( + (* Now get the relation from the database *) + match Manipulation.Memory.get_relation db ~name:"items" with + | None -> assert false + | Some rel -> + assert (rel.name = "items"); + assert (rel.schema = schema))) let%test_unit "schema: persisted and loaded correctly" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty - |> Schema.add "id" "integer" - |> Schema.add "name" "string" - |> Schema.add "email" "string" in - match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false - | Ok (db, _relation) -> - (* Get the relation hash *) - let rel_hash = Option.get (Management.Database.get_relation_hash db "users") in - (* Load the relation from storage *) - match Manipulation.Memory.load_relation storage rel_hash with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = + Schema.empty |> Schema.add "id" "integer" |> Schema.add "name" "string" + |> Schema.add "email" "string" + in + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded_rel) -> - (* Verify schema was persisted and loaded correctly *) - assert (loaded_rel.schema = schema); - assert (List.mem ("id", "integer") loaded_rel.schema); - assert (List.mem ("name", "string") loaded_rel.schema); - assert (List.mem ("email", "string") loaded_rel.schema)) + | Ok (db, _relation) -> ( + (* Get the relation hash *) + let rel_hash = + Option.get (Management.Database.get_relation_hash db "users") + in + (* Load the relation from storage *) + match Manipulation.Memory.load_relation storage rel_hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded_rel) -> + (* Verify schema was persisted and loaded correctly *) + assert (loaded_rel.schema = schema); + assert (List.mem ("id", "integer") loaded_rel.schema); + assert (List.mem ("name", "string") loaded_rel.schema); + assert (List.mem ("email", "string") loaded_rel.schema))) let%test_unit "catalog: create_database seeds 6 catalog relations" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let catalog_names = Prelude.Catalog.catalog_relation_names in - List.iter (fun name -> - assert (Management.Database.has_relation db name) - ) catalog_names) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let catalog_names = Prelude.Catalog.catalog_relation_names in + List.iter + (fun name -> assert (Management.Database.has_relation db name)) + catalog_names) let%test_unit "catalog: sakura:relation contains all 6 catalog names" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let rel = match Manipulation.Memory.get_relation db ~name:"sakura:relation" with - | None -> assert false - | Some r -> r - in - assert (Manipulation.Memory.tuple_count rel = 6)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let rel = + match + Manipulation.Memory.get_relation db ~name:"sakura:relation" + with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 6)) let%test_unit "catalog: sakura:on contains insert, update, delete" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let on_rel = match Manipulation.Memory.get_relation db ~name:"sakura:on" with - | None -> assert false - | Some r -> r - in - assert (Manipulation.Memory.tuple_count on_rel = 3)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let on_rel = + match Manipulation.Memory.get_relation db ~name:"sakura:on" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count on_rel = 3)) let%test_unit "catalog: sakura:timing contains immediate, deferred" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let timing_rel = match Manipulation.Memory.get_relation db ~name:"sakura:timing" with - | None -> assert false - | Some r -> r - in - assert (Manipulation.Memory.tuple_count timing_rel = 2)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let timing_rel = + match Manipulation.Memory.get_relation db ~name:"sakura:timing" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count timing_rel = 2)) let%test_unit "catalog: sakura:domain seeded with 4 prelude domains" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let dom_rel = match Manipulation.Memory.get_relation db ~name:"sakura:domain" with - | None -> assert false - | Some r -> r - in - assert (Manipulation.Memory.tuple_count dom_rel = 4)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let dom_rel = + match Manipulation.Memory.get_relation db ~name:"sakura:domain" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count dom_rel = 4)) let%test_unit "catalog: create_relation updates sakura:relation" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let schema = Schema.empty |> Schema.add "id" "natural" in - let db = match Manipulation.Memory.create_relation storage db ~name:"employees" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let rel = match Manipulation.Memory.get_relation db ~name:"sakura:relation" with - | None -> assert false | Some r -> r - in - (* 6 catalog relations + 1 user relation *) - assert (Manipulation.Memory.tuple_count rel = 7)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let schema = Schema.empty |> Schema.add "id" "natural" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"employees" + ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let rel = + match + Manipulation.Memory.get_relation db ~name:"sakura:relation" + with + | None -> assert false + | Some r -> r + in + (* 6 catalog relations + 1 user relation *) + assert (Manipulation.Memory.tuple_count rel = 7)) let%test_unit "catalog: create_relation updates sakura:attribute" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let schema = Schema.empty |> Schema.add "id" "natural" in - let db = match Manipulation.Memory.create_relation storage db ~name:"employees" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let attr_rel = match Manipulation.Memory.get_relation db ~name:"sakura:attribute" with - | None -> assert false | Some r -> r - in - (* catalog attribute tuples (sum of schema sizes of all 6 catalog relations) + 1 for employees *) - (* sakura:relation: 1, sakura:domain: 1, sakura:attribute: 3, sakura:constraint: 2, + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let schema = Schema.empty |> Schema.add "id" "natural" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"employees" + ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let attr_rel = + match + Manipulation.Memory.get_relation db ~name:"sakura:attribute" + with + | None -> assert false + | Some r -> r + in + (* catalog attribute tuples (sum of schema sizes of all 6 catalog relations) + 1 for employees *) + (* sakura:relation: 1, sakura:domain: 1, sakura:attribute: 3, sakura:constraint: 2, sakura:on: 1, sakura:timing: 1 => 9 catalog attrs + 1 for employees = 10 *) - let count = Manipulation.Memory.tuple_count attr_rel in - assert (count = 10)) + let count = Manipulation.Memory.tuple_count attr_rel in + assert (count = 10)) let%test_unit "catalog: retract_relation removes from sakura:relation" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let schema = Schema.empty |> Schema.add "id" "natural" in - let db = match Manipulation.Memory.create_relation storage db ~name:"employees" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Manipulation.Memory.retract_relation storage db ~name:"employees" with - | Error _ -> assert false | Ok db -> db - in - let rel = match Manipulation.Memory.get_relation db ~name:"sakura:relation" with - | None -> assert false | Some r -> r - in - (* Back to 6 catalog relations *) - assert (Manipulation.Memory.tuple_count rel = 6)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let schema = Schema.empty |> Schema.add "id" "natural" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"employees" + ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Manipulation.Memory.retract_relation storage db ~name:"employees" + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match + Manipulation.Memory.get_relation db ~name:"sakura:relation" + with + | None -> assert false + | Some r -> r + in + (* Back to 6 catalog relations *) + assert (Manipulation.Memory.tuple_count rel = 6)) let%test_unit "catalog: register_constraint inserts into sakura:constraint" = with_storage (fun storage -> - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> - let schema = Schema.empty |> Schema.add "id" "natural" in - let db = match Manipulation.Memory.create_relation storage db ~name:"orders" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Manipulation.Memory.register_constraint storage db - ~constraint_name:"orders_id_positive" - ~relation_name:"orders" - ~body:(Constraint.And []) with - | Error _ -> assert false | Ok db -> db - in - let con_rel = match Manipulation.Memory.get_relation db ~name:"sakura:constraint" with - | None -> assert false | Some r -> r - in - assert (Manipulation.Memory.tuple_count con_rel = 1)) + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> + let schema = Schema.empty |> Schema.add "id" "natural" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"orders" + ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"orders_id_positive" ~relation_name:"orders" + ~body:(Constraint.And []) + with + | Error _ -> assert false + | Ok db -> db + in + let con_rel = + match + Manipulation.Memory.get_relation db ~name:"sakura:constraint" + with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count con_rel = 1)) let%test_unit "integration: full workflow with storage" = with_storage (fun storage -> - (* Create database *) - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - - (* Create products relation *) - let schema = Schema.empty - |> Schema.add "id" "integer" - |> Schema.add "name" "string" - |> Schema.add "price" "integer" in + (* Create database *) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in - let (db, products) = match Manipulation.Memory.create_relation storage db ~name:"products" ~schema with - | Error _ -> assert false - | Ok x -> x - in + (* Create products relation *) + let schema = + Schema.empty |> Schema.add "id" "integer" |> Schema.add "name" "string" + |> Schema.add "price" "integer" + in - (* Insert some products *) - let make_product id name price = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.magic id } - |> Tuple.AttributeMap.add "name" { Attribute.value = Obj.magic name } - |> Tuple.AttributeMap.add "price" { Attribute.value = Obj.magic price } in - ({ Tuple.relation = "products"; attributes = attrs } : Tuple.materialized) - in + let db, products = + match + Manipulation.Memory.create_relation storage db ~name:"products" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in - let tuples = [ - make_product 1 "Apple" 100; - make_product 2 "Banana" 50; - make_product 3 "Cherry" 200; - ] in + (* Insert some products *) + let make_product id name price = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.magic id } + |> Tuple.AttributeMap.add "name" { Attribute.value = Obj.magic name } + |> Tuple.AttributeMap.add "price" + { Attribute.value = Obj.magic price } + in + ({ Tuple.relation = "products"; attributes = attrs } + : Tuple.materialized) + in - let (db, products, hashes) = match Manipulation.Memory.create_tuples storage db products tuples with - | Error _ -> assert false - | Ok x -> x - in + let tuples = + [ + make_product 1 "Apple" 100; + make_product 2 "Banana" 50; + make_product 3 "Cherry" 200; + ] + in - assert (Manipulation.Memory.tuple_count products = 3); - assert (List.length hashes = 3); + let db, products, hashes = + match Manipulation.Memory.create_tuples storage db products tuples with + | Error _ -> assert false + | Ok x -> x + in - (* Verify we can load all tuples back *) - (match Manipulation.Memory.load_tuples storage hashes with - | Error _ -> assert false - | Ok loaded -> assert (List.length loaded = 3)); + assert (Manipulation.Memory.tuple_count products = 3); + assert (List.length hashes = 3); - (* Delete one product *) - let banana_hash = Manipulation.hash_tuple (make_product 2 "Banana" 50) in - let (_db, products) = match Manipulation.Memory.retract_tuple storage db products ~tuple_hash:banana_hash with + (* Verify we can load all tuples back *) + (match Manipulation.Memory.load_tuples storage hashes with | Error _ -> assert false - | Ok x -> x - in + | Ok loaded -> assert (List.length loaded = 3)); + + (* Delete one product *) + let banana_hash = Manipulation.hash_tuple (make_product 2 "Banana" 50) in + let _db, products = + match + Manipulation.Memory.retract_tuple storage db products + ~tuple_hash:banana_hash + with + | Error _ -> assert false + | Ok x -> x + in - assert (Manipulation.Memory.tuple_count products = 2); + assert (Manipulation.Memory.tuple_count products = 2); - (* Verify remaining hashes *) - let remaining_hashes = Manipulation.Memory.tuple_hashes products in - assert (List.length remaining_hashes = 2); - assert (not (List.mem banana_hash remaining_hashes)); + (* Verify remaining hashes *) + let remaining_hashes = Manipulation.Memory.tuple_hashes products in + assert (List.length remaining_hashes = 2); + assert (not (List.mem banana_hash remaining_hashes)); - (* But banana data is still in storage (append-only) *) - match Manipulation.Memory.load_tuple storage banana_hash with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded) -> - let name_attr = Tuple.AttributeMap.find "name" loaded.attributes in - assert ((Obj.magic name_attr.Attribute.value : string) = "Banana")) + (* But banana data is still in storage (append-only) *) + match Manipulation.Memory.load_tuple storage banana_hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded) -> + let name_attr = Tuple.AttributeMap.find "name" loaded.attributes in + assert ((Obj.magic name_attr.Attribute.value : string) = "Banana")) let%test_unit "integration: database history tracking" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"versioned" with - | Error _ -> assert false | Ok db -> db - in - let initial_hash = db.hash in + let db = + match Manipulation.Memory.create_database storage ~name:"versioned" with + | Error _ -> assert false + | Ok db -> db + in + let initial_hash = db.hash in - let schema = Schema.empty in - let (db, _) = match Manipulation.Memory.create_relation storage db ~name:"rel1" ~schema with - | Error _ -> assert false - | Ok x -> x - in + let schema = Schema.empty in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"rel1" ~schema + with + | Error _ -> assert false + | Ok x -> x + in - (* After first change, initial hash should be in history *) - assert (db.hash <> initial_hash); + (* After first change, initial hash should be in history *) + assert (db.hash <> initial_hash); - let hash_after_rel1 = db.hash in + let hash_after_rel1 = db.hash in - let (db, _) = match Manipulation.Memory.create_relation storage db ~name:"rel2" ~schema with - | Error _ -> assert false - | Ok x -> x - in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"rel2" ~schema + with + | Error _ -> assert false + | Ok x -> x + in - (* After second change, previous hash should be in history *) - assert (db.hash <> hash_after_rel1); - assert (List.mem hash_after_rel1 db.history)) + (* After second change, previous hash should be in history *) + assert (db.hash <> hash_after_rel1); + assert (List.mem hash_after_rel1 db.history)) let%test_unit "integration: hash bubbles up correctly" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in - let (db, relation) = match Manipulation.Memory.create_relation storage db ~name:"items" ~schema with - | Error _ -> assert false - | Ok x -> x - in + let db, relation = + match + Manipulation.Memory.create_relation storage db ~name:"items" ~schema + with + | Error _ -> assert false + | Ok x -> x + in - let db_hash_before = db.hash in - let rel_hash_before = relation.hash in + let db_hash_before = db.hash in + let rel_hash_before = relation.hash in - (* Insert a tuple *) - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } in - let tuple : Tuple.materialized = { relation = "items"; attributes = attrs } in + (* Insert a tuple *) + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } + in + let tuple : Tuple.materialized = + { relation = "items"; attributes = attrs } + in - let (db, relation, _) = match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok x -> x - in + let db, relation, _ = + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok x -> x + in - (* Relation hash should change *) - assert (relation.hash <> rel_hash_before); + (* Relation hash should change *) + assert (relation.hash <> rel_hash_before); - (* Database hash should change *) - assert (db.hash <> db_hash_before); + (* Database hash should change *) + assert (db.hash <> db_hash_before); - (* Old database hash should be in history *) - assert (List.mem db_hash_before db.history); + (* Old database hash should be in history *) + assert (List.mem db_hash_before db.history); - (* Database's merkle tree should contain the new relation hash *) - match relation.hash with - | None -> assert false - | Some rel_hash -> - assert (Merkle.member rel_hash db.tree)) + (* Database's merkle tree should contain the new relation hash *) + match relation.hash with + | None -> assert false + | Some rel_hash -> assert (Merkle.member rel_hash db.tree)) let%test_unit "branching: load database from historical hash" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in - (* Create relation and add a tuple *) - let (db, relation) = match Manipulation.Memory.create_relation storage db ~name:"items" ~schema with - | Error _ -> assert false - | Ok x -> x - in + (* Create relation and add a tuple *) + let db, relation = + match + Manipulation.Memory.create_relation storage db ~name:"items" ~schema + with + | Error _ -> assert false + | Ok x -> x + in - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } in - let tuple : Tuple.materialized = { relation = "items"; attributes = attrs } in + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 1 } + in + let tuple : Tuple.materialized = + { relation = "items"; attributes = attrs } + in - let (db, _relation, _) = match Manipulation.Memory.create_tuple storage db relation tuple with - | Error _ -> assert false - | Ok x -> x - in + let db, _relation, _ = + match Manipulation.Memory.create_tuple storage db relation tuple with + | Error _ -> assert false + | Ok x -> x + in - (* Capture hash H1 - database with 1 tuple *) - let h1 = db.hash in + (* Capture hash H1 - database with 1 tuple *) + let h1 = db.hash in - (* Add another tuple *) - let attrs2 = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 2 } in - let tuple2 : Tuple.materialized = { relation = "items"; attributes = attrs2 } in + (* Add another tuple *) + let attrs2 = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "x" { Attribute.value = Obj.magic 2 } + in + let tuple2 : Tuple.materialized = + { relation = "items"; attributes = attrs2 } + in - (* Get relation from database - it's now integrated! *) - let relation = match Manipulation.Memory.get_relation db ~name:"items" with - | None -> assert false - | Some r -> r - in + (* Get relation from database - it's now integrated! *) + let relation = + match Manipulation.Memory.get_relation db ~name:"items" with + | None -> assert false + | Some r -> r + in - let (db, _relation, _) = match Manipulation.Memory.create_tuple storage db relation tuple2 with - | Error _ -> assert false - | Ok x -> x - in + let db, _relation, _ = + match Manipulation.Memory.create_tuple storage db relation tuple2 with + | Error _ -> assert false + | Ok x -> x + in - let h2 = db.hash in - assert (h1 <> h2); + let h2 = db.hash in + assert (h1 <> h2); - (* Now load H1 from storage - should get database state with 1 tuple *) - match Manipulation.Memory.load_database storage h1 with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded_db) -> - assert (loaded_db.hash = h1); - assert (loaded_db.name = "test"); - (* The relation is now loaded directly in the database *) - match Manipulation.Memory.get_relation loaded_db ~name:"items" with - | None -> assert false - | Some loaded_rel -> - (* Should have 1 tuple *) - assert (Manipulation.Memory.tuple_count loaded_rel = 1)) + (* Now load H1 from storage - should get database state with 1 tuple *) + match Manipulation.Memory.load_database storage h1 with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded_db) -> ( + assert (loaded_db.hash = h1); + assert (loaded_db.name = "test"); + (* The relation is now loaded directly in the database *) + match Manipulation.Memory.get_relation loaded_db ~name:"items" with + | None -> assert false + | Some loaded_rel -> + (* Should have 1 tuple *) + assert (Manipulation.Memory.tuple_count loaded_rel = 1))) let%test_unit "branching: branch from historical state" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in - (* Create relation *) - let (db, relation) = match Manipulation.Memory.create_relation storage db ~name:"data" ~schema with - | Error _ -> assert false - | Ok x -> x - in + (* Create relation *) + let db, relation = + match + Manipulation.Memory.create_relation storage db ~name:"data" ~schema + with + | Error _ -> assert false + | Ok x -> x + in - (* Add tuple A *) - let tuple_a : Tuple.materialized = { - relation = "data"; - attributes = Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "A" } - } in - let (db, relation, _) = match Manipulation.Memory.create_tuple storage db relation tuple_a with - | Error _ -> assert false - | Ok x -> x - in + (* Add tuple A *) + let tuple_a : Tuple.materialized = + { + relation = "data"; + attributes = + Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "A" }; + } + in + let db, relation, _ = + match Manipulation.Memory.create_tuple storage db relation tuple_a with + | Error _ -> assert false + | Ok x -> x + in - (* Capture H1 - state with tuple A *) - let h1 = db.hash in + (* Capture H1 - state with tuple A *) + let h1 = db.hash in - (* Continue on main branch: add tuple B *) - let tuple_b : Tuple.materialized = { - relation = "data"; - attributes = Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "B" } - } in - let (db_main, relation_main, _) = match Manipulation.Memory.create_tuple storage db relation tuple_b with - | Error _ -> assert false - | Ok x -> x - in - let h2 = db_main.hash in + (* Continue on main branch: add tuple B *) + let tuple_b : Tuple.materialized = + { + relation = "data"; + attributes = + Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "B" }; + } + in + let db_main, relation_main, _ = + match Manipulation.Memory.create_tuple storage db relation tuple_b with + | Error _ -> assert false + | Ok x -> x + in + let h2 = db_main.hash in - (* Now branch from H1 and add tuple C instead *) - let db_branch = match Manipulation.Memory.load_database storage h1 with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some d) -> d - in - (* Relation is now loaded directly in the database *) - let relation_branch = match Manipulation.Memory.get_relation db_branch ~name:"data" with - | None -> assert false - | Some r -> r - in + (* Now branch from H1 and add tuple C instead *) + let db_branch = + match Manipulation.Memory.load_database storage h1 with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some d) -> d + in + (* Relation is now loaded directly in the database *) + let relation_branch = + match Manipulation.Memory.get_relation db_branch ~name:"data" with + | None -> assert false + | Some r -> r + in - let tuple_c : Tuple.materialized = { - relation = "data"; - attributes = Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "C" } - } in - let (db_branch, _relation_branch, _) = match Manipulation.Memory.create_tuple storage db_branch relation_branch tuple_c with - | Error _ -> assert false - | Ok x -> x - in - let h3 = db_branch.hash in + let tuple_c : Tuple.materialized = + { + relation = "data"; + attributes = + Tuple.AttributeMap.singleton "v" { Attribute.value = Obj.magic "C" }; + } + in + let db_branch, _relation_branch, _ = + match + Manipulation.Memory.create_tuple storage db_branch relation_branch + tuple_c + with + | Error _ -> assert false + | Ok x -> x + in + let h3 = db_branch.hash in - (* Verify: H1, H2, H3 are all different *) - assert (h1 <> h2); - assert (h1 <> h3); - assert (h2 <> h3); + (* Verify: H1, H2, H3 are all different *) + assert (h1 <> h2); + assert (h1 <> h3); + assert (h2 <> h3); - (* H2 (main branch) has tuples A and B *) - assert (Manipulation.Memory.tuple_count relation_main = 2); + (* H2 (main branch) has tuples A and B *) + assert (Manipulation.Memory.tuple_count relation_main = 2); - (* H1 is still loadable and unchanged *) - match Manipulation.Memory.load_database storage h1 with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded_h1) -> - match Manipulation.Memory.get_relation loaded_h1 ~name:"data" with - | None -> assert false - | Some r -> - assert (Manipulation.Memory.tuple_count r = 1)) + (* H1 is still loadable and unchanged *) + match Manipulation.Memory.load_database storage h1 with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded_h1) -> ( + match Manipulation.Memory.get_relation loaded_h1 ~name:"data" with + | None -> assert false + | Some r -> assert (Manipulation.Memory.tuple_count r = 1))) let%test_unit "branching: full reconstruction from hash" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - - (* Create relation *) - let (db, relation) = match Manipulation.Memory.create_relation storage db ~name:"products" ~schema with - | Error _ -> assert false - | Ok x -> x - in + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in + + (* Create relation *) + let db, relation = + match + Manipulation.Memory.create_relation storage db ~name:"products" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in - (* Add products *) - let make_product name = - { Tuple.relation = "products"; - attributes = Tuple.AttributeMap.singleton "name" { Attribute.value = Obj.magic name } } - in - let (db, _relation, _) = match Manipulation.Memory.create_tuples storage db relation - [make_product "Apple"; make_product "Banana"; make_product "Cherry"] with - | Error _ -> assert false - | Ok x -> x - in + (* Add products *) + let make_product name = + { + Tuple.relation = "products"; + attributes = + Tuple.AttributeMap.singleton "name" + { Attribute.value = Obj.magic name }; + } + in + let db, _relation, _ = + match + Manipulation.Memory.create_tuples storage db relation + [ + make_product "Apple"; make_product "Banana"; make_product "Cherry"; + ] + with + | Error _ -> assert false + | Ok x -> x + in - let saved_hash = db.hash in + let saved_hash = db.hash in - (* Now reconstruct everything from just the hash *) - match Manipulation.Memory.load_database storage saved_hash with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some loaded_db) -> - (* Relation is now loaded directly in the database *) - match Manipulation.Memory.get_relation loaded_db ~name:"products" with - | None -> assert false - | Some loaded_rel -> - (* Get tuple hashes from relation *) - let tuple_hashes = Manipulation.Memory.tuple_hashes loaded_rel in - assert (List.length tuple_hashes = 3); - - (* Load all tuples *) - match Manipulation.Memory.load_tuples storage tuple_hashes with - | Error _ -> assert false - | Ok tuples -> - assert (List.length tuples = 3); - let names = List.map (fun t -> - let attr = Tuple.AttributeMap.find "name" t.Tuple.attributes in - (Obj.magic attr.Attribute.value : string) - ) tuples in - assert (List.mem "Apple" names); - assert (List.mem "Banana" names); - assert (List.mem "Cherry" names)) + (* Now reconstruct everything from just the hash *) + match Manipulation.Memory.load_database storage saved_hash with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some loaded_db) -> ( + (* Relation is now loaded directly in the database *) + match Manipulation.Memory.get_relation loaded_db ~name:"products" with + | None -> assert false + | Some loaded_rel -> ( + (* Get tuple hashes from relation *) + let tuple_hashes = Manipulation.Memory.tuple_hashes loaded_rel in + assert (List.length tuple_hashes = 3); + + (* Load all tuples *) + match Manipulation.Memory.load_tuples storage tuple_hashes with + | Error _ -> assert false + | Ok tuples -> + assert (List.length tuples = 3); + let names = + List.map + (fun t -> + let attr = + Tuple.AttributeMap.find "name" t.Tuple.attributes + in + (Obj.magic attr.Attribute.value : string)) + tuples + in + assert (List.mem "Apple" names); + assert (List.mem "Banana" names); + assert (List.mem "Cherry" names)))) (* Helper: build a materialized tuple with int attributes *) let make_int_tuple relation pairs = let attributes = - List.fold_left (fun acc (k, v) -> - Tuple.AttributeMap.add k { Attribute.value = Obj.repr (v : int) } acc) + List.fold_left + (fun acc (k, v) -> + Tuple.AttributeMap.add k { Attribute.value = Obj.repr (v : int) } acc) Tuple.AttributeMap.empty pairs in (Tuple.Materialized { Tuple.relation; attributes } : Tuple.t) @@ -1024,289 +1257,429 @@ let make_int_tuple relation pairs = (* Helper: build a stored relation with one integer column and given values *) let stored_relation_with_ints storage db ~name ~attr values = let schema = Schema.empty |> Schema.add attr "integer" in - let (db, rel) = match Manipulation.Memory.create_relation storage db ~name ~schema with - | Error _ -> assert false | Ok x -> x + let db, rel = + match Manipulation.Memory.create_relation storage db ~name ~schema with + | Error _ -> assert false + | Ok x -> x in - let tuples = List.map (fun v -> - let attributes = - Tuple.AttributeMap.singleton attr { Attribute.value = Obj.repr (v : int) } - in - ({ Tuple.relation = name; attributes } : Tuple.materialized) - ) values in - let (db, rel, _) = match Manipulation.Memory.create_tuples storage db rel tuples with - | Error _ -> assert false | Ok x -> x + let tuples = + List.map + (fun v -> + let attributes = + Tuple.AttributeMap.singleton attr + { Attribute.value = Obj.repr (v : int) } + in + ({ Tuple.relation = name; attributes } : Tuple.materialized)) + values + in + let db, rel, _ = + match Manipulation.Memory.create_tuples storage db rel tuples with + | Error _ -> assert false + | Ok x -> x in (db, rel) let%test_unit "algebra: const_relation single tuple" = with_storage (fun storage -> - let rel = Algebra.Memory.const_relation - [ "x", Obj.repr (42 : int); - "y", Obj.repr (99 : int) ] in - match Algebra.Memory.materialize storage rel with - | Error _ -> assert false - | Ok [tup] -> - let x: int = Obj.obj (Tuple.AttributeMap.find "x" tup.Tuple.attributes).Attribute.value in - assert (x = 42) - | Ok _ -> assert false) + let rel = + Algebra.Memory.const_relation + [ ("x", Obj.repr (42 : int)); ("y", Obj.repr (99 : int)) ] + in + match Algebra.Memory.materialize storage rel with + | Error _ -> assert false + | Ok [ tup ] -> + let x : int = + Obj.obj + (Tuple.AttributeMap.find "x" tup.Tuple.attributes).Attribute.value + in + assert (x = 42) + | Ok _ -> assert false) let%test_unit "algebra: select_fn with predicate" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (_, rel) = stored_relation_with_ints storage db ~name:"nums" ~attr:"n" [1;2;3;4;5] in - let predicate = function - | Tuple.Materialized t -> - let v: int = Obj.obj (Tuple.AttributeMap.find "n" t.Tuple.attributes).Attribute.value in - v > 3 - | _ -> false - in - match Algebra.Memory.select_fn storage predicate rel with - | Error _ -> assert false - | Ok result -> - match Algebra.Memory.materialize storage result with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let _, rel = + stored_relation_with_ints storage db ~name:"nums" ~attr:"n" + [ 1; 2; 3; 4; 5 ] + in + let predicate = function + | Tuple.Materialized t -> + let v : int = + Obj.obj + (Tuple.AttributeMap.find "n" t.Tuple.attributes).Attribute.value + in + v > 3 + | _ -> false + in + match Algebra.Memory.select_fn storage predicate rel with | Error _ -> assert false - | Ok rows -> - assert (List.length rows = 2); - List.iter (fun t -> - let v: int = Obj.obj (Tuple.AttributeMap.find "n" t.Tuple.attributes).Attribute.value in - assert (v > 3)) rows) + | Ok result -> ( + match Algebra.Memory.materialize storage result with + | Error _ -> assert false + | Ok rows -> + assert (List.length rows = 2); + List.iter + (fun t -> + let v : int = + Obj.obj + (Tuple.AttributeMap.find "n" t.Tuple.attributes) + .Attribute.value + in + assert (v > 3)) + rows)) let%test_unit "algebra: project restricts schema" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "a" "integer" |> Schema.add "b" "integer" in - let (db, rel) = match Manipulation.Memory.create_relation storage db ~name:"ab" ~schema with - | Error _ -> assert false | Ok x -> x - in - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "a" { Attribute.value = Obj.repr (1 : int) } - |> Tuple.AttributeMap.add "b" { Attribute.value = Obj.repr (2 : int) } in - let (_, rel, _) = match Manipulation.Memory.create_tuple storage db rel - { Tuple.relation = "ab"; attributes = attrs } with - | Error _ -> assert false | Ok x -> x - in - match Algebra.Memory.project storage ["a"] rel with - | Error _ -> assert false - | Ok projected -> - match Algebra.Memory.materialize storage projected with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = + Schema.empty |> Schema.add "a" "integer" |> Schema.add "b" "integer" + in + let db, rel = + match + Manipulation.Memory.create_relation storage db ~name:"ab" ~schema + with + | Error _ -> assert false + | Ok x -> x + in + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "a" { Attribute.value = Obj.repr (1 : int) } + |> Tuple.AttributeMap.add "b" { Attribute.value = Obj.repr (2 : int) } + in + let _, rel, _ = + match + Manipulation.Memory.create_tuple storage db rel + { Tuple.relation = "ab"; attributes = attrs } + with + | Error _ -> assert false + | Ok x -> x + in + match Algebra.Memory.project storage [ "a" ] rel with | Error _ -> assert false - | Ok [t] -> - assert (Tuple.AttributeMap.mem "a" t.Tuple.attributes); - assert (not (Tuple.AttributeMap.mem "b" t.Tuple.attributes)) - | Ok _ -> assert false) + | Ok projected -> ( + match Algebra.Memory.materialize storage projected with + | Error _ -> assert false + | Ok [ t ] -> + assert (Tuple.AttributeMap.mem "a" t.Tuple.attributes); + assert (not (Tuple.AttributeMap.mem "b" t.Tuple.attributes)) + | Ok _ -> assert false)) let%test_unit "algebra: rename changes attr names" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (_, rel) = stored_relation_with_ints storage db ~name:"r" ~attr:"x" [7] in - match Algebra.Memory.rename storage [("x", "y")] rel with - | Error _ -> assert false - | Ok renamed -> - match Algebra.Memory.materialize storage renamed with - | Error _ -> assert false - | Ok [t] -> - assert (not (Tuple.AttributeMap.mem "x" t.Tuple.attributes)); - assert (Tuple.AttributeMap.mem "y" t.Tuple.attributes); - let v: int = Obj.obj (Tuple.AttributeMap.find "y" t.Tuple.attributes).Attribute.value in - assert (v = 7) - | Ok _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let _, rel = + stored_relation_with_ints storage db ~name:"r" ~attr:"x" [ 7 ] + in + match Algebra.Memory.rename storage [ ("x", "y") ] rel with + | Error _ -> assert false + | Ok renamed -> ( + match Algebra.Memory.materialize storage renamed with + | Error _ -> assert false + | Ok [ t ] -> + assert (not (Tuple.AttributeMap.mem "x" t.Tuple.attributes)); + assert (Tuple.AttributeMap.mem "y" t.Tuple.attributes); + let v : int = + Obj.obj + (Tuple.AttributeMap.find "y" t.Tuple.attributes) + .Attribute.value + in + assert (v = 7) + | Ok _ -> assert false)) let%test_unit "algebra: equijoin merges matching tuples" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - (* left: {id=1, name="Alice"}, {id=2, name="Bob"} *) - let schema_l = Schema.empty |> Schema.add "id" "integer" |> Schema.add "name" "string" in - let (db, left) = match Manipulation.Memory.create_relation storage db ~name:"L" ~schema:schema_l with - | Error _ -> assert false | Ok x -> x - in - let make_left id name = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.repr (id : int) } - |> Tuple.AttributeMap.add "name" { Attribute.value = Obj.repr (name : string) } in - ({ Tuple.relation = "L"; attributes = attrs } : Tuple.materialized) - in - let (db, left, _) = match Manipulation.Memory.create_tuples storage db left - [make_left 1 "Alice"; make_left 2 "Bob"] with - | Error _ -> assert false | Ok x -> x - in - (* right: {id=1, score=100}, {id=3, score=999} *) - let schema_r = Schema.empty |> Schema.add "id" "integer" |> Schema.add "score" "integer" in - let (db, right) = match Manipulation.Memory.create_relation storage db ~name:"R" ~schema:schema_r with - | Error _ -> assert false | Ok x -> x - in - let make_right id score = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "id" { Attribute.value = Obj.repr (id : int) } - |> Tuple.AttributeMap.add "score" { Attribute.value = Obj.repr (score : int) } in - ({ Tuple.relation = "R"; attributes = attrs } : Tuple.materialized) - in - let (_, right, _) = match Manipulation.Memory.create_tuples storage db right - [make_right 1 100; make_right 3 999] with - | Error _ -> assert false | Ok x -> x - in - match Algebra.Memory.equijoin storage ["id"] left right with - | Error _ -> assert false - | Ok joined -> - match Algebra.Memory.materialize storage joined with - | Error _ -> assert false - | Ok rows -> - (* Only id=1 matches *) - assert (List.length rows = 1); - let t = List.hd rows in - let score: int = Obj.obj (Tuple.AttributeMap.find "score" t.Tuple.attributes).Attribute.value in - assert (score = 100)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + (* left: {id=1, name="Alice"}, {id=2, name="Bob"} *) + let schema_l = + Schema.empty |> Schema.add "id" "integer" |> Schema.add "name" "string" + in + let db, left = + match + Manipulation.Memory.create_relation storage db ~name:"L" + ~schema:schema_l + with + | Error _ -> assert false + | Ok x -> x + in + let make_left id name = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "id" + { Attribute.value = Obj.repr (id : int) } + |> Tuple.AttributeMap.add "name" + { Attribute.value = Obj.repr (name : string) } + in + ({ Tuple.relation = "L"; attributes = attrs } : Tuple.materialized) + in + let db, left, _ = + match + Manipulation.Memory.create_tuples storage db left + [ make_left 1 "Alice"; make_left 2 "Bob" ] + with + | Error _ -> assert false + | Ok x -> x + in + (* right: {id=1, score=100}, {id=3, score=999} *) + let schema_r = + Schema.empty |> Schema.add "id" "integer" + |> Schema.add "score" "integer" + in + let db, right = + match + Manipulation.Memory.create_relation storage db ~name:"R" + ~schema:schema_r + with + | Error _ -> assert false + | Ok x -> x + in + let make_right id score = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "id" + { Attribute.value = Obj.repr (id : int) } + |> Tuple.AttributeMap.add "score" + { Attribute.value = Obj.repr (score : int) } + in + ({ Tuple.relation = "R"; attributes = attrs } : Tuple.materialized) + in + let _, right, _ = + match + Manipulation.Memory.create_tuples storage db right + [ make_right 1 100; make_right 3 999 ] + with + | Error _ -> assert false + | Ok x -> x + in + match Algebra.Memory.equijoin storage [ "id" ] left right with + | Error _ -> assert false + | Ok joined -> ( + match Algebra.Memory.materialize storage joined with + | Error _ -> assert false + | Ok rows -> + (* Only id=1 matches *) + assert (List.length rows = 1); + let t = List.hd rows in + let score : int = + Obj.obj + (Tuple.AttributeMap.find "score" t.Tuple.attributes) + .Attribute.value + in + assert (score = 100))) let%test_unit "algebra: equijoin empty match" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (db, left) = stored_relation_with_ints storage db ~name:"L" ~attr:"id" [1;2] in - let (_, right) = stored_relation_with_ints storage db ~name:"R" ~attr:"id" [9;8] in - match Algebra.Memory.equijoin storage ["id"] left right with - | Error _ -> assert false - | Ok joined -> - match Algebra.Memory.materialize storage joined with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let db, left = + stored_relation_with_ints storage db ~name:"L" ~attr:"id" [ 1; 2 ] + in + let _, right = + stored_relation_with_ints storage db ~name:"R" ~attr:"id" [ 9; 8 ] + in + match Algebra.Memory.equijoin storage [ "id" ] left right with | Error _ -> assert false - | Ok rows -> assert (List.length rows = 0)) + | Ok joined -> ( + match Algebra.Memory.materialize storage joined with + | Error _ -> assert false + | Ok rows -> assert (List.length rows = 0))) let%test_unit "algebra: union concatenates streams" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (db, r1) = stored_relation_with_ints storage db ~name:"A" ~attr:"n" [1;2] in - let (_, r2) = stored_relation_with_ints storage db ~name:"B" ~attr:"n" [3;4] in - match Algebra.Memory.union storage r1 r2 with - | Error _ -> assert false - | Ok unioned -> - match Algebra.Memory.materialize storage unioned with - | Error _ -> assert false - | Ok rows -> - assert (List.length rows = 4); - let vals = List.map (fun t -> - Obj.obj (Tuple.AttributeMap.find "n" t.Tuple.attributes).Attribute.value - ) rows in - assert (List.mem 1 vals); - assert (List.mem 2 vals); - assert (List.mem 3 vals); - assert (List.mem 4 vals)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let db, r1 = + stored_relation_with_ints storage db ~name:"A" ~attr:"n" [ 1; 2 ] + in + let _, r2 = + stored_relation_with_ints storage db ~name:"B" ~attr:"n" [ 3; 4 ] + in + match Algebra.Memory.union storage r1 r2 with + | Error _ -> assert false + | Ok unioned -> ( + match Algebra.Memory.materialize storage unioned with + | Error _ -> assert false + | Ok rows -> + assert (List.length rows = 4); + let vals = + List.map + (fun t -> + Obj.obj + (Tuple.AttributeMap.find "n" t.Tuple.attributes) + .Attribute.value) + rows + in + assert (List.mem 1 vals); + assert (List.mem 2 vals); + assert (List.mem 3 vals); + assert (List.mem 4 vals))) let%test_unit "algebra: diff removes right from left" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (db, r1) = stored_relation_with_ints storage db ~name:"A" ~attr:"n" [1;2;3] in - let (_, r2) = stored_relation_with_ints storage db ~name:"B" ~attr:"n" [2;3] in - match Algebra.Memory.diff storage r1 r2 with - | Error _ -> assert false - | Ok result -> - match Algebra.Memory.materialize storage result with - | Error _ -> assert false - | Ok rows -> - assert (List.length rows = 1); - let v: int = Obj.obj (Tuple.AttributeMap.find "n" (List.hd rows).Tuple.attributes).Attribute.value in - assert (v = 1)) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let db, r1 = + stored_relation_with_ints storage db ~name:"A" ~attr:"n" [ 1; 2; 3 ] + in + let _, r2 = + stored_relation_with_ints storage db ~name:"B" ~attr:"n" [ 2; 3 ] + in + match Algebra.Memory.diff storage r1 r2 with + | Error _ -> assert false + | Ok result -> ( + match Algebra.Memory.materialize storage result with + | Error _ -> assert false + | Ok rows -> + assert (List.length rows = 1); + let v : int = + Obj.obj + (Tuple.AttributeMap.find "n" (List.hd rows).Tuple.attributes) + .Attribute.value + in + assert (v = 1))) let%test_unit "algebra: take limits output" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (_, rel) = stored_relation_with_ints storage db ~name:"big" ~attr:"n" [10;20;30;40;50] in - match Algebra.Memory.take storage 3 rel with - | Error _ -> assert false - | Ok result -> - match Algebra.Memory.materialize storage result with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + let _, rel = + stored_relation_with_ints storage db ~name:"big" ~attr:"n" + [ 10; 20; 30; 40; 50 ] + in + match Algebra.Memory.take storage 3 rel with | Error _ -> assert false - | Ok rows -> assert (List.length rows = 3)) + | Ok result -> ( + match Algebra.Memory.materialize storage result with + | Error _ -> assert false + | Ok rows -> assert (List.length rows = 3))) let%test_unit "drl: parse Base" = match Drl.Parser.of_string {|(Base "users")|} with - | Error _ -> assert false - | Ok (Drl.Ast.Base s) -> assert (s = "users") - | Ok _ -> assert false + | Error _ -> assert false + | Ok (Drl.Ast.Base s) -> assert (s = "users") + | Ok _ -> assert false let%test_unit "drl: parse Const" = match Drl.Parser.of_string {|(Const (("age" (Int 18))))|} with - | Error _ -> assert false - | Ok (Drl.Ast.Const [("age", Drl.Ast.Int 18)]) -> () - | Ok _ -> assert false + | Error _ -> assert false + | Ok (Drl.Ast.Const [ ("age", Drl.Ast.Int 18) ]) -> () + | Ok _ -> assert false let%test_unit "drl: parse Join" = match Drl.Parser.of_string {|(Join (id) (Base "L") (Base "R"))|} with - | Error _ -> assert false - | Ok (Drl.Ast.Join (["id"], Drl.Ast.Base "L", Drl.Ast.Base "R")) -> () - | Ok _ -> assert false + | Error _ -> assert false + | Ok (Drl.Ast.Join ([ "id" ], Drl.Ast.Base "L", Drl.Ast.Base "R")) -> () + | Ok _ -> assert false let%test_unit "drl: parse Select" = let s = {|(Select (Const (("age" (Int 18)))) (Base "users"))|} in match Drl.Parser.of_string s with | Error _ -> assert false | Ok (Drl.Ast.Select (Drl.Ast.Const _, Drl.Ast.Base "users")) -> () - | Ok _ -> assert false + | Ok _ -> assert false let%test_unit "drl: execute Base" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - let (db, _) = stored_relation_with_ints storage db ~name:"items" ~attr:"v" [10; 20] in - match Drl.Parser.of_string {|(Base "items")|} with - | Error _ -> assert false - | Ok query -> - match Drl.Executor.Memory.execute storage db query with - | Error _ -> assert false - | Ok rel -> - match Algebra.Memory.materialize storage rel with + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with | Error _ -> assert false - | Ok rows -> assert (List.length rows = 2)) + | Ok db -> db + in + let db, _ = + stored_relation_with_ints storage db ~name:"items" ~attr:"v" [ 10; 20 ] + in + match Drl.Parser.of_string {|(Base "items")|} with + | Error _ -> assert false + | Ok query -> ( + match Drl.Executor.Memory.execute storage db query with + | Error _ -> assert false + | Ok rel -> ( + match Algebra.Memory.materialize storage rel with + | Error _ -> assert false + | Ok rows -> assert (List.length rows = 2)))) let%test_unit "drl: execute Select+Const" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"test_db" with - | Error _ -> assert false | Ok db -> db - in - (* users: {age=18, name="Alice"}, {age=25, name="Bob"} *) - let schema = Schema.empty |> Schema.add "age" "integer" |> Schema.add "name" "string" in - let (db, users) = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok x -> x - in - let make_user age name = - let attrs = Tuple.AttributeMap.empty - |> Tuple.AttributeMap.add "age" { Attribute.value = Obj.repr (age : int) } - |> Tuple.AttributeMap.add "name" { Attribute.value = Obj.repr (name : string) } in - ({ Tuple.relation = "users"; attributes = attrs } : Tuple.materialized) - in - let (db, _, _) = match Manipulation.Memory.create_tuples storage db users - [make_user 18 "Alice"; make_user 25 "Bob"] with - | Error _ -> assert false | Ok x -> x - in - (* Query: select users where age = 18 *) - let s = {|(Select (Const (("age" (Int 18)))) (Base "users"))|} in - match Drl.Parser.of_string s with - | Error _ -> assert false - | Ok query -> - match Drl.Executor.Memory.execute storage db query with - | Error _ -> assert false - | Ok rel -> - match Algebra.Memory.materialize storage rel with - | Error _ -> assert false - | Ok rows -> - assert (List.length rows = 1); - let name_v: string = - Obj.obj (Tuple.AttributeMap.find "name" (List.hd rows).Tuple.attributes).Attribute.value - in - assert (name_v = "Alice")) + let db = + match Manipulation.Memory.create_database storage ~name:"test_db" with + | Error _ -> assert false + | Ok db -> db + in + (* users: {age=18, name="Alice"}, {age=25, name="Bob"} *) + let schema = + Schema.empty |> Schema.add "age" "integer" |> Schema.add "name" "string" + in + let db, users = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok x -> x + in + let make_user age name = + let attrs = + Tuple.AttributeMap.empty + |> Tuple.AttributeMap.add "age" + { Attribute.value = Obj.repr (age : int) } + |> Tuple.AttributeMap.add "name" + { Attribute.value = Obj.repr (name : string) } + in + ({ Tuple.relation = "users"; attributes = attrs } : Tuple.materialized) + in + let db, _, _ = + match + Manipulation.Memory.create_tuples storage db users + [ make_user 18 "Alice"; make_user 25 "Bob" ] + with + | Error _ -> assert false + | Ok x -> x + in + (* Query: select users where age = 18 *) + let s = {|(Select (Const (("age" (Int 18)))) (Base "users"))|} in + match Drl.Parser.of_string s with + | Error _ -> assert false + | Ok query -> ( + match Drl.Executor.Memory.execute storage db query with + | Error _ -> assert false + | Ok rel -> ( + match Algebra.Memory.materialize storage rel with + | Error _ -> assert false + | Ok rows -> + assert (List.length rows = 1); + let name_v : string = + Obj.obj + (Tuple.AttributeMap.find "name" + (List.hd rows).Tuple.attributes) + .Attribute.value + in + assert (name_v = "Alice")))) (* ---------- Constraint construction ---------- *) @@ -1322,12 +1695,8 @@ let%test_unit "constraint: vars_in MemberOf" = assert (not (List.mem "right" vars)) let%test_unit "constraint: vars_in And" = - let b1 = - Constraint.BindingMap.singleton "left" (Constraint.Var "a") - in - let b2 = - Constraint.BindingMap.singleton "left" (Constraint.Var "b") - in + let b1 = Constraint.BindingMap.singleton "left" (Constraint.Var "a") in + let b2 = Constraint.BindingMap.singleton "left" (Constraint.Var "b") in let c = Constraint.And [ @@ -1340,9 +1709,7 @@ let%test_unit "constraint: vars_in And" = assert (List.mem "b" vars) let%test_unit "constraint: rename_vars" = - let b = - Constraint.BindingMap.singleton "left" (Constraint.Var "old_name") - in + let b = Constraint.BindingMap.singleton "left" (Constraint.Var "old_name") in let c = Constraint.MemberOf { target = "t"; binding = b } in let c' = Constraint.rename_vars [ ("old_name", "new_name") ] c in let vars = Constraint.vars_in c' in @@ -1350,9 +1717,7 @@ let%test_unit "constraint: rename_vars" = assert (not (List.mem "old_name" vars)) let%test_unit "constraint: filter_by_attrs keeps relevant" = - let b = - Constraint.BindingMap.singleton "left" (Constraint.Var "x") - in + let b = Constraint.BindingMap.singleton "left" (Constraint.Var "x") in let c = Constraint.MemberOf { target = "t"; binding = b } in assert (Constraint.filter_by_attrs [ "x" ] c <> None); assert (Constraint.filter_by_attrs [ "y" ] c = None) @@ -1399,7 +1764,8 @@ let%test_unit "constraint: lt shorthand" = let%test_unit "constraint: between shorthand" = match - Constraint.between ~value:(Var "x") ~low:(Const (Obj.repr 0)) + Constraint.between ~value:(Var "x") + ~low:(Const (Obj.repr 0)) ~high:(Const (Obj.repr 100)) with | Constraint.And [ _; _ ] -> () @@ -1444,26 +1810,16 @@ let make_eval_ctx ?(relations = []) () : Constraint.eval_context = } let%test_unit "constraint: evaluate MemberOf success" = - let ctx = - make_eval_ctx - ~relations:[ ("my_rel", fun _pairs -> true) ] - () - in + let ctx = make_eval_ctx ~relations:[ ("my_rel", fun _pairs -> true) ] () in let b = Constraint.BindingMap.empty in let c = Constraint.MemberOf { target = "my_rel"; binding = b } in let tuple : Tuple.materialized = { relation = "test"; attributes = Tuple.AttributeMap.empty } in - match Constraint.evaluate ctx tuple c with - | Ok true -> () - | _ -> assert false + match Constraint.evaluate ctx tuple c with Ok true -> () | _ -> assert false let%test_unit "constraint: evaluate MemberOf failure" = - let ctx = - make_eval_ctx - ~relations:[ ("my_rel", fun _pairs -> false) ] - () - in + let ctx = make_eval_ctx ~relations:[ ("my_rel", fun _pairs -> false) ] () in let b = Constraint.BindingMap.empty in let c = Constraint.MemberOf { target = "my_rel"; binding = b } in let tuple : Tuple.materialized = @@ -1502,20 +1858,14 @@ let%test_unit "constraint: evaluate And short-circuits" = { relation = "test"; attributes = Tuple.AttributeMap.empty } in (match Constraint.evaluate ctx tuple c with - | Error _ -> () - | _ -> assert false); + | Error _ -> () + | _ -> assert false); (* Second branch should not have been called *) assert (!call_count = 1) let%test_unit "constraint: evaluate Or succeeds on first match" = let ctx = - make_eval_ctx - ~relations: - [ - ("a", fun _ -> false); - ("b", fun _ -> true); - ] - () + make_eval_ctx ~relations:[ ("a", fun _ -> false); ("b", fun _ -> true) ] () in let b = Constraint.BindingMap.empty in let c = @@ -1528,9 +1878,7 @@ let%test_unit "constraint: evaluate Or succeeds on first match" = let tuple : Tuple.materialized = { relation = "test"; attributes = Tuple.AttributeMap.empty } in - match Constraint.evaluate ctx tuple c with - | Ok true -> () - | _ -> assert false + match Constraint.evaluate ctx tuple c with Ok true -> () | _ -> assert false let%test_unit "constraint: evaluate Not negates" = let ctx : Constraint.eval_context = @@ -1555,16 +1903,14 @@ let%test_unit "constraint: evaluate Not negates" = let tuple : Tuple.materialized = { relation = "test"; attributes = Tuple.AttributeMap.empty } in - match Constraint.evaluate ctx tuple c with - | Ok true -> () - | _ -> assert false + match Constraint.evaluate ctx tuple c with Ok true -> () | _ -> assert false let%test_unit "constraint: evaluate Exists over finite relation" = let ctx : Constraint.eval_context = { check_membership = (fun _rel_name pairs -> - match List.assoc_opt "left" pairs, List.assoc_opt "right" pairs with + match (List.assoc_opt "left" pairs, List.assoc_opt "right" pairs) with | Some l, Some r -> (Obj.obj l : int) < (Obj.obj r : int) | _ -> false); iterate_finite = @@ -1600,26 +1946,20 @@ let%test_unit "constraint: evaluate Exists over finite relation" = Tuple.AttributeMap.singleton "x" { Attribute.value = Obj.repr 3 }; } in - match Constraint.evaluate ctx tuple c with - | Ok true -> () - | _ -> assert false + match Constraint.evaluate ctx tuple c with Ok true -> () | _ -> assert false let%test_unit "constraint: evaluate Forall fails when not all match" = let ctx : Constraint.eval_context = { check_membership = (fun _rel_name pairs -> - match List.assoc_opt "left" pairs, List.assoc_opt "right" pairs with + match (List.assoc_opt "left" pairs, List.assoc_opt "right" pairs) with | Some l, Some r -> (Obj.obj l : int) < (Obj.obj r : int) | _ -> false); iterate_finite = (fun rel_name -> if rel_name = "small_set" then - Some - [ - [ ("value", Obj.repr 1) ]; - [ ("value", Obj.repr 5) ]; - ] + Some [ [ ("value", Obj.repr 1) ]; [ ("value", Obj.repr 5) ] ] else None); } in @@ -1650,10 +1990,7 @@ let%test_unit "constraint: evaluate Forall fails when not all match" = let%test_unit "constraint: Forall unbounded quantifier errors" = let ctx : Constraint.eval_context = - { - check_membership = (fun _ _ -> true); - iterate_finite = (fun _ -> None); - } + { check_membership = (fun _ _ -> true); iterate_finite = (fun _ -> None) } in let b = Constraint.BindingMap.empty in let c = @@ -1675,667 +2012,659 @@ let%test_unit "constraint: Forall unbounded quantifier errors" = let%test_unit "constraint: create_tuple with passing constraint" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = Schema.empty |> Schema.add "value" "natural" in - let db, _rel = - match - Manipulation.Memory.create_relation storage db ~name:"positives" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - (* Register a constraint: value must be member of natural (always true for naturals) *) - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"is_natural" ~relation_name:"positives" - ~body:(Constraint.And []) - with - | Error _ -> assert false - | Ok db -> db - in - let rel = - match Manipulation.Memory.get_relation db ~name:"positives" with - | None -> assert false - | Some r -> r - in - let tuple : Tuple.materialized = - { - relation = "positives"; - attributes = - Tuple.AttributeMap.singleton "value" - { Attribute.value = Obj.repr 5 }; - } - in - match Manipulation.Memory.create_tuple storage db rel tuple with - | Ok _ -> () - | Error _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "value" "natural" in + let db, _rel = + match + Manipulation.Memory.create_relation storage db ~name:"positives" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* Register a constraint: value must be member of natural (always true for naturals) *) + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"is_natural" ~relation_name:"positives" + ~body:(Constraint.And []) + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Manipulation.Memory.get_relation db ~name:"positives" with + | None -> assert false + | Some r -> r + in + let tuple : Tuple.materialized = + { + relation = "positives"; + attributes = + Tuple.AttributeMap.singleton "value" + { Attribute.value = Obj.repr 5 }; + } + in + match Manipulation.Memory.create_tuple storage db rel tuple with + | Ok _ -> () + | Error _ -> assert false) let%test_unit "constraint: create_tuple with failing constraint" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = Schema.empty |> Schema.add "value" "natural" in - let db, _rel = - match - Manipulation.Memory.create_relation storage db ~name:"checked" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - (* Register a constraint that always fails: MemberOf a nonexistent relation *) - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"impossible" ~relation_name:"checked" - ~body: - (Constraint.MemberOf - { - target = "nonexistent_relation"; - binding = - Constraint.BindingMap.singleton "x" (Constraint.Var "value"); - }) - with - | Error _ -> assert false - | Ok db -> db - in - let rel = - match Manipulation.Memory.get_relation db ~name:"checked" with - | None -> assert false - | Some r -> r - in - let tuple : Tuple.materialized = - { - relation = "checked"; - attributes = - Tuple.AttributeMap.singleton "value" - { Attribute.value = Obj.repr 5 }; - } - in - match Manipulation.Memory.create_tuple storage db rel tuple with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "value" "natural" in + let db, _rel = + match + Manipulation.Memory.create_relation storage db ~name:"checked" ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* Register a constraint that always fails: MemberOf a nonexistent relation *) + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"impossible" ~relation_name:"checked" + ~body: + (Constraint.MemberOf + { + target = "nonexistent_relation"; + binding = + Constraint.BindingMap.singleton "x" + (Constraint.Var "value"); + }) + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Manipulation.Memory.get_relation db ~name:"checked" with + | None -> assert false + | Some r -> r + in + let tuple : Tuple.materialized = + { + relation = "checked"; + attributes = + Tuple.AttributeMap.singleton "value" + { Attribute.value = Obj.repr 5 }; + } + in + match Manipulation.Memory.create_tuple storage db rel tuple with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* ---------- Scenario tests ported from Erlang ---------- *) (* Scenario 1: Mutual exclusion between subtypes *) let%test_unit "constraint scenario: mutual exclusion subtypes" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = Schema.empty |> Schema.add "id" "natural" in - let db, _employee = - match - Manipulation.Memory.create_relation storage db ~name:"employee" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - let db, _manager = - match - Manipulation.Memory.create_relation storage db ~name:"manager" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - (* manager must NOT be in employee (mutual exclusion) *) - let body = - Constraint.Not + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "id" "natural" in + let db, _employee = + match + Manipulation.Memory.create_relation storage db ~name:"employee" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + let db, _manager = + match + Manipulation.Memory.create_relation storage db ~name:"manager" ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* manager must NOT be in employee (mutual exclusion) *) + let body = + Constraint.Not + { + body = + Constraint.MemberOf + { + target = "employee"; + binding = + Constraint.BindingMap.singleton "id" (Constraint.Var "id"); + }; + universe = "manager"; + } + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"not_employee" ~relation_name:"manager" ~body + with + | Error _ -> assert false + | Ok db -> db + in + (* Insert into employee first *) + let employee = + match Manipulation.Memory.get_relation db ~name:"employee" with + | None -> assert false + | Some r -> r + in + let emp_tuple : Tuple.materialized = { - body = - Constraint.MemberOf - { - target = "employee"; - binding = - Constraint.BindingMap.singleton "id" (Constraint.Var "id"); - }; - universe = "manager"; + relation = "employee"; + attributes = + Tuple.AttributeMap.singleton "id" { Attribute.value = Obj.repr 1 }; } - in - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"not_employee" ~relation_name:"manager" ~body - with - | Error _ -> assert false - | Ok db -> db - in - (* Insert into employee first *) - let employee = - match Manipulation.Memory.get_relation db ~name:"employee" with - | None -> assert false - | Some r -> r - in - let emp_tuple : Tuple.materialized = - { - relation = "employee"; - attributes = - Tuple.AttributeMap.singleton "id" - { Attribute.value = Obj.repr 1 }; - } - in - let db, _employee, _ = - match - Manipulation.Memory.create_tuple storage db employee emp_tuple - with - | Error _ -> assert false - | Ok x -> x - in - (* Now try to insert same id into manager, should fail *) - let manager = - match Manipulation.Memory.get_relation db ~name:"manager" with - | None -> assert false - | Some r -> r - in - let mgr_tuple : Tuple.materialized = - { - relation = "manager"; - attributes = - Tuple.AttributeMap.singleton "id" - { Attribute.value = Obj.repr 1 }; - } - in - match Manipulation.Memory.create_tuple storage db manager mgr_tuple with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + in + let db, _employee, _ = + match + Manipulation.Memory.create_tuple storage db employee emp_tuple + with + | Error _ -> assert false + | Ok x -> x + in + (* Now try to insert same id into manager, should fail *) + let manager = + match Manipulation.Memory.get_relation db ~name:"manager" with + | None -> assert false + | Some r -> r + in + let mgr_tuple : Tuple.materialized = + { + relation = "manager"; + attributes = + Tuple.AttributeMap.singleton "id" { Attribute.value = Obj.repr 1 }; + } + in + match Manipulation.Memory.create_tuple storage db manager mgr_tuple with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* Scenario 2: Foreign key constraint *) let%test_unit "constraint scenario: foreign key" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let order_schema = Schema.empty |> Schema.add "order_id" "natural" in - let db, _orders = - match - Manipulation.Memory.create_relation storage db ~name:"orders" - ~schema:order_schema - with - | Error _ -> assert false - | Ok x -> x - in - let item_schema = - Schema.empty |> Schema.add "item_id" "natural" - |> Schema.add "order_id" "natural" - in - let db, _items = - match - Manipulation.Memory.create_relation storage db ~name:"order_items" - ~schema:item_schema - with - | Error _ -> assert false - | Ok x -> x - in - (* FK: order_items.order_id must exist in orders *) - let fk_body = - Constraint.MemberOf + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let order_schema = Schema.empty |> Schema.add "order_id" "natural" in + let db, _orders = + match + Manipulation.Memory.create_relation storage db ~name:"orders" + ~schema:order_schema + with + | Error _ -> assert false + | Ok x -> x + in + let item_schema = + Schema.empty + |> Schema.add "item_id" "natural" + |> Schema.add "order_id" "natural" + in + let db, _items = + match + Manipulation.Memory.create_relation storage db ~name:"order_items" + ~schema:item_schema + with + | Error _ -> assert false + | Ok x -> x + in + (* FK: order_items.order_id must exist in orders *) + let fk_body = + Constraint.MemberOf + { + target = "orders"; + binding = + Constraint.BindingMap.singleton "order_id" + (Constraint.Var "order_id"); + } + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"fk_order" ~relation_name:"order_items" + ~body:fk_body + with + | Error _ -> assert false + | Ok db -> db + in + (* Insert an order *) + let orders = + match Manipulation.Memory.get_relation db ~name:"orders" with + | None -> assert false + | Some r -> r + in + let order_tuple : Tuple.materialized = { - target = "orders"; - binding = - Constraint.BindingMap.singleton "order_id" - (Constraint.Var "order_id"); + relation = "orders"; + attributes = + Tuple.AttributeMap.singleton "order_id" + { Attribute.value = Obj.repr 100 }; } - in - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"fk_order" ~relation_name:"order_items" - ~body:fk_body - with - | Error _ -> assert false - | Ok db -> db - in - (* Insert an order *) - let orders = - match Manipulation.Memory.get_relation db ~name:"orders" with - | None -> assert false - | Some r -> r - in - let order_tuple : Tuple.materialized = - { - relation = "orders"; - attributes = - Tuple.AttributeMap.singleton "order_id" - { Attribute.value = Obj.repr 100 }; - } - in - let db, _orders, _ = - match - Manipulation.Memory.create_tuple storage db orders order_tuple - with - | Error _ -> assert false - | Ok x -> x - in - (* Insert item referencing valid order, should succeed *) - let items = - match Manipulation.Memory.get_relation db ~name:"order_items" with - | None -> assert false - | Some r -> r - in - let valid_item : Tuple.materialized = - { - relation = "order_items"; - attributes = - Tuple.AttributeMap.of_list - [ - ("item_id", { Attribute.value = Obj.repr 1 }); - ("order_id", { Attribute.value = Obj.repr 100 }); - ]; - } - in - let db, items, _ = - match - Manipulation.Memory.create_tuple storage db items valid_item - with - | Error _ -> assert false - | Ok x -> x - in - (* Insert item referencing nonexistent order, should fail *) - let invalid_item : Tuple.materialized = - { - relation = "order_items"; - attributes = - Tuple.AttributeMap.of_list - [ - ("item_id", { Attribute.value = Obj.repr 2 }); - ("order_id", { Attribute.value = Obj.repr 999 }); - ]; - } - in - match Manipulation.Memory.create_tuple storage db items invalid_item with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + in + let db, _orders, _ = + match + Manipulation.Memory.create_tuple storage db orders order_tuple + with + | Error _ -> assert false + | Ok x -> x + in + (* Insert item referencing valid order, should succeed *) + let items = + match Manipulation.Memory.get_relation db ~name:"order_items" with + | None -> assert false + | Some r -> r + in + let valid_item : Tuple.materialized = + { + relation = "order_items"; + attributes = + Tuple.AttributeMap.of_list + [ + ("item_id", { Attribute.value = Obj.repr 1 }); + ("order_id", { Attribute.value = Obj.repr 100 }); + ]; + } + in + let db, items, _ = + match Manipulation.Memory.create_tuple storage db items valid_item with + | Error _ -> assert false + | Ok x -> x + in + (* Insert item referencing nonexistent order, should fail *) + let invalid_item : Tuple.materialized = + { + relation = "order_items"; + attributes = + Tuple.AttributeMap.of_list + [ + ("item_id", { Attribute.value = Obj.repr 2 }); + ("order_id", { Attribute.value = Obj.repr 999 }); + ]; + } + in + match Manipulation.Memory.create_tuple storage db items invalid_item with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* Scenario 3: Self-reference (emp_id != mgr_id via not_equal comparison) *) let%test_unit "constraint scenario: self-reference neq" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = - Schema.empty |> Schema.add "emp_id" "natural" - |> Schema.add "mgr_id" "natural" - in - let db, _rel = - match - Manipulation.Memory.create_relation storage db ~name:"reports_to" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - (* Register the not_equal comparison relation *) - let db, _ = - match - Manipulation.Memory.create_immutable_relation storage db - ~name:"not_equal" - ~schema: - (Schema.empty |> Schema.add "left" "natural" - |> Schema.add "right" "natural") - ~generator:(fun _ -> Generator.Error "not enumerable") - ~membership_criteria:(fun t -> - match t with - | Tuple.Materialized m -> ( - match - ( Tuple.AttributeMap.find_opt "left" m.attributes, - Tuple.AttributeMap.find_opt "right" m.attributes ) - with - | Some l, Some r -> - (Obj.obj l.Attribute.value : int) - <> (Obj.obj r.Attribute.value : int) + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = + Schema.empty + |> Schema.add "emp_id" "natural" + |> Schema.add "mgr_id" "natural" + in + let db, _rel = + match + Manipulation.Memory.create_relation storage db ~name:"reports_to" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* Register the not_equal comparison relation *) + let db, _ = + match + Manipulation.Memory.create_immutable_relation storage db + ~name:"not_equal" + ~schema: + (Schema.empty + |> Schema.add "left" "natural" + |> Schema.add "right" "natural") + ~generator:(fun _ -> Generator.Error "not enumerable") + ~membership_criteria:(fun t -> + match t with + | Tuple.Materialized m -> ( + match + ( Tuple.AttributeMap.find_opt "left" m.attributes, + Tuple.AttributeMap.find_opt "right" m.attributes ) + with + | Some l, Some r -> + (Obj.obj l.Attribute.value : int) + <> (Obj.obj r.Attribute.value : int) + | _ -> false) | _ -> false) - | _ -> false) - ~cardinality:Conventions.Cardinality.AlephZero - with - | Error _ -> assert false - | Ok x -> x - in - (* Constraint: emp_id != mgr_id via not_equal relation *) - let neq_body = - Constraint.neq ~left:(Var "emp_id") ~right:(Var "mgr_id") - in - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"no_self_manage" ~relation_name:"reports_to" - ~body:neq_body - with - | Error _ -> assert false - | Ok db -> db - in - let rel = - match Manipulation.Memory.get_relation db ~name:"reports_to" with - | None -> assert false - | Some r -> r - in - (* Insert (emp_id=1, mgr_id=2), should succeed *) - let valid : Tuple.materialized = - { - relation = "reports_to"; - attributes = - Tuple.AttributeMap.of_list - [ - ("emp_id", { Attribute.value = Obj.repr 1 }); - ("mgr_id", { Attribute.value = Obj.repr 2 }); - ]; - } - in - let db, rel, _ = - match Manipulation.Memory.create_tuple storage db rel valid with - | Error _ -> assert false - | Ok x -> x - in - (* Insert (emp_id=3, mgr_id=3): same person, should fail *) - let invalid : Tuple.materialized = - { - relation = "reports_to"; - attributes = - Tuple.AttributeMap.of_list - [ - ("emp_id", { Attribute.value = Obj.repr 3 }); - ("mgr_id", { Attribute.value = Obj.repr 3 }); - ]; - } - in - match Manipulation.Memory.create_tuple storage db rel invalid with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + ~cardinality:Conventions.Cardinality.AlephZero + with + | Error _ -> assert false + | Ok x -> x + in + (* Constraint: emp_id != mgr_id via not_equal relation *) + let neq_body = + Constraint.neq ~left:(Var "emp_id") ~right:(Var "mgr_id") + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"no_self_manage" ~relation_name:"reports_to" + ~body:neq_body + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Manipulation.Memory.get_relation db ~name:"reports_to" with + | None -> assert false + | Some r -> r + in + (* Insert (emp_id=1, mgr_id=2), should succeed *) + let valid : Tuple.materialized = + { + relation = "reports_to"; + attributes = + Tuple.AttributeMap.of_list + [ + ("emp_id", { Attribute.value = Obj.repr 1 }); + ("mgr_id", { Attribute.value = Obj.repr 2 }); + ]; + } + in + let db, rel, _ = + match Manipulation.Memory.create_tuple storage db rel valid with + | Error _ -> assert false + | Ok x -> x + in + (* Insert (emp_id=3, mgr_id=3): same person, should fail *) + let invalid : Tuple.materialized = + { + relation = "reports_to"; + attributes = + Tuple.AttributeMap.of_list + [ + ("emp_id", { Attribute.value = Obj.repr 3 }); + ("mgr_id", { Attribute.value = Obj.repr 3 }); + ]; + } + in + match Manipulation.Memory.create_tuple storage db rel invalid with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* Scenario 4: Mutual exclusion between open_ticket and closed_ticket *) let%test_unit "constraint scenario: open vs closed ticket" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = Schema.empty |> Schema.add "ticket_id" "natural" in - let db, _ = - match - Manipulation.Memory.create_relation storage db ~name:"open_ticket" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - let db, _ = - match - Manipulation.Memory.create_relation storage db ~name:"closed_ticket" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - (* open_ticket must not be in closed_ticket *) - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"not_closed" ~relation_name:"open_ticket" - ~body: - (Constraint.Not - { - body = - Constraint.MemberOf - { - target = "closed_ticket"; - binding = - Constraint.BindingMap.singleton "ticket_id" - (Constraint.Var "ticket_id"); - }; - universe = "open_ticket"; - }) - with - | Error _ -> assert false - | Ok db -> db - in - (* Insert ticket 1 as closed *) - let closed = - match Manipulation.Memory.get_relation db ~name:"closed_ticket" with - | None -> assert false - | Some r -> r - in - let t1 : Tuple.materialized = - { - relation = "closed_ticket"; - attributes = - Tuple.AttributeMap.singleton "ticket_id" - { Attribute.value = Obj.repr 1 }; - } - in - let db, _, _ = - match Manipulation.Memory.create_tuple storage db closed t1 with - | Error _ -> assert false - | Ok x -> x - in - (* Try to open same ticket, should fail *) - let open_ = - match Manipulation.Memory.get_relation db ~name:"open_ticket" with - | None -> assert false - | Some r -> r - in - let t1_open : Tuple.materialized = - { - relation = "open_ticket"; - attributes = - Tuple.AttributeMap.singleton "ticket_id" - { Attribute.value = Obj.repr 1 }; - } - in - match Manipulation.Memory.create_tuple storage db open_ t1_open with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "ticket_id" "natural" in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"open_ticket" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"closed_ticket" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* open_ticket must not be in closed_ticket *) + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"not_closed" ~relation_name:"open_ticket" + ~body: + (Constraint.Not + { + body = + Constraint.MemberOf + { + target = "closed_ticket"; + binding = + Constraint.BindingMap.singleton "ticket_id" + (Constraint.Var "ticket_id"); + }; + universe = "open_ticket"; + }) + with + | Error _ -> assert false + | Ok db -> db + in + (* Insert ticket 1 as closed *) + let closed = + match Manipulation.Memory.get_relation db ~name:"closed_ticket" with + | None -> assert false + | Some r -> r + in + let t1 : Tuple.materialized = + { + relation = "closed_ticket"; + attributes = + Tuple.AttributeMap.singleton "ticket_id" + { Attribute.value = Obj.repr 1 }; + } + in + let db, _, _ = + match Manipulation.Memory.create_tuple storage db closed t1 with + | Error _ -> assert false + | Ok x -> x + in + (* Try to open same ticket, should fail *) + let open_ = + match Manipulation.Memory.get_relation db ~name:"open_ticket" with + | None -> assert false + | Some r -> r + in + let t1_open : Tuple.materialized = + { + relation = "open_ticket"; + attributes = + Tuple.AttributeMap.singleton "ticket_id" + { Attribute.value = Obj.repr 1 }; + } + in + match Manipulation.Memory.create_tuple storage db open_ t1_open with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* Scenario 5: Weak entity dependency *) let%test_unit "constraint scenario: weak entity dependency" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let parent_schema = Schema.empty |> Schema.add "parent_id" "natural" in - let db, _ = - match - Manipulation.Memory.create_relation storage db ~name:"parent" - ~schema:parent_schema - with - | Error _ -> assert false - | Ok x -> x - in - let dep_schema = - Schema.empty |> Schema.add "dep_id" "natural" - |> Schema.add "parent_id" "natural" - in - let db, _ = - match - Manipulation.Memory.create_relation storage db ~name:"dependent" - ~schema:dep_schema - with - | Error _ -> assert false - | Ok x -> x - in - (* dependent.parent_id must exist in parent *) - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"parent_exists" ~relation_name:"dependent" - ~body: - (Constraint.MemberOf - { - target = "parent"; - binding = - Constraint.BindingMap.singleton "parent_id" - (Constraint.Var "parent_id"); - }) - with - | Error _ -> assert false - | Ok db -> db - in - (* Insert parent *) - let parent = - match Manipulation.Memory.get_relation db ~name:"parent" with - | None -> assert false - | Some r -> r - in - let pt : Tuple.materialized = - { - relation = "parent"; - attributes = - Tuple.AttributeMap.singleton "parent_id" - { Attribute.value = Obj.repr 10 }; - } - in - let db, _, _ = - match Manipulation.Memory.create_tuple storage db parent pt with - | Error _ -> assert false - | Ok x -> x - in - (* Insert dependent with valid parent, should succeed *) - let dep = - match Manipulation.Memory.get_relation db ~name:"dependent" with - | None -> assert false - | Some r -> r - in - let valid_dep : Tuple.materialized = - { - relation = "dependent"; - attributes = - Tuple.AttributeMap.of_list - [ - ("dep_id", { Attribute.value = Obj.repr 1 }); - ("parent_id", { Attribute.value = Obj.repr 10 }); - ]; - } - in - let db, dep, _ = - match - Manipulation.Memory.create_tuple storage db dep valid_dep - with - | Error _ -> assert false - | Ok x -> x - in - (* Insert dependent with nonexistent parent, should fail *) - let invalid_dep : Tuple.materialized = - { - relation = "dependent"; - attributes = - Tuple.AttributeMap.of_list - [ - ("dep_id", { Attribute.value = Obj.repr 2 }); - ("parent_id", { Attribute.value = Obj.repr 999 }); - ]; - } - in - match - Manipulation.Memory.create_tuple storage db dep invalid_dep - with - | Error (Manipulation.ConstraintViolation _) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let parent_schema = Schema.empty |> Schema.add "parent_id" "natural" in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"parent" + ~schema:parent_schema + with + | Error _ -> assert false + | Ok x -> x + in + let dep_schema = + Schema.empty + |> Schema.add "dep_id" "natural" + |> Schema.add "parent_id" "natural" + in + let db, _ = + match + Manipulation.Memory.create_relation storage db ~name:"dependent" + ~schema:dep_schema + with + | Error _ -> assert false + | Ok x -> x + in + (* dependent.parent_id must exist in parent *) + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"parent_exists" ~relation_name:"dependent" + ~body: + (Constraint.MemberOf + { + target = "parent"; + binding = + Constraint.BindingMap.singleton "parent_id" + (Constraint.Var "parent_id"); + }) + with + | Error _ -> assert false + | Ok db -> db + in + (* Insert parent *) + let parent = + match Manipulation.Memory.get_relation db ~name:"parent" with + | None -> assert false + | Some r -> r + in + let pt : Tuple.materialized = + { + relation = "parent"; + attributes = + Tuple.AttributeMap.singleton "parent_id" + { Attribute.value = Obj.repr 10 }; + } + in + let db, _, _ = + match Manipulation.Memory.create_tuple storage db parent pt with + | Error _ -> assert false + | Ok x -> x + in + (* Insert dependent with valid parent, should succeed *) + let dep = + match Manipulation.Memory.get_relation db ~name:"dependent" with + | None -> assert false + | Some r -> r + in + let valid_dep : Tuple.materialized = + { + relation = "dependent"; + attributes = + Tuple.AttributeMap.of_list + [ + ("dep_id", { Attribute.value = Obj.repr 1 }); + ("parent_id", { Attribute.value = Obj.repr 10 }); + ]; + } + in + let db, dep, _ = + match Manipulation.Memory.create_tuple storage db dep valid_dep with + | Error _ -> assert false + | Ok x -> x + in + (* Insert dependent with nonexistent parent, should fail *) + let invalid_dep : Tuple.materialized = + { + relation = "dependent"; + attributes = + Tuple.AttributeMap.of_list + [ + ("dep_id", { Attribute.value = Obj.repr 2 }); + ("parent_id", { Attribute.value = Obj.repr 999 }); + ]; + } + in + match Manipulation.Memory.create_tuple storage db dep invalid_dep with + | Error (Manipulation.ConstraintViolation _) -> () + | _ -> assert false) (* Scenario 6: Algebra propagation, select preserves constraints *) let%test_unit "constraint propagation: select preserves constraints" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = Schema.empty |> Schema.add "x" "natural" in - let db, _rel = - match - Manipulation.Memory.create_relation storage db ~name:"constrained" - ~schema - with - | Error _ -> assert false - | Ok x -> x - in - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"c1" ~relation_name:"constrained" - ~body:(Constraint.And []) - with + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "x" "natural" in + let db, _rel = + match + Manipulation.Memory.create_relation storage db ~name:"constrained" + ~schema + with + | Error _ -> assert false + | Ok x -> x + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"c1" ~relation_name:"constrained" + ~body:(Constraint.And []) + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Manipulation.Memory.get_relation db ~name:"constrained" with + | None -> assert false + | Some r -> r + in + match Algebra.Memory.select_fn storage (fun _ -> true) rel with | Error _ -> assert false - | Ok db -> db - in - let rel = - match Manipulation.Memory.get_relation db ~name:"constrained" with - | None -> assert false - | Some r -> r - in - match - Algebra.Memory.select_fn storage (fun _ -> true) rel - with - | Error _ -> assert false - | Ok result -> - assert (result.Relation.constraints <> None)) + | Ok result -> assert (result.Relation.constraints <> None)) (* Scenario 7: Algebra propagation, project filters constraints *) let%test_unit "constraint propagation: project filters constraints" = with_storage (fun storage -> - let db = - match Manipulation.Memory.create_database storage ~name:"test" with - | Error _ -> assert false - | Ok db -> db - in - let schema = - Schema.empty |> Schema.add "x" "natural" |> Schema.add "y" "natural" - in - let db, _rel = - match - Manipulation.Memory.create_relation storage db ~name:"xy" ~schema - with + let db = + match Manipulation.Memory.create_database storage ~name:"test" with + | Error _ -> assert false + | Ok db -> db + in + let schema = + Schema.empty |> Schema.add "x" "natural" |> Schema.add "y" "natural" + in + let db, _rel = + match + Manipulation.Memory.create_relation storage db ~name:"xy" ~schema + with + | Error _ -> assert false + | Ok x -> x + in + (* Constraint on x only *) + let c_on_x = + Constraint.MemberOf + { + target = "some_rel"; + binding = + Constraint.BindingMap.singleton "left" (Constraint.Var "x"); + } + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"x_only" ~relation_name:"xy" ~body:c_on_x + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Manipulation.Memory.get_relation db ~name:"xy" with + | None -> assert false + | Some r -> r + in + (* Project to only "x", constraint should survive *) + (match Algebra.Memory.project storage [ "x" ] rel with | Error _ -> assert false - | Ok x -> x - in - (* Constraint on x only *) - let c_on_x = - Constraint.MemberOf - { - target = "some_rel"; - binding = - Constraint.BindingMap.singleton "left" (Constraint.Var "x"); - } - in - let db = - match - Manipulation.Memory.register_constraint storage db - ~constraint_name:"x_only" ~relation_name:"xy" ~body:c_on_x - with + | Ok result -> assert (result.Relation.constraints <> None)); + (* Project to only "y", constraint referencing "x" should be dropped *) + match Algebra.Memory.project storage [ "y" ] rel with | Error _ -> assert false - | Ok db -> db - in - let rel = - match Manipulation.Memory.get_relation db ~name:"xy" with - | None -> assert false - | Some r -> r - in - (* Project to only "x", constraint should survive *) - (match Algebra.Memory.project storage [ "x" ] rel with - | Error _ -> assert false - | Ok result -> assert (result.Relation.constraints <> None)); - (* Project to only "y", constraint referencing "x" should be dropped *) - match Algebra.Memory.project storage [ "y" ] rel with - | Error _ -> assert false - | Ok result -> assert (result.Relation.constraints = None)) + | Ok result -> assert (result.Relation.constraints = None)) (* Parse tests use canonical flat sexp format: (Constructor (field val) ...) *) @@ -2355,37 +2684,46 @@ let%test_unit "ddl: parse ClearRelation" = | Ok stmt -> assert (stmt = Ddl.Ast.ClearRelation "users") let%test_unit "dml: round-trip InsertTuple" = - let src = Dml.Ast.InsertTuple { - relation = "users"; - attributes = [("name", Drl.Ast.Str "Alice"); ("age", Drl.Ast.Int 30)] - } in + let src = + Dml.Ast.InsertTuple + { + relation = "users"; + attributes = [ ("name", Drl.Ast.Str "Alice"); ("age", Drl.Ast.Int 30) ]; + } + in match Dml.Parser.of_string (Dml.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "ddl: round-trip CreateRelation" = - let src = Ddl.Ast.CreateRelation { - name = "users"; - schema = [("name", "string"); ("age", "natural")] - } in + let src = + Ddl.Ast.CreateRelation + { name = "users"; schema = [ ("name", "string"); ("age", "natural") ] } + in match Ddl.Parser.of_string (Ddl.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "dml: round-trip InsertTuples" = - let src = Dml.Ast.InsertTuples { - relation = "users"; - tuples = [ - [("name", Drl.Ast.Str "Alice"); ("age", Drl.Ast.Int 30)]; - [("name", Drl.Ast.Str "Bob"); ("age", Drl.Ast.Int 25)]; - ] - } in + let src = + Dml.Ast.InsertTuples + { + relation = "users"; + tuples = + [ + [ ("name", Drl.Ast.Str "Alice"); ("age", Drl.Ast.Int 30) ]; + [ ("name", Drl.Ast.Str "Bob"); ("age", Drl.Ast.Int 25) ]; + ]; + } + in match Dml.Parser.of_string (Dml.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "ddl: round-trip RegisterDomain" = - let src = Ddl.Ast.RegisterDomain { name = "money"; cardinality = Ddl.Ast.AlephZero } in + let src = + Ddl.Ast.RegisterDomain { name = "money"; cardinality = Ddl.Ast.AlephZero } + in match Ddl.Parser.of_string (Ddl.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) @@ -2394,550 +2732,867 @@ let%test_unit "ddl: round-trip RegisterDomain" = let%test_unit "ddl: execute CreateDatabase" = with_storage (fun storage -> - let db = Management.Database.empty ~name:"" in - let stmt = Ddl.Ast.CreateDatabase "shop" in - match Ddl.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok (db, _msg) -> assert (db.name = "shop")) + let db = Management.Database.empty ~name:"" in + let stmt = Ddl.Ast.CreateDatabase "shop" in + match Ddl.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok (db, _msg) -> assert (db.name = "shop")) let%test_unit "ddl: execute CreateRelation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let stmt = Ddl.Ast.CreateRelation { name = "users"; schema = [("name", "string"); ("age", "natural")] } in - match Ddl.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok (db, _msg) -> assert (Management.Database.has_relation db "users")) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let stmt = + Ddl.Ast.CreateRelation + { + name = "users"; + schema = [ ("name", "string"); ("age", "natural") ]; + } + in + match Ddl.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok (db, _msg) -> assert (Management.Database.has_relation db "users")) let%test_unit "dml: execute InsertTuple" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "name" "string" in - let db = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let stmt = Dml.Ast.InsertTuple { relation = "users"; attributes = [("name", Drl.Ast.Str "Alice")] } in - match Dml.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok db -> - let rel = match Management.Database.get_relation db "users" with - | None -> assert false | Some r -> r + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "name" "string" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db in - assert (Manipulation.Memory.tuple_count rel = 1)) + let stmt = + Dml.Ast.InsertTuple + { relation = "users"; attributes = [ ("name", Drl.Ast.Str "Alice") ] } + in + match Dml.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok db -> + let rel = + match Management.Database.get_relation db "users" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 1)) let%test_unit "dml: execute InsertTuples" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "name" "string" in - let db = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let stmt = Dml.Ast.InsertTuples { - relation = "users"; - tuples = [[("name", Drl.Ast.Str "Alice")]; [("name", Drl.Ast.Str "Bob")]] - } in - match Dml.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok db -> - let rel = match Management.Database.get_relation db "users" with - | None -> assert false | Some r -> r + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "name" "string" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let stmt = + Dml.Ast.InsertTuples + { + relation = "users"; + tuples = + [ + [ ("name", Drl.Ast.Str "Alice") ]; + [ ("name", Drl.Ast.Str "Bob") ]; + ]; + } in - assert (Manipulation.Memory.tuple_count rel = 2)) + match Dml.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok db -> + let rel = + match Management.Database.get_relation db "users" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 2)) let%test_unit "dml: execute DeleteTuple" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "name" "string" in - let db = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "users"; attributes = [("name", Drl.Ast.Str "Alice")] }) with - | Error _ -> assert false | Ok db -> db - in - let stmt = Dml.Ast.DeleteTuple { relation = "users"; attributes = [("name", Drl.Ast.Str "Alice")] } in - match Dml.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok db -> - let rel = match Management.Database.get_relation db "users" with - | None -> assert false | Some r -> r + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "name" "string" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "users"; + attributes = [ ("name", Drl.Ast.Str "Alice") ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let stmt = + Dml.Ast.DeleteTuple + { relation = "users"; attributes = [ ("name", Drl.Ast.Str "Alice") ] } in - assert (Manipulation.Memory.tuple_count rel = 0)) + match Dml.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok db -> + let rel = + match Management.Database.get_relation db "users" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 0)) let%test_unit "ddl: execute RetractRelation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty in - let db = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - match Ddl.Executor.Memory.execute storage db (Ddl.Ast.RetractRelation "users") with - | Error _ -> assert false - | Ok (db, _msg) -> assert (not (Management.Database.has_relation db "users"))) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + match + Ddl.Executor.Memory.execute storage db (Ddl.Ast.RetractRelation "users") + with + | Error _ -> assert false + | Ok (db, _msg) -> + assert (not (Management.Database.has_relation db "users"))) let%test_unit "ddl: execute ClearRelation" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "name" "string" in - let db = match Manipulation.Memory.create_relation storage db ~name:"users" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuples { relation = "users"; - tuples = [[("name", Drl.Ast.Str "Alice")]; [("name", Drl.Ast.Str "Bob")]] }) with - | Error _ -> assert false | Ok db -> db - in - match Ddl.Executor.Memory.execute storage db (Ddl.Ast.ClearRelation "users") with - | Error _ -> assert false - | Ok (db, _msg) -> - let rel = match Management.Database.get_relation db "users" with - | None -> assert false | Some r -> r + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "name" "string" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"users" ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuples + { + relation = "users"; + tuples = + [ + [ ("name", Drl.Ast.Str "Alice") ]; + [ ("name", Drl.Ast.Str "Bob") ]; + ]; + }) + with + | Error _ -> assert false + | Ok db -> db in - assert (Manipulation.Memory.tuple_count rel = 0)) + match + Ddl.Executor.Memory.execute storage db (Ddl.Ast.ClearRelation "users") + with + | Error _ -> assert false + | Ok (db, _msg) -> + let rel = + match Management.Database.get_relation db "users" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 0)) let%test_unit "ddl: execute RegisterDomain" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let stmt = Ddl.Ast.RegisterDomain { name = "money"; cardinality = Ddl.Ast.AlephZero } in - match Ddl.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok (db, _msg) -> assert (Management.Database.has_domain db "money")) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let stmt = + Ddl.Ast.RegisterDomain + { name = "money"; cardinality = Ddl.Ast.AlephZero } + in + match Ddl.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok (db, _msg) -> assert (Management.Database.has_domain db "money")) let%test_unit "dml: insert into nonexistent relation returns error" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let stmt = Dml.Ast.InsertTuple { relation = "ghost"; attributes = [("x", Drl.Ast.Int 1)] } in - match Dml.Executor.Memory.execute storage db stmt with - | Error (Dml.Executor.Memory.RelationNotFound "ghost") -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let stmt = + Dml.Ast.InsertTuple + { relation = "ghost"; attributes = [ ("x", Drl.Ast.Int 1) ] } + in + match Dml.Executor.Memory.execute storage db stmt with + | Error (Dml.Executor.Memory.RelationNotFound "ghost") -> () + | _ -> assert false) let%test_unit "dcl: round-trip RegisterConstraint MemberOf" = - let src = Icl.Ast.RegisterConstraint { - constraint_name = "fk_order"; - relation_name = "order_items"; - body = Icl.Ast.MemberOf { target = "orders"; binding = [("order_id", Icl.Ast.Var "order_id")] } - } in + let src = + Icl.Ast.RegisterConstraint + { + constraint_name = "fk_order"; + relation_name = "order_items"; + body = + Icl.Ast.MemberOf + { + target = "orders"; + binding = [ ("order_id", Icl.Ast.Var "order_id") ]; + }; + } + in match Icl.Parser.of_string (Icl.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "dcl: round-trip And constraint" = - let src = Icl.Ast.RegisterConstraint { - constraint_name = "valid_range"; - relation_name = "scores"; - body = Icl.Ast.And [ - Icl.Ast.MemberOf { target = "ge_zero"; - binding = [("left", Icl.Ast.Var "score"); ("right", Icl.Ast.Const (Drl.Ast.Int 0))] }; - Icl.Ast.MemberOf { target = "le_hundred"; - binding = [("left", Icl.Ast.Var "score"); ("right", Icl.Ast.Const (Drl.Ast.Int 100))] }; - ] - } in + let src = + Icl.Ast.RegisterConstraint + { + constraint_name = "valid_range"; + relation_name = "scores"; + body = + Icl.Ast.And + [ + Icl.Ast.MemberOf + { + target = "ge_zero"; + binding = + [ + ("left", Icl.Ast.Var "score"); + ("right", Icl.Ast.Const (Drl.Ast.Int 0)); + ]; + }; + Icl.Ast.MemberOf + { + target = "le_hundred"; + binding = + [ + ("left", Icl.Ast.Var "score"); + ("right", Icl.Ast.Const (Drl.Ast.Int 100)); + ]; + }; + ]; + } + in match Icl.Parser.of_string (Icl.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "dcl: round-trip Not constraint" = - let src = Icl.Ast.RegisterConstraint { - constraint_name = "not_closed"; - relation_name = "open_ticket"; - body = Icl.Ast.Not { - body = Icl.Ast.MemberOf { target = "closed_ticket"; - binding = [("ticket_id", Icl.Ast.Var "ticket_id")] }; - universe = "open_ticket" - } - } in + let src = + Icl.Ast.RegisterConstraint + { + constraint_name = "not_closed"; + relation_name = "open_ticket"; + body = + Icl.Ast.Not + { + body = + Icl.Ast.MemberOf + { + target = "closed_ticket"; + binding = [ ("ticket_id", Icl.Ast.Var "ticket_id") ]; + }; + universe = "open_ticket"; + }; + } + in match Icl.Parser.of_string (Icl.Parser.to_string src) with | Error _ -> assert false | Ok parsed -> assert (parsed = src) let%test_unit "dcl: execute RegisterConstraint attaches constraint" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "order_id" "natural" in - let db = match Manipulation.Memory.create_relation storage db ~name:"order_items" ~schema with - | Error _ -> assert false | Ok (db, _) -> db - in - let stmt = Icl.Ast.RegisterConstraint { - constraint_name = "fk_order"; - relation_name = "order_items"; - body = Icl.Ast.MemberOf { target = "orders"; binding = [("order_id", Icl.Ast.Var "order_id")] } - } in - match Icl.Executor.Memory.execute storage db stmt with - | Error _ -> assert false - | Ok (db, _) -> - let rel = match Management.Database.get_relation db "order_items" with - | None -> assert false | Some r -> r + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db in - assert (rel.Relation.constraints <> None)) + let schema = Schema.empty |> Schema.add "order_id" "natural" in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"order_items" + ~schema + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let stmt = + Icl.Ast.RegisterConstraint + { + constraint_name = "fk_order"; + relation_name = "order_items"; + body = + Icl.Ast.MemberOf + { + target = "orders"; + binding = [ ("order_id", Icl.Ast.Var "order_id") ]; + }; + } + in + match Icl.Executor.Memory.execute storage db stmt with + | Error _ -> assert false + | Ok (db, _) -> + let rel = + match Management.Database.get_relation db "order_items" with + | None -> assert false + | Some r -> r + in + assert (rel.Relation.constraints <> None)) let%test_unit "dcl: FK constraint enforced on insert" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"shop" with - | Error _ -> assert false | Ok db -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"orders" - ~schema:(Schema.empty |> Schema.add "id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - (* Insert a valid order *) - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "orders"; attributes = [("id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"order_items" - ~schema:(Schema.empty |> Schema.add "order_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - (* Register FK: order_id in order_items must match id in orders *) - let db = match Icl.Executor.Memory.execute storage db - (Icl.Ast.RegisterConstraint { - constraint_name = "fk_order"; - relation_name = "order_items"; - body = Icl.Ast.MemberOf { target = "orders"; binding = [("id", Icl.Ast.Var "order_id")] } - }) with - | Error _ -> assert false | Ok (db, _) -> db - in - (* Valid insert: order_id=1 exists in orders *) - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "order_items"; attributes = [("order_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let rel = match Management.Database.get_relation db "order_items" with - | None -> assert false | Some r -> r - in - assert (Manipulation.Memory.tuple_count rel = 1); - (* Invalid insert: order_id=99 does not exist in orders *) - match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "order_items"; attributes = [("order_id", Drl.Ast.Int 99)] }) with - | Error (Dml.Executor.Memory.ManipulationError (Manipulation.ConstraintViolation _)) -> () - | _ -> assert false) + let db = + match Manipulation.Memory.create_database storage ~name:"shop" with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"orders" + ~schema:(Schema.empty |> Schema.add "id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + (* Insert a valid order *) + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { relation = "orders"; attributes = [ ("id", Drl.Ast.Int 1) ] }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"order_items" + ~schema:(Schema.empty |> Schema.add "order_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + (* Register FK: order_id in order_items must match id in orders *) + let db = + match + Icl.Executor.Memory.execute storage db + (Icl.Ast.RegisterConstraint + { + constraint_name = "fk_order"; + relation_name = "order_items"; + body = + Icl.Ast.MemberOf + { + target = "orders"; + binding = [ ("id", Icl.Ast.Var "order_id") ]; + }; + }) + with + | Error _ -> assert false + | Ok (db, _) -> db + in + (* Valid insert: order_id=1 exists in orders *) + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "order_items"; + attributes = [ ("order_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let rel = + match Management.Database.get_relation db "order_items" with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count rel = 1); + (* Invalid insert: order_id=99 does not exist in orders *) + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "order_items"; + attributes = [ ("order_id", Drl.Ast.Int 99) ]; + }) + with + | Error + (Dml.Executor.Memory.ManipulationError + (Manipulation.ConstraintViolation _)) -> + () + | _ -> assert false) -module BranchMemory = Management.Branch.Make(Management.Physical.Memory) +module BranchMemory = Management.Branch.Make (Management.Physical.Memory) let%test_unit "branch: create and get_tip" = with_storage (fun storage -> - let tip = "deadbeef" in - (match BranchMemory.create storage ~name:"main" ~tip with - | Error _ -> assert false - | Ok () -> ()); - match BranchMemory.get_tip storage "main" with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some t) -> assert (t = tip)) + let tip = "deadbeef" in + (match BranchMemory.create storage ~name:"main" ~tip with + | Error _ -> assert false + | Ok () -> ()); + match BranchMemory.get_tip storage "main" with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some t) -> assert (t = tip)) let%test_unit "branch: get_tip returns None for unknown branch" = with_storage (fun storage -> - match BranchMemory.get_tip storage "nonexistent" with - | Error _ -> assert false - | Ok None -> () - | Ok (Some _) -> assert false) + match BranchMemory.get_tip storage "nonexistent" with + | Error _ -> assert false + | Ok None -> () + | Ok (Some _) -> assert false) let%test_unit "branch: checkout and get_head" = with_storage (fun storage -> - (match BranchMemory.create storage ~name:"main" ~tip:"abc" with - | Error _ -> assert false | Ok () -> ()); - (match BranchMemory.checkout storage "main" with - | Error _ -> assert false | Ok () -> ()); - match BranchMemory.get_head storage with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some h) -> assert (h = "main")) + (match BranchMemory.create storage ~name:"main" ~tip:"abc" with + | Error _ -> assert false + | Ok () -> ()); + (match BranchMemory.checkout storage "main" with + | Error _ -> assert false + | Ok () -> ()); + match BranchMemory.get_head storage with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some h) -> assert (h = "main")) let%test_unit "branch: get_head returns None when not set" = with_storage (fun storage -> - match BranchMemory.get_head storage with - | Error _ -> assert false - | Ok None -> () - | Ok (Some _) -> assert false) + match BranchMemory.get_head storage with + | Error _ -> assert false + | Ok None -> () + | Ok (Some _) -> assert false) let%test_unit "branch: update_tip advances the branch" = with_storage (fun storage -> - (match BranchMemory.create storage ~name:"main" ~tip:"v1" with - | Error _ -> assert false | Ok () -> ()); - (match BranchMemory.update_tip storage ~name:"main" ~tip:"v2" with - | Error _ -> assert false | Ok () -> ()); - match BranchMemory.get_tip storage "main" with - | Error _ -> assert false - | Ok None -> assert false - | Ok (Some t) -> assert (t = "v2")) + (match BranchMemory.create storage ~name:"main" ~tip:"v1" with + | Error _ -> assert false + | Ok () -> ()); + (match BranchMemory.update_tip storage ~name:"main" ~tip:"v2" with + | Error _ -> assert false + | Ok () -> ()); + match BranchMemory.get_tip storage "main" with + | Error _ -> assert false + | Ok None -> assert false + | Ok (Some t) -> assert (t = "v2")) let%test_unit "branch: update_tip fails on unknown branch" = with_storage (fun storage -> - match BranchMemory.update_tip storage ~name:"ghost" ~tip:"v1" with - | Error _ -> () - | Ok () -> assert false) + match BranchMemory.update_tip storage ~name:"ghost" ~tip:"v1" with + | Error _ -> () + | Ok () -> assert false) let%test_unit "branch: multiple branches are independent" = with_storage (fun storage -> - (match BranchMemory.create storage ~name:"main" ~tip:"hash-main" with - | Error _ -> assert false | Ok () -> ()); - (match BranchMemory.create storage ~name:"feature" ~tip:"hash-feature" with - | Error _ -> assert false | Ok () -> ()); - let main_tip = match BranchMemory.get_tip storage "main" with - | Ok (Some t) -> t | _ -> assert false - in - let feat_tip = match BranchMemory.get_tip storage "feature" with - | Ok (Some t) -> t | _ -> assert false - in - assert (main_tip = "hash-main"); - assert (feat_tip = "hash-feature"); - assert (main_tip <> feat_tip)) + (match BranchMemory.create storage ~name:"main" ~tip:"hash-main" with + | Error _ -> assert false + | Ok () -> ()); + (match + BranchMemory.create storage ~name:"feature" ~tip:"hash-feature" + with + | Error _ -> assert false + | Ok () -> ()); + let main_tip = + match BranchMemory.get_tip storage "main" with + | Ok (Some t) -> t + | _ -> assert false + in + let feat_tip = + match BranchMemory.get_tip storage "feature" with + | Ok (Some t) -> t + | _ -> assert false + in + assert (main_tip = "hash-main"); + assert (feat_tip = "hash-feature"); + assert (main_tip <> feat_tip)) let%test_unit "diff: identical databases produce empty diff" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let diffs = Management.Diff.diff ~ancestor:db ~target:db in - assert (diffs = [])) + let db = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let diffs = Management.Diff.diff ~ancestor:db ~target:db in + assert (diffs = [])) let%test_unit "diff: added relation detected" = with_storage (fun storage -> - let ancestor = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let schema = Schema.empty |> Schema.add "x" "natural" in - let target, _ = match Manipulation.Memory.create_relation storage ancestor - ~name:"new_rel" ~schema with - | Error _ -> assert false | Ok p -> p - in - let diffs = Management.Diff.diff ~ancestor ~target in - let added = List.filter_map (function - | Management.Diff.RelationAdded r -> Some r - | _ -> None) diffs - in - assert (List.exists (fun r -> r.Relation.name = "new_rel") added)) + let ancestor = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let schema = Schema.empty |> Schema.add "x" "natural" in + let target, _ = + match + Manipulation.Memory.create_relation storage ancestor ~name:"new_rel" + ~schema + with + | Error _ -> assert false + | Ok p -> p + in + let diffs = Management.Diff.diff ~ancestor ~target in + let added = + List.filter_map + (function Management.Diff.RelationAdded r -> Some r | _ -> None) + diffs + in + assert (List.exists (fun r -> r.Relation.name = "new_rel") added)) let%test_unit "diff: removed relation detected" = with_storage (fun storage -> - let schema = Schema.empty |> Schema.add "x" "natural" in - let ancestor = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let ancestor, _ = match Manipulation.Memory.create_relation storage ancestor - ~name:"gone" ~schema with - | Error _ -> assert false | Ok p -> p - in - let target = match Manipulation.Memory.retract_relation storage ancestor ~name:"gone" with - | Error _ -> assert false | Ok db -> db - in - let diffs = Management.Diff.diff ~ancestor ~target in - let removed = List.filter_map (function - | Management.Diff.RelationRemoved n -> Some n - | _ -> None) diffs - in - assert (List.mem "gone" removed)) + let schema = Schema.empty |> Schema.add "x" "natural" in + let ancestor = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let ancestor, _ = + match + Manipulation.Memory.create_relation storage ancestor ~name:"gone" + ~schema + with + | Error _ -> assert false + | Ok p -> p + in + let target = + match + Manipulation.Memory.retract_relation storage ancestor ~name:"gone" + with + | Error _ -> assert false + | Ok db -> db + in + let diffs = Management.Diff.diff ~ancestor ~target in + let removed = + List.filter_map + (function Management.Diff.RelationRemoved n -> Some n | _ -> None) + diffs + in + assert (List.mem "gone" removed)) let%test_unit "diff: modified relation detected with added tuple" = with_storage (fun storage -> - let schema = Schema.empty |> Schema.add "val" "natural" in - let ancestor = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let ancestor, _ = match Manipulation.Memory.create_relation storage ancestor - ~name:"r" ~schema with - | Error _ -> assert false | Ok p -> p - in - let rel = Option.get (Management.Database.get_relation ancestor "r") in - let t : Tuple.materialized = { - Tuple.relation = "r"; - attributes = Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 42 }; - } in - let (target, _, _) = match Manipulation.Memory.create_tuple storage ancestor rel t with - | Error _ -> assert false | Ok p -> p - in - let diffs = Management.Diff.diff ~ancestor ~target in - assert (List.length diffs = 1); - match diffs with - | [ Management.Diff.RelationModified { name; added_tuples; removed_tuples; _ } ] -> - assert (name = "r"); - assert (List.length added_tuples = 1); - assert (removed_tuples = []) - | _ -> assert false) - -module MergeMemory = Management.Merge.Make - (Management.Physical.Memory) - (Manipulation.Memory) + let schema = Schema.empty |> Schema.add "val" "natural" in + let ancestor = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let ancestor, _ = + match + Manipulation.Memory.create_relation storage ancestor ~name:"r" ~schema + with + | Error _ -> assert false + | Ok p -> p + in + let rel = Option.get (Management.Database.get_relation ancestor "r") in + let t : Tuple.materialized = + { + Tuple.relation = "r"; + attributes = + Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 42 }; + } + in + let target, _, _ = + match Manipulation.Memory.create_tuple storage ancestor rel t with + | Error _ -> assert false + | Ok p -> p + in + let diffs = Management.Diff.diff ~ancestor ~target in + assert (List.length diffs = 1); + match diffs with + | [ + Management.Diff.RelationModified + { name; added_tuples; removed_tuples; _ }; + ] -> + assert (name = "r"); + assert (List.length added_tuples = 1); + assert (removed_tuples = []) + | _ -> assert false) + +module MergeMemory = + Management.Merge.Make (Management.Physical.Memory) (Manipulation.Memory) let%test_unit "merge: fast-forward when only one side changed" = with_storage (fun storage -> - let schema = Schema.empty |> Schema.add "val" "natural" in - let base_db = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let base_db, _ = match Manipulation.Memory.create_relation storage base_db - ~name:"r" ~schema with - | Error _ -> assert false | Ok p -> p - in - (match Manipulation.Memory.store_database storage base_db with - | Error _ -> assert false | Ok () -> ()); - let rel = Option.get (Management.Database.get_relation base_db "r") in - let t1 : Tuple.materialized = { - Tuple.relation = "r"; - attributes = Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 1 }; - } in - let (left_db, _, _) = match Manipulation.Memory.create_tuple storage base_db rel t1 with - | Error _ -> assert false | Ok p -> p - in - (match Manipulation.Memory.store_database storage left_db with - | Error _ -> assert false | Ok () -> ()); - match MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft - ~left_tip:left_db.Management.Database.hash - ~right_tip:base_db.Management.Database.hash with - | Error _ -> assert false - | Ok (Management.Merge.Failed _) -> assert false - | Ok (Management.Merge.Clean merged) -> - let merged_rel = Option.get (Management.Database.get_relation merged "r") in - assert (Manipulation.Memory.tuple_count merged_rel = 1)) + let schema = Schema.empty |> Schema.add "val" "natural" in + let base_db = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let base_db, _ = + match + Manipulation.Memory.create_relation storage base_db ~name:"r" ~schema + with + | Error _ -> assert false + | Ok p -> p + in + (match Manipulation.Memory.store_database storage base_db with + | Error _ -> assert false + | Ok () -> ()); + let rel = Option.get (Management.Database.get_relation base_db "r") in + let t1 : Tuple.materialized = + { + Tuple.relation = "r"; + attributes = + Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 1 }; + } + in + let left_db, _, _ = + match Manipulation.Memory.create_tuple storage base_db rel t1 with + | Error _ -> assert false + | Ok p -> p + in + (match Manipulation.Memory.store_database storage left_db with + | Error _ -> assert false + | Ok () -> ()); + match + MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft + ~left_tip:left_db.Management.Database.hash + ~right_tip:base_db.Management.Database.hash + with + | Error _ -> assert false + | Ok (Management.Merge.Failed _) -> assert false + | Ok (Management.Merge.Clean merged) -> + let merged_rel = + Option.get (Management.Database.get_relation merged "r") + in + assert (Manipulation.Memory.tuple_count merged_rel = 1)) let%test_unit "merge: independent tuple additions produce union" = with_storage (fun storage -> - let schema = Schema.empty |> Schema.add "val" "natural" in - let base_db = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - let base_db, _ = match Manipulation.Memory.create_relation storage base_db - ~name:"r" ~schema with - | Error _ -> assert false | Ok p -> p - in - (match Manipulation.Memory.store_database storage base_db with - | Error _ -> assert false | Ok () -> ()); - let rel = Option.get (Management.Database.get_relation base_db "r") in - let t_left : Tuple.materialized = { - Tuple.relation = "r"; - attributes = Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 10 }; - } in - let (left_db, _, _) = match Manipulation.Memory.create_tuple storage base_db rel t_left with - | Error _ -> assert false | Ok p -> p - in - (match Manipulation.Memory.store_database storage left_db with - | Error _ -> assert false | Ok () -> ()); - let t_right : Tuple.materialized = { - Tuple.relation = "r"; - attributes = Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 20 }; - } in - let (right_db, _, _) = match Manipulation.Memory.create_tuple storage base_db rel t_right with - | Error _ -> assert false | Ok p -> p - in - (match Manipulation.Memory.store_database storage right_db with - | Error _ -> assert false | Ok () -> ()); - match MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft - ~left_tip:left_db.Management.Database.hash - ~right_tip:right_db.Management.Database.hash with - | Error _ -> assert false - | Ok (Management.Merge.Failed _) -> assert false - | Ok (Management.Merge.Clean merged) -> - let merged_rel = Option.get (Management.Database.get_relation merged "r") in - assert (Manipulation.Memory.tuple_count merged_rel = 2)) + let schema = Schema.empty |> Schema.add "val" "natural" in + let base_db = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + let base_db, _ = + match + Manipulation.Memory.create_relation storage base_db ~name:"r" ~schema + with + | Error _ -> assert false + | Ok p -> p + in + (match Manipulation.Memory.store_database storage base_db with + | Error _ -> assert false + | Ok () -> ()); + let rel = Option.get (Management.Database.get_relation base_db "r") in + let t_left : Tuple.materialized = + { + Tuple.relation = "r"; + attributes = + Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 10 }; + } + in + let left_db, _, _ = + match Manipulation.Memory.create_tuple storage base_db rel t_left with + | Error _ -> assert false + | Ok p -> p + in + (match Manipulation.Memory.store_database storage left_db with + | Error _ -> assert false + | Ok () -> ()); + let t_right : Tuple.materialized = + { + Tuple.relation = "r"; + attributes = + Tuple.AttributeMap.singleton "val" { Attribute.value = Obj.repr 20 }; + } + in + let right_db, _, _ = + match Manipulation.Memory.create_tuple storage base_db rel t_right with + | Error _ -> assert false + | Ok p -> p + in + (match Manipulation.Memory.store_database storage right_db with + | Error _ -> assert false + | Ok () -> ()); + match + MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft + ~left_tip:left_db.Management.Database.hash + ~right_tip:right_db.Management.Database.hash + with + | Error _ -> assert false + | Ok (Management.Merge.Failed _) -> assert false + | Ok (Management.Merge.Clean merged) -> + let merged_rel = + Option.get (Management.Database.get_relation merged "r") + in + assert (Manipulation.Memory.tuple_count merged_rel = 2)) let%test_unit "merge: no-op when both sides are identical" = with_storage (fun storage -> - let base_db = match Manipulation.Memory.create_database storage ~name:"db" with - | Error _ -> assert false | Ok db -> db - in - (match Manipulation.Memory.store_database storage base_db with - | Error _ -> assert false | Ok () -> ()); - match MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft - ~left_tip:base_db.Management.Database.hash - ~right_tip:base_db.Management.Database.hash with - | Error _ -> assert false - | Ok (Management.Merge.Failed _) -> assert false - | Ok (Management.Merge.Clean merged) -> - assert (Management.Database.get_relation_names merged = - Management.Database.get_relation_names base_db)) + let base_db = + match Manipulation.Memory.create_database storage ~name:"db" with + | Error _ -> assert false + | Ok db -> db + in + (match Manipulation.Memory.store_database storage base_db with + | Error _ -> assert false + | Ok () -> ()); + match + MergeMemory.merge ~storage ~strategy:Management.Merge.PreferLeft + ~left_tip:base_db.Management.Database.hash + ~right_tip:base_db.Management.Database.hash + with + | Error _ -> assert false + | Ok (Management.Merge.Failed _) -> assert false + | Ok (Management.Merge.Clean merged) -> + assert ( + Management.Database.get_relation_names merged + = Management.Database.get_relation_names base_db)) (* Helper: look up polarity for a relation name in the result map *) -let find_polarity name pols = - Constraint.PolarityMap.find_opt name pols +let find_polarity name pols = Constraint.PolarityMap.find_opt name pols let%test_unit "polarity: MemberOf target is Positive" = - let c = Constraint.MemberOf { - target = "R"; - binding = Constraint.BindingMap.empty; - } in - assert (find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Positive) + let c = + Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty } + in + assert ( + find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Positive) let%test_unit "polarity: Not MemberOf flips to Negative" = - let c = Constraint.Not { - body = Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty }; - universe = "U"; - } in - assert (find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Negative) + let c = + Constraint.Not + { + body = + Constraint.MemberOf + { target = "R"; binding = Constraint.BindingMap.empty }; + universe = "U"; + } + in + assert ( + find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Negative) let%test_unit "polarity: Exists quantifier is Positive" = - let c = Constraint.Exists { - variable = "x"; - quantifier = "Q"; - body = Constraint.MemberOf { target = "Q"; binding = Constraint.BindingMap.empty }; - } in - assert (find_polarity "Q" (Constraint.polarity_of c) = Some Constraint.Positive) + let c = + Constraint.Exists + { + variable = "x"; + quantifier = "Q"; + body = + Constraint.MemberOf + { target = "Q"; binding = Constraint.BindingMap.empty }; + } + in + assert ( + find_polarity "Q" (Constraint.polarity_of c) = Some Constraint.Positive) let%test_unit "polarity: Forall quantifier is Negative" = - let c = Constraint.Forall { - variable = "x"; - quantifier = "Q"; - body = Constraint.MemberOf { target = "T"; binding = Constraint.BindingMap.empty }; - } in - assert (find_polarity "Q" (Constraint.polarity_of c) = Some Constraint.Negative); - assert (find_polarity "T" (Constraint.polarity_of c) = Some Constraint.Positive) + let c = + Constraint.Forall + { + variable = "x"; + quantifier = "Q"; + body = + Constraint.MemberOf + { target = "T"; binding = Constraint.BindingMap.empty }; + } + in + assert ( + find_polarity "Q" (Constraint.polarity_of c) = Some Constraint.Negative); + assert ( + find_polarity "T" (Constraint.polarity_of c) = Some Constraint.Positive) let%test_unit "polarity: same relation with both polarities merges to Both" = (* Not(MemberOf R) AND MemberOf R => R appears Negative and Positive => Both *) - let c = Constraint.And [ - Constraint.Not { - body = Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty }; - universe = "U"; - }; - Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty }; - ] in + let c = + Constraint.And + [ + Constraint.Not + { + body = + Constraint.MemberOf + { target = "R"; binding = Constraint.BindingMap.empty }; + universe = "U"; + }; + Constraint.MemberOf + { target = "R"; binding = Constraint.BindingMap.empty }; + ] + in assert (find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Both) let%test_unit "polarity: Forall body MemberOf keeps Positive" = (* Forall x in Q, MemberOf(T): T still has positive polarity *) - let c = Constraint.Forall { - variable = "x"; - quantifier = "Q"; - body = Constraint.MemberOf { target = "T"; binding = Constraint.BindingMap.empty }; - } in - assert (find_polarity "T" (Constraint.polarity_of c) = Some Constraint.Positive) + let c = + Constraint.Forall + { + variable = "x"; + quantifier = "Q"; + body = + Constraint.MemberOf + { target = "T"; binding = Constraint.BindingMap.empty }; + } + in + assert ( + find_polarity "T" (Constraint.polarity_of c) = Some Constraint.Positive) let%test_unit "polarity: nested Not double-negation restores Positive" = - let c = Constraint.Not { - body = Constraint.Not { - body = Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty }; - universe = "U"; - }; - universe = "V"; - } in - assert (find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Positive) + let c = + Constraint.Not + { + body = + Constraint.Not + { + body = + Constraint.MemberOf + { target = "R"; binding = Constraint.BindingMap.empty }; + universe = "U"; + }; + universe = "V"; + } + in + assert ( + find_polarity "R" (Constraint.polarity_of c) = Some Constraint.Positive) let%test_unit "polarity: unrelated relation absent from result" = - let c = Constraint.MemberOf { - target = "R"; - binding = Constraint.BindingMap.empty; - } in + let c = + Constraint.MemberOf { target = "R"; binding = Constraint.BindingMap.empty } + in assert (find_polarity "S" (Constraint.polarity_of c) = None) -let abs i = Obj.magic (i : int) (* lift int to AbstractValue for tests *) +let abs i = Obj.magic (i : int) (* lift int to AbstractValue for tests *) -let%test_unit "focused_filter: Var binding maps deleted value to constrained attr" = +let%test_unit + "focused_filter: Var binding maps deleted value to constrained attr" = (* Constraint: MemberOf Dept (dept_id = Var "dept_id") Deleted: Dept { dept_id = 99 } Expected filter: [("dept_id", 99)] @@ -2953,11 +3608,11 @@ let%test_unit "focused_filter: Var binding maps deleted value to constrained att Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id") in let c = Constraint.MemberOf { target = "Dept"; binding } in - let deleted = [("dept_id", abs 99)] in + let deleted = [ ("dept_id", abs 99) ] in let filter = Constraint.focused_filter c "Dept" deleted in assert (List.length filter = 1); assert (fst (List.hd filter) = "dept_id"); - assert (Stdlib.(=) (snd (List.hd filter)) (abs 99)) + assert (Stdlib.( = ) (snd (List.hd filter)) (abs 99)) let%test_unit "focused_filter: Const binding is ignored (no var link)" = (* Constraint: MemberOf Dept (code = Const "eng") @@ -2979,7 +3634,9 @@ let%test_unit "focused_filter: Const binding is ignored (no var link)" = (Constraint.Const (Obj.magic ("eng" : string))) in let c = Constraint.MemberOf { target = "Dept"; binding } in - let filter = Constraint.focused_filter c "Dept" [("code", Obj.magic ("eng" : string))] in + let filter = + Constraint.focused_filter c "Dept" [ ("code", Obj.magic ("eng" : string)) ] + in assert (filter = []) let%test_unit "focused_filter: Exists body MemberOf same relation is followed" = @@ -2998,12 +3655,15 @@ let%test_unit "focused_filter: Exists body MemberOf same relation is followed" = let binding = Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id") in - let c = Constraint.Exists { - variable = "d"; - quantifier = "Dept"; - body = Constraint.MemberOf { target = "Dept"; binding }; - } in - let filter = Constraint.focused_filter c "Dept" [("dept_id", abs 7)] in + let c = + Constraint.Exists + { + variable = "d"; + quantifier = "Dept"; + body = Constraint.MemberOf { target = "Dept"; binding }; + } + in + let filter = Constraint.focused_filter c "Dept" [ ("dept_id", abs 7) ] in assert (List.length filter = 1); assert (fst (List.hd filter) = "dept_id") @@ -3015,11 +3675,9 @@ let%test_unit "focused_filter: unrelated dep_rel yields empty filter" = the deletion cannot possibly affect any constrained tuple since no join variable links them. The cascade checker uses [] as a signal to skip the re-check for this constraint entirely, not to re-check everything. *) - let binding = - Constraint.BindingMap.singleton "x" (Constraint.Var "x") - in + let binding = Constraint.BindingMap.singleton "x" (Constraint.Var "x") in let c = Constraint.MemberOf { target = "R"; binding } in - let filter = Constraint.focused_filter c "S" [("x", abs 1)] in + let filter = Constraint.focused_filter c "S" [ ("x", abs 1) ] in assert (filter = []) let%test_unit "trigger_constants: Const value in binding is extracted" = @@ -3051,9 +3709,7 @@ let%test_unit "trigger_constants: Var binding produces no constant" = It is a join variable, not a filter. Any deleted tuple from R could match a constrained tuple via the Var link, so no early-exit is possible. [] means: always proceed to the cascade scan. *) - let binding = - Constraint.BindingMap.singleton "id" (Constraint.Var "id") - in + let binding = Constraint.BindingMap.singleton "id" (Constraint.Var "id") in let c = Constraint.MemberOf { target = "R"; binding } in let consts = Constraint.trigger_constants c "R" in assert (consts = []) @@ -3074,72 +3730,99 @@ let%test_unit "trigger_constants: unrelated dep_rel yields empty" = let consts = Constraint.trigger_constants c "S" in assert (consts = []) -let%test_unit "substitute_transition: Var \"variable.attr\" is replaced by Const from transition tuple" = +let%test_unit + "substitute_transition: Var \"variable.attr\" is replaced by Const from \ + transition tuple" = (* Constraint: Exists d in Department, MemberOf Target (key = Var "d.dept_id") Transition: Department row { dept_id = 99 } After substitution: Exists d in Department, MemberOf Target (key = Const 99) The namespaced Var "d.dept_id" (quantifier-scoped) is replaced; a base-tuple Var "dept_id" would not be. *) let c = - Constraint.Exists { - variable = "d"; quantifier = "Department"; - body = Constraint.MemberOf { - target = "Target"; - binding = Constraint.BindingMap.singleton "key" (Constraint.Var "d.dept_id"); - }; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = + Constraint.MemberOf + { + target = "Target"; + binding = + Constraint.BindingMap.singleton "key" + (Constraint.Var "d.dept_id"); + }; + } + in + let result = + Constraint.substitute_transition c "Department" [ ("dept_id", abs 99) ] in - let result = Constraint.substitute_transition c "Department" [("dept_id", abs 99)] in - (match result with - | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> - (match Constraint.BindingMap.find_opt "key" binding with - | Some (Constraint.Const v) -> assert (Stdlib.(=) v (abs 99)) + match result with + | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> ( + match Constraint.BindingMap.find_opt "key" binding with + | Some (Constraint.Const v) -> assert (Stdlib.( = ) v (abs 99)) | _ -> assert false) - | _ -> assert false) + | _ -> assert false let%test_unit "substitute_transition: base-tuple Var is not substituted" = (* Constraint: Exists d in Department, MemberOf Target (key = Var "dept_id") Var "dept_id" is a base-tuple reference (no "d." prefix), so it must survive substitution unchanged. Only "d.dept_id" would be replaced. *) let c = - Constraint.Exists { - variable = "d"; quantifier = "Department"; - body = Constraint.MemberOf { - target = "Target"; - binding = Constraint.BindingMap.singleton "key" (Constraint.Var "dept_id"); - }; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = + Constraint.MemberOf + { + target = "Target"; + binding = + Constraint.BindingMap.singleton "key" (Constraint.Var "dept_id"); + }; + } in - let result = Constraint.substitute_transition c "Department" [("dept_id", abs 99)] in - (match result with - | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> - (match Constraint.BindingMap.find_opt "key" binding with + let result = + Constraint.substitute_transition c "Department" [ ("dept_id", abs 99) ] + in + match result with + | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> ( + match Constraint.BindingMap.find_opt "key" binding with | Some (Constraint.Var "dept_id") -> () | _ -> assert false) - | _ -> assert false) + | _ -> assert false -let%test_unit "substitute_transition: non-matching quantifier relation is unchanged" = +let%test_unit + "substitute_transition: non-matching quantifier relation is unchanged" = (* Constraint: Exists d in Department, MemberOf Target (key = Var "d.dept_id") Transition is for relation "Other", not "Department". The body must survive unmodified. *) let c = - Constraint.Exists { - variable = "d"; quantifier = "Department"; - body = Constraint.MemberOf { - target = "Target"; - binding = Constraint.BindingMap.singleton "key" (Constraint.Var "d.dept_id"); - }; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = + Constraint.MemberOf + { + target = "Target"; + binding = + Constraint.BindingMap.singleton "key" + (Constraint.Var "d.dept_id"); + }; + } + in + let result = + Constraint.substitute_transition c "Other" [ ("dept_id", abs 99) ] in - let result = Constraint.substitute_transition c "Other" [("dept_id", abs 99)] in - (match result with - | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> - (match Constraint.BindingMap.find_opt "key" binding with + match result with + | Constraint.Exists { body = Constraint.MemberOf { binding; _ }; _ } -> ( + match Constraint.BindingMap.find_opt "key" binding with | Some (Constraint.Var "d.dept_id") -> () | _ -> assert false) - | _ -> assert false) + | _ -> assert false -let%test_unit "substitute_transition: substitution applies through And and Not" = +let%test_unit "substitute_transition: substitution applies through And and Not" + = (* Constraint: Exists d in Department, And [ @@ -3149,75 +3832,112 @@ let%test_unit "substitute_transition: substitution applies through And and Not" Transition: { x = 1, y = 2 } After substitution: Var "d.x" -> Const 1, Var "d.y" -> Const 2 *) let c = - Constraint.Exists { - variable = "d"; quantifier = "Department"; - body = Constraint.And [ - Constraint.MemberOf { - target = "A"; - binding = Constraint.BindingMap.singleton "x" (Constraint.Var "d.x"); - }; - Constraint.Not { - universe = "B"; - body = Constraint.MemberOf { - target = "B"; - binding = Constraint.BindingMap.singleton "y" (Constraint.Var "d.y"); - }; - }; - ]; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = + Constraint.And + [ + Constraint.MemberOf + { + target = "A"; + binding = + Constraint.BindingMap.singleton "x" (Constraint.Var "d.x"); + }; + Constraint.Not + { + universe = "B"; + body = + Constraint.MemberOf + { + target = "B"; + binding = + Constraint.BindingMap.singleton "y" + (Constraint.Var "d.y"); + }; + }; + ]; + } + in + let result = + Constraint.substitute_transition c "Department" + [ ("x", abs 1); ("y", abs 2) ] in - let result = Constraint.substitute_transition c "Department" [("x", abs 1); ("y", abs 2)] in - (match result with - | Constraint.Exists { - body = Constraint.And [ - Constraint.MemberOf { binding = b1; _ }; - Constraint.Not { body = Constraint.MemberOf { binding = b2; _ }; _ }; - ]; _ - } -> - (match Constraint.BindingMap.find_opt "x" b1, - Constraint.BindingMap.find_opt "y" b2 with + match result with + | Constraint.Exists + { + body = + Constraint.And + [ + Constraint.MemberOf { binding = b1; _ }; + Constraint.Not + { body = Constraint.MemberOf { binding = b2; _ }; _ }; + ]; + _; + } -> ( + match + ( Constraint.BindingMap.find_opt "x" b1, + Constraint.BindingMap.find_opt "y" b2 ) + with | Some (Constraint.Const v1), Some (Constraint.Const v2) -> - assert (Stdlib.(=) v1 (abs 1)); - assert (Stdlib.(=) v2 (abs 2)) + assert (Stdlib.( = ) v1 (abs 1)); + assert (Stdlib.( = ) v2 (abs 2)) | _ -> assert false) - | _ -> assert false) + | _ -> assert false (* Build a minimal db with two relations and an FK constraint: Department { dept_id } <-- Employee { emp_id, dept_id } Constraint on Employee: Exists d in Department, MemberOf Department (dept_id = Var "dept_id") *) let setup_fk_db storage = - let db = match Manipulation.Memory.create_database storage ~name:"hr" with - | Error _ -> assert false | Ok db -> db + let db = + match Manipulation.Memory.create_database storage ~name:"hr" with + | Error _ -> assert false + | Ok db -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Department" - ~schema:(Schema.empty |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Department" + ~schema:(Schema.empty |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Employee" - ~schema:(Schema.empty - |> Schema.add "emp_id" "natural" - |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Employee" + ~schema: + (Schema.empty + |> Schema.add "emp_id" "natural" + |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in let fk_body = let binding = Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id") in - Constraint.Exists { - variable = "d"; - quantifier = "Department"; - body = Constraint.MemberOf { target = "Department"; binding }; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = Constraint.MemberOf { target = "Department"; binding }; + } in - let db = match Manipulation.Memory.register_constraint storage db - ~constraint_name:"fk_dept" - ~relation_name:"Employee" - ~body:fk_body with - | Error _ -> assert false | Ok db -> db + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"fk_dept" ~relation_name:"Employee" ~body:fk_body + with + | Error _ -> assert false + | Ok db -> db in db -let%test_unit "extend_tuple namespacing: insert Employee with non-existent dept_id is rejected" = +let%test_unit + "extend_tuple namespacing: insert Employee with non-existent dept_id is \ + rejected" = (* Regression test for the extend_tuple namespacing bug. Without namespacing, extend_tuple would overwrite the Employee's dept_id=99 with the Department row's dept_id=1 during Exists iteration. Var "dept_id" @@ -3226,202 +3946,346 @@ let%test_unit "extend_tuple namespacing: insert Employee with non-existent dept_ With namespacing, Department attributes are stored as d.dept_id=1 and the base Employee attribute dept_id=99 is preserved, so the check correctly fails. *) with_storage (fun storage -> - let db = setup_fk_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 1); - ("dept_id", Drl.Ast.Int 99)] }) with - | Ok _ -> assert false (* dept_id=99 does not exist in Department *) - | Error _ -> ()) + let db = setup_fk_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 1); ("dept_id", Drl.Ast.Int 99) ]; + }) + with + | Ok _ -> assert false (* dept_id=99 does not exist in Department *) + | Error _ -> ()) let%test_unit "cascade: delete referenced row violates FK and is rejected" = with_storage (fun storage -> - let db = setup_fk_db storage in - (* Insert Dept 1 and Employee referencing it *) - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let dept_rel = match Manipulation.Memory.get_relation db ~name:"Department" with - | None -> assert false | Some r -> r - in - let dept_hash = Hashing.hash_tuple - { Tuple.relation = "Department"; - attributes = Tuple.AttributeMap.singleton "dept_id" - { Attribute.value = Obj.magic (1 : int) } } - in - (* Deleting Dept 1 while Employee references it must fail *) - match Manipulation.Memory.retract_tuple storage db dept_rel ~tuple_hash:dept_hash with - | Ok _ -> assert false (* should have been rejected *) - | Error (Manipulation.ConstraintViolation msg) -> - assert (String.length msg > 0) - | Error _ -> assert false) + let db = setup_fk_db storage in + (* Insert Dept 1 and Employee referencing it *) + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let dept_rel = + match Manipulation.Memory.get_relation db ~name:"Department" with + | None -> assert false + | Some r -> r + in + let dept_hash = + Hashing.hash_tuple + { + Tuple.relation = "Department"; + attributes = + Tuple.AttributeMap.singleton "dept_id" + { Attribute.value = Obj.magic (1 : int) }; + } + in + (* Deleting Dept 1 while Employee references it must fail *) + match + Manipulation.Memory.retract_tuple storage db dept_rel + ~tuple_hash:dept_hash + with + | Ok _ -> assert false (* should have been rejected *) + | Error (Manipulation.ConstraintViolation msg) -> + assert (String.length msg > 0) + | Error _ -> assert false) let%test_unit "cascade: delete unreferenced row succeeds" = with_storage (fun storage -> - let db = setup_fk_db storage in - (* Insert two departments; employee only references dept 1 *) - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 2)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let dept_rel = match Manipulation.Memory.get_relation db ~name:"Department" with - | None -> assert false | Some r -> r - in - let dept2_hash = Hashing.hash_tuple - { Tuple.relation = "Department"; - attributes = Tuple.AttributeMap.singleton "dept_id" - { Attribute.value = Obj.magic (2 : int) } } - in - (* Deleting unreferenced Dept 2 must succeed *) - match Manipulation.Memory.retract_tuple storage db dept_rel ~tuple_hash:dept2_hash with - | Error _ -> assert false - | Ok (new_db, _) -> - let dept = match Manipulation.Memory.get_relation new_db ~name:"Department" with - | None -> assert false | Some r -> r + let db = setup_fk_db storage in + (* Insert two departments; employee only references dept 1 *) + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 2) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db in - assert (Manipulation.Memory.tuple_count dept = 1)) + let dept_rel = + match Manipulation.Memory.get_relation db ~name:"Department" with + | None -> assert false + | Some r -> r + in + let dept2_hash = + Hashing.hash_tuple + { + Tuple.relation = "Department"; + attributes = + Tuple.AttributeMap.singleton "dept_id" + { Attribute.value = Obj.magic (2 : int) }; + } + in + (* Deleting unreferenced Dept 2 must succeed *) + match + Manipulation.Memory.retract_tuple storage db dept_rel + ~tuple_hash:dept2_hash + with + | Error _ -> assert false + | Ok (new_db, _) -> + let dept = + match + Manipulation.Memory.get_relation new_db ~name:"Department" + with + | None -> assert false + | Some r -> r + in + assert (Manipulation.Memory.tuple_count dept = 1)) let%test_unit "cascade: Negative-polarity relation deletion is not checked" = (* Constraint on Employee: NOT (MemberOf Blacklist (emp_id = Var "emp_id")) Blacklist has Negative polarity (INSERT into Blacklist could violate). DELETE from Blacklist should never trigger a cascade check. *) with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"hr" with - | Error _ -> assert false | Ok db -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"Blacklist" - ~schema:(Schema.empty |> Schema.add "emp_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"Employee" - ~schema:(Schema.empty |> Schema.add "emp_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - let not_body = - let binding = - Constraint.BindingMap.singleton "emp_id" (Constraint.Var "emp_id") + let db = + match Manipulation.Memory.create_database storage ~name:"hr" with + | Error _ -> assert false + | Ok db -> db in - Constraint.Not { - body = Constraint.MemberOf { target = "Blacklist"; binding }; - universe = "Employee"; - } - in - let db = match Manipulation.Memory.register_constraint storage db - ~constraint_name:"not_blacklisted" - ~relation_name:"Employee" - ~body:not_body with - | Error _ -> assert false | Ok db -> db - in - (* Insert emp_id=5 into Blacklist, then insert emp_id=99 into Employee (unrelated) *) - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Blacklist"; - attributes = [("emp_id", Drl.Ast.Int 5)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 99)] }) with - | Error _ -> assert false | Ok db -> db - in - let bl_rel = match Manipulation.Memory.get_relation db ~name:"Blacklist" with - | None -> assert false | Some r -> r - in - let bl_hash = Hashing.hash_tuple - { Tuple.relation = "Blacklist"; - attributes = Tuple.AttributeMap.singleton "emp_id" - { Attribute.value = Obj.magic (5 : int) } } - in - (* Deleting from Blacklist (negative polarity) must not trigger cascade check *) - match Manipulation.Memory.retract_tuple storage db bl_rel ~tuple_hash:bl_hash with - | Error _ -> assert false - | Ok _ -> ()) + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Blacklist" + ~schema:(Schema.empty |> Schema.add "emp_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Employee" + ~schema:(Schema.empty |> Schema.add "emp_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let not_body = + let binding = + Constraint.BindingMap.singleton "emp_id" (Constraint.Var "emp_id") + in + Constraint.Not + { + body = Constraint.MemberOf { target = "Blacklist"; binding }; + universe = "Employee"; + } + in + let db = + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"not_blacklisted" ~relation_name:"Employee" + ~body:not_body + with + | Error _ -> assert false + | Ok db -> db + in + (* Insert emp_id=5 into Blacklist, then insert emp_id=99 into Employee (unrelated) *) + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Blacklist"; + attributes = [ ("emp_id", Drl.Ast.Int 5) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = [ ("emp_id", Drl.Ast.Int 99) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let bl_rel = + match Manipulation.Memory.get_relation db ~name:"Blacklist" with + | None -> assert false + | Some r -> r + in + let bl_hash = + Hashing.hash_tuple + { + Tuple.relation = "Blacklist"; + attributes = + Tuple.AttributeMap.singleton "emp_id" + { Attribute.value = Obj.magic (5 : int) }; + } + in + (* Deleting from Blacklist (negative polarity) must not trigger cascade check *) + match + Manipulation.Memory.retract_tuple storage db bl_rel ~tuple_hash:bl_hash + with + | Error _ -> assert false + | Ok _ -> ()) let%test_unit "cascade: deferred constraint not checked during retract_tuple" = with_storage (fun storage -> - let db = match Manipulation.Memory.create_database storage ~name:"hr" with - | Error _ -> assert false | Ok db -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"Department" - ~schema:(Schema.empty |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - let db = match Manipulation.Memory.create_relation storage db ~name:"Employee" - ~schema:(Schema.empty - |> Schema.add "emp_id" "natural" - |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db - in - let fk_body = - let binding = - Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id") + let db = + match Manipulation.Memory.create_database storage ~name:"hr" with + | Error _ -> assert false + | Ok db -> db in - Constraint.Exists { - variable = "d"; - quantifier = "Department"; - body = Constraint.MemberOf { target = "Department"; binding }; - } - in - (* Register as DEFERRED *) - let db = match Manipulation.Memory.attach_constraint storage db - ~constraint_name:"fk_dept_deferred" - ~relation_name:"Employee" - ~body:fk_body - ~timing:Constraint.Deferred with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let dept_rel = match Manipulation.Memory.get_relation db ~name:"Department" with - | None -> assert false | Some r -> r - in - let dept_hash = Hashing.hash_tuple - { Tuple.relation = "Department"; - attributes = Tuple.AttributeMap.singleton "dept_id" - { Attribute.value = Obj.magic (1 : int) } } - in - (* Deferred: retract_tuple itself should NOT reject the deletion *) - match Manipulation.Memory.retract_tuple storage db dept_rel ~tuple_hash:dept_hash with - | Error _ -> assert false (* deferred: must pass here *) - | Ok (new_db, _) -> - (* But check_deferred_constraints must catch the violation *) - match Manipulation.Memory.check_deferred_constraints storage new_db with - | Ok () -> assert false (* should have caught the violation *) - | Error (Manipulation.ConstraintViolation _) -> () - | Error _ -> assert false) + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Department" + ~schema:(Schema.empty |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Employee" + ~schema: + (Schema.empty + |> Schema.add "emp_id" "natural" + |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db + in + let fk_body = + let binding = + Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id") + in + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = Constraint.MemberOf { target = "Department"; binding }; + } + in + (* Register as DEFERRED *) + let db = + match + Manipulation.Memory.attach_constraint storage db + ~constraint_name:"fk_dept_deferred" ~relation_name:"Employee" + ~body:fk_body ~timing:Constraint.Deferred + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let dept_rel = + match Manipulation.Memory.get_relation db ~name:"Department" with + | None -> assert false + | Some r -> r + in + let dept_hash = + Hashing.hash_tuple + { + Tuple.relation = "Department"; + attributes = + Tuple.AttributeMap.singleton "dept_id" + { Attribute.value = Obj.magic (1 : int) }; + } + in + (* Deferred: retract_tuple itself should NOT reject the deletion *) + match + Manipulation.Memory.retract_tuple storage db dept_rel + ~tuple_hash:dept_hash + with + | Error _ -> assert false (* deferred: must pass here *) + | Ok (new_db, _) -> ( + (* But check_deferred_constraints must catch the violation *) + match + Manipulation.Memory.check_deferred_constraints storage new_db + with + | Ok () -> assert false (* should have caught the violation *) + | Error (Manipulation.ConstraintViolation _) -> () + | Error _ -> assert false)) (* INSERT cascade tests. @@ -3435,64 +4299,109 @@ let%test_unit "cascade: deferred constraint not checked during retract_tuple" = Blacklist has Negative polarity in this constraint, so inserting into Blacklist must trigger a re-check of Employee tuples. *) let setup_blacklist_db storage = - let db = match Manipulation.Memory.create_database storage ~name:"hr" with - | Error _ -> assert false | Ok db -> db + let db = + match Manipulation.Memory.create_database storage ~name:"hr" with + | Error _ -> assert false + | Ok db -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Blacklist" - ~schema:(Schema.empty |> Schema.add "emp_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Blacklist" + ~schema:(Schema.empty |> Schema.add "emp_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Employee" - ~schema:(Schema.empty |> Schema.add "emp_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Employee" + ~schema:(Schema.empty |> Schema.add "emp_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in let not_body = - Constraint.Not { - body = Constraint.MemberOf { - target = "Blacklist"; - binding = Constraint.BindingMap.singleton "emp_id" (Constraint.Var "emp_id"); - }; - universe = "Employee"; - } + Constraint.Not + { + body = + Constraint.MemberOf + { + target = "Blacklist"; + binding = + Constraint.BindingMap.singleton "emp_id" + (Constraint.Var "emp_id"); + }; + universe = "Employee"; + } in - match Manipulation.Memory.register_constraint storage db - ~constraint_name:"not_blacklisted" - ~relation_name:"Employee" - ~body:not_body with - | Error _ -> assert false | Ok db -> db + match + Manipulation.Memory.register_constraint storage db + ~constraint_name:"not_blacklisted" ~relation_name:"Employee" + ~body:not_body + with + | Error _ -> assert false + | Ok db -> db -let%test_unit "cascade INSERT: inserting into Blacklist violates constraint on existing Employee" = +let%test_unit + "cascade INSERT: inserting into Blacklist violates constraint on existing \ + Employee" = (* Employee emp_id=5 already exists. Inserting Blacklist{emp_id=5} must be rejected because the Employee constraint (NOT member of Blacklist) would be violated for the existing emp_id=5 row. *) with_storage (fun storage -> - let db = setup_blacklist_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 5)] }) with - | Error _ -> assert false | Ok db -> db - in - match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Blacklist"; - attributes = [("emp_id", Drl.Ast.Int 5)] }) with - | Ok _ -> assert false (* emp_id=5 exists in Employee, must be rejected *) - | Error _ -> ()) + let db = setup_blacklist_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = [ ("emp_id", Drl.Ast.Int 5) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Blacklist"; + attributes = [ ("emp_id", Drl.Ast.Int 5) ]; + }) + with + | Ok _ -> assert false (* emp_id=5 exists in Employee, must be rejected *) + | Error _ -> ()) -let%test_unit "cascade INSERT: inserting into Blacklist with no matching Employee succeeds" = +let%test_unit + "cascade INSERT: inserting into Blacklist with no matching Employee \ + succeeds" = (* Employee emp_id=99 does not exist. Inserting Blacklist{emp_id=5} must succeed because no Employee tuple is affected. *) with_storage (fun storage -> - let db = setup_blacklist_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 99)] }) with - | Error _ -> assert false | Ok db -> db - in - match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Blacklist"; - attributes = [("emp_id", Drl.Ast.Int 5)] }) with - | Error _ -> assert false (* emp_id=5 not in Employee, must succeed *) - | Ok _ -> ()) + let db = setup_blacklist_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = [ ("emp_id", Drl.Ast.Int 99) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Blacklist"; + attributes = [ ("emp_id", Drl.Ast.Int 5) ]; + }) + with + | Error _ -> assert false (* emp_id=5 not in Employee, must succeed *) + | Ok _ -> ()) (* commit tests. @@ -3500,108 +4409,179 @@ let%test_unit "cascade INSERT: inserting into Blacklist with no matching Employe Individual mutations succeed even when they temporarily violate the constraint; commit is the boundary where deferred violations are caught. *) let setup_deferred_fk_db storage = - let db = match Manipulation.Memory.create_database storage ~name:"hr" with - | Error _ -> assert false | Ok db -> db + let db = + match Manipulation.Memory.create_database storage ~name:"hr" with + | Error _ -> assert false + | Ok db -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Department" - ~schema:(Schema.empty |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Department" + ~schema:(Schema.empty |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in - let db = match Manipulation.Memory.create_relation storage db ~name:"Employee" - ~schema:(Schema.empty - |> Schema.add "emp_id" "natural" - |> Schema.add "dept_id" "natural") with - | Error _ -> assert false | Ok (db, _) -> db + let db = + match + Manipulation.Memory.create_relation storage db ~name:"Employee" + ~schema: + (Schema.empty + |> Schema.add "emp_id" "natural" + |> Schema.add "dept_id" "natural") + with + | Error _ -> assert false + | Ok (db, _) -> db in let fk_body = - Constraint.Exists { - variable = "d"; quantifier = "Department"; - body = Constraint.MemberOf { - target = "Department"; - binding = Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id"); - }; - } + Constraint.Exists + { + variable = "d"; + quantifier = "Department"; + body = + Constraint.MemberOf + { + target = "Department"; + binding = + Constraint.BindingMap.singleton "dept_id" + (Constraint.Var "dept_id"); + }; + } in - match Manipulation.Memory.attach_constraint storage db - ~constraint_name:"fk_dept" - ~relation_name:"Employee" - ~body:fk_body - ~timing:Constraint.Deferred with - | Error _ -> assert false | Ok db -> db + match + Manipulation.Memory.attach_constraint storage db ~constraint_name:"fk_dept" + ~relation_name:"Employee" ~body:fk_body ~timing:Constraint.Deferred + with + | Error _ -> assert false + | Ok db -> db let%test_unit "commit: no deferred constraints, returns Ok with db unchanged" = with_storage (fun storage -> - let db = setup_deferred_fk_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - (* All constraints satisfied, commit must pass *) - match Manipulation.Memory.commit storage db with - | Error _ -> assert false - | Ok _ -> ()) + let db = setup_deferred_fk_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + (* All constraints satisfied, commit must pass *) + match Manipulation.Memory.commit storage db with + | Error _ -> assert false + | Ok _ -> ()) let%test_unit "commit: deferred violation is caught at commit boundary" = with_storage (fun storage -> - let db = setup_deferred_fk_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let dept_rel = match Manipulation.Memory.get_relation db ~name:"Department" with - | None -> assert false | Some r -> r - in - let dept_hash = Hashing.hash_tuple - { Tuple.relation = "Department"; - attributes = Tuple.AttributeMap.singleton "dept_id" - { Attribute.value = Obj.magic (1 : int) } } - in - (* Delete the referenced department. Deferred, so retract succeeds *) - let db = match Manipulation.Memory.retract_tuple storage db dept_rel - ~tuple_hash:dept_hash with - | Error _ -> assert false | Ok (db, _) -> db - in - (* commit must now catch the orphaned Employee *) - match Manipulation.Memory.commit storage db with - | Ok _ -> assert false - | Error (Manipulation.ConstraintViolation _) -> () - | Error _ -> assert false) + let db = setup_deferred_fk_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let dept_rel = + match Manipulation.Memory.get_relation db ~name:"Department" with + | None -> assert false + | Some r -> r + in + let dept_hash = + Hashing.hash_tuple + { + Tuple.relation = "Department"; + attributes = + Tuple.AttributeMap.singleton "dept_id" + { Attribute.value = Obj.magic (1 : int) }; + } + in + (* Delete the referenced department. Deferred, so retract succeeds *) + let db = + match + Manipulation.Memory.retract_tuple storage db dept_rel + ~tuple_hash:dept_hash + with + | Error _ -> assert false + | Ok (db, _) -> db + in + (* commit must now catch the orphaned Employee *) + match Manipulation.Memory.commit storage db with + | Ok _ -> assert false + | Error (Manipulation.ConstraintViolation _) -> () + | Error _ -> assert false) let%test_unit "commit: clears deferred list, second commit passes" = (* After a successful commit the deferred list is empty. A follow-up mutation + commit on the returned db must also work correctly rather than re-running stale entries from the previous window. *) with_storage (fun storage -> - let db = setup_deferred_fk_db storage in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Department"; - attributes = [("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Dml.Executor.Memory.execute storage db - (Dml.Ast.InsertTuple { relation = "Employee"; - attributes = [("emp_id", Drl.Ast.Int 10); - ("dept_id", Drl.Ast.Int 1)] }) with - | Error _ -> assert false | Ok db -> db - in - let db = match Manipulation.Memory.commit storage db with - | Error _ -> assert false | Ok db -> db - in - (* Second commit with no new mutations must also pass *) - match Manipulation.Memory.commit storage db with - | Error _ -> assert false - | Ok _ -> ()) + let db = setup_deferred_fk_db storage in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Department"; + attributes = [ ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match + Dml.Executor.Memory.execute storage db + (Dml.Ast.InsertTuple + { + relation = "Employee"; + attributes = + [ ("emp_id", Drl.Ast.Int 10); ("dept_id", Drl.Ast.Int 1) ]; + }) + with + | Error _ -> assert false + | Ok db -> db + in + let db = + match Manipulation.Memory.commit storage db with + | Error _ -> assert false + | Ok db -> db + in + (* Second commit with no new mutations must also pass *) + match Manipulation.Memory.commit storage db with + | Error _ -> assert false + | Ok _ -> ()) |
