open Relational_engine (* Content-addressed store: hash -> bytes. In production this is NT storage. *) let store : (string, bytes) Hashtbl.t = Hashtbl.create 64 (* --- Object construction helpers --- *) let make_tuple ~relation attributes = let attr_map = List.fold_left (fun acc (name, value) -> Tuple.AttributeMap.add name { Attribute.value = Obj.repr (value : string) } acc) Tuple.AttributeMap.empty attributes in { Tuple.relation; attributes = attr_map } (* Simulate B+ tree root: hash of sorted tuple hashes. *) let tuple_tree_pointer tuples = List.map Hashing.hash_tuple tuples |> List.sort String.compare |> List.fold_left ( ^ ) "" |> Conventions.Hash.hash_text let make_relation ~name ~schema ~cardinality tuples = (new Relation.stored ~name ~schema ~constraints:None ~cardinality:(Conventions.Cardinality.Finite cardinality) ~membership_criteria:(fun _ -> true) ~lineage:None ~provenance:None) #set_tree_pointer (Some (tuple_tree_pointer tuples)) (* --- Persistence layer (writing into the store) --- *) let persist_tuple tuple = let hash = Hashing.hash_tuple tuple in Hashtbl.add store hash (Marshal.to_bytes tuple [ Marshal.Closures ]); hash (* Store an ordered list of child hashes at a tree node address. *) let persist_tree_node node_hash (child_hashes : string list) = Hashtbl.add store node_hash (Marshal.to_bytes child_hashes []) (* Store a relation's metadata (name, schema, tree_pointer) at its hash. *) let persist_relation (rel : Relation.stored) = Hashtbl.add store rel#hash (Marshal.to_bytes (rel#name, rel#schema, rel#tree_pointer) []) (* --- Reconstruction layer (reading from the store) --- *) let load : 'a. string -> 'a = fun hash -> Marshal.from_bytes (Hashtbl.find store hash) 0 let reconstruct_from_tree_pointer mg_tp = let rel_hashes : string list = load mg_tp in List.map (fun rel_hash -> let (name, schema, tp_opt) : string * Schema.t * string option = load rel_hash in let tuples : Tuple.materialized list = match tp_opt with | None -> [] | Some tp -> let tuple_hashes : string list = load tp in List.map load tuple_hashes in (name, schema, tuples) ) rel_hashes (* --- Printing --- *) let print_tuple (t : Tuple.materialized) = let fields = Tuple.AttributeMap.bindings t.attributes |> List.map (fun (k, attr) -> k ^ " = " ^ (Obj.magic attr.Attribute.value : string)) |> String.concat ", " in Printf.printf " { %s }\n" fields let print_reconstructed relations = List.iter (fun (name, schema, tuples) -> Printf.printf " relation: %s [%s]\n" name (String.concat ", " (List.map (fun (a, d) -> a ^ ":" ^ d) schema)); List.iter print_tuple tuples ) relations (* --- Main --- *) let boot () = (* Tuples *) let dept_engineering = make_tuple ~relation:"departments" [ ("id", "1"); ("name", "Engineering") ] in let dept_sales = make_tuple ~relation:"departments" [ ("id", "2"); ("name", "Sales") ] in let emp_alice = make_tuple ~relation:"employees" [ ("dept_id", "1"); ("id", "1"); ("name", "Alice") ] in let emp_bob = make_tuple ~relation:"employees" [ ("dept_id", "2"); ("id", "2"); ("name", "Bob") ] in (* Persist tuples and capture their hashes *) let dept_eng_hash = persist_tuple dept_engineering in let dept_sales_hash = persist_tuple dept_sales in let emp_alice_hash = persist_tuple emp_alice in let emp_bob_hash = persist_tuple emp_bob in (* Build relations with tree_pointers derived from tuple hashes *) let departments = make_relation ~name:"departments" ~schema:[ ("id", "int"); ("name", "string") ] ~cardinality:2 [ dept_engineering; dept_sales ] in let employees = make_relation ~name:"employees" ~schema:[ ("id", "int"); ("name", "string"); ("dept_id", "int") ] ~cardinality:2 [ emp_alice; emp_bob ] in (* Persist each relation's B+ tree node (tuple hashes) at its tree_pointer *) let dept_tp = Option.get departments#tree_pointer in let emp_tp = Option.get employees#tree_pointer in persist_tree_node dept_tp (List.sort String.compare [ dept_eng_hash; dept_sales_hash ]); persist_tree_node emp_tp (List.sort String.compare [ emp_alice_hash; emp_bob_hash ]); (* Persist relation metadata at each relation hash *) persist_relation departments; persist_relation employees; (* Build multigroup *) let company = List.fold_left (fun mg rel -> mg#add_relation rel) (new Management.Multigroup.multigroup ~name:"company") [ (departments :> Relation.relation); (employees :> Relation.relation) ] in (* Persist multigroup's B+ tree node (relation hashes) at its tree_pointer *) let mg_tp = Option.get company#tree_pointer in persist_tree_node mg_tp (List.sort String.compare [ departments#hash; employees#hash ]); Printf.printf "=== Built state ===\n"; Printf.printf " multigroup hash: %s\n" company#hash; Printf.printf " tree_pointer: %s\n\n" mg_tp; (* Drop everything except the tree_pointer and reconstruct *) Printf.printf "=== Reconstructing from tree_pointer only ===\n"; let relations = reconstruct_from_tree_pointer mg_tp in print_reconstructed relations let () = boot ()