aboutsummaryrefslogblamecommitdiffstats
path: root/bin/tree.ml
blob: 66f5e1cd4bbdd4cff1b1a9d2b9f16075d541b7ec (plain) (tree)




































































































































































                                                                               
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 ()