aboutsummaryrefslogtreecommitdiffstats
path: root/lib/manipulation.ml
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-04-20 21:55:07 -0300
committerGitHub <noreply@github.com>2026-04-20 21:55:07 -0300
commitfdc63e730f91a4605ed4ff96a908e1f5e822d1bb (patch)
tree36b0a66b81fc201a426171e190975289640cd579 /lib/manipulation.ml
parentd3708bb28adcd0220142f1890ab96bfd2df637d3 (diff)
downloadSakura-fdc63e730f91a4605ed4ff96a908e1f5e822d1bb.tar.gz
Sakura-fdc63e730f91a4605ed4ff96a908e1f5e822d1bb.zip
New Server & Manipulation refactor
Co-authored-by: Estevan Castilho <tevo@riajuu.net>
Diffstat (limited to '')
-rw-r--r--lib/manipulation.ml388
-rw-r--r--lib/manipulation.mli137
2 files changed, 187 insertions, 338 deletions
diff --git a/lib/manipulation.ml b/lib/manipulation.ml
index b44cc53..91cd35d 100644
--- a/lib/manipulation.ml
+++ b/lib/manipulation.ml
@@ -1,6 +1,6 @@
(** Database manipulation operations.
- This module implements the core CRUD operations for the relational engine:
+ This module implements the core operations for the relational engine:
- Database creation
- Relation creation and deletion
- Tuple insertion and deletion
@@ -14,68 +14,69 @@
module RelationMap = Management.Database.RelationMap
-(** Error types for manipulation operations *)
-type error =
- | RelationNotFound of string
- | RelationAlreadyExists of string
- | TupleNotFound of Conventions.Hash.t
- | DuplicateTuple of Conventions.Hash.t
- | ConstraintViolation of string
- | StorageError of string
-
-let string_of_error = function
- | RelationNotFound s -> "RelationNotFound: " ^ s
- | RelationAlreadyExists s -> "RelationAlreadyExists: " ^ s
- | TupleNotFound h -> "TupleNotFound: " ^ h
- | DuplicateTuple h -> "DuplicateTuple: " ^ h
- | ConstraintViolation s -> "ConstraintViolation: " ^ s
- | StorageError s -> "StorageError: " ^ s
-
-let sexp_of_error e =
- let open Sexplib.Sexp in
- match e with
- | RelationNotFound s -> List [ Atom "relation-not-found"; Atom s ]
- | RelationAlreadyExists s -> List [ Atom "relation-already-exists"; Atom s ]
- | TupleNotFound h -> List [ Atom "tuple-not-found"; Atom h ]
- | DuplicateTuple h -> List [ Atom "duplicate-tuple"; Atom h ]
- | ConstraintViolation s -> List [ Atom "constraint-violation"; Atom s ]
- | StorageError s -> List [ Atom "storage-error"; Atom s ]
-
-type 'a result = ('a, error) Result.t
-(** Result type for operations *)
-
-module Schema = Schema
-(** Re-export Schema for convenience *)
-
-(** Re-export hashing functions for convenience *)
-let hash_tuple = Hashing.hash_tuple
-
-(** Build base provenance for a schema *)
-let build_base_provenance _schema relation_name =
- Relation.Provenance.Base relation_name
-
-(** Build membership criteria from schema (validates type membership) *)
-let build_membership_criteria _schema : Tuple.t -> bool =
- (* For now, accept all tuples. Full implementation would check domain membership. *)
- fun _ ->
- true
-
-(** Mutation direction for cascade constraint checking *)
-type mutation_kind = Insert | Delete
-
-type mutation_context = {
- dep_rel : string;
- transition : (string * Conventions.AbstractValue.t) list;
- kind : mutation_kind;
-}
-(** Context for a single tuple mutation, used by cascade checking *)
+(** Build membership criteria from schema (validates type membership).
+ Returns a function that takes a tree-lookup function and a tuple, checking
+ schema conformance and tree membership at call time. *)
+let build_membership_criteria ~name schema :
+ (string -> Merkle.t option) -> Tuple.t -> bool =
+ let value_conforms_to_domain domain_name (value : Conventions.AbstractValue.t)
+ =
+ match domain_name with
+ | "integer" -> Obj.is_int value
+ | "natural" -> Obj.is_int value && (Obj.obj value : int) >= 0
+ | "string" -> Obj.tag value = Obj.string_tag
+ | _ ->
+ (* Unknown domain: could be a relation-typed attribute (nested tuple)
+ or a user-defined domain. Accept for now; full validation would
+ require looking up the target relation's schema. *)
+ true
+ in
+ let n_expected = List.length schema in
+ fun tree_of tuple ->
+ match tuple with
+ | Tuple.NonMaterialized nm -> (
+ match tree_of name with
+ | None -> false
+ | Some t ->
+ Merkle.member nm.hash t
+ && Tuple.AttributeMap.cardinal nm.attributes = n_expected
+ && List.for_all
+ (fun (attr_name, _) ->
+ Tuple.AttributeMap.mem attr_name nm.attributes)
+ schema)
+ | Tuple.Materialized m ->
+ Tuple.AttributeMap.cardinal m.attributes = n_expected
+ && List.for_all
+ (fun (attr_name, domain_name) ->
+ match Tuple.AttributeMap.find_opt attr_name m.attributes with
+ | None -> false
+ | Some attr ->
+ value_conforms_to_domain domain_name attr.Attribute.value)
+ schema
+
+let tree_of_db (db : Management.Database.t) name =
+ Option.bind (Management.Database.get_relation db name) (fun r -> r.Relation.tree)
+
+module Constraint = struct
+ include Constraint
+
+ (** Mutation direction for cascade constraint checking *)
+ type mutation = Insert | Delete
+
+ type mutation_context = {
+ target_relation : string;
+ transition : (string * Conventions.AbstractValue.t) list;
+ kind : mutation;
+ }
+ (** Context for a single tuple mutation, used by cascade checking *)
+end
(** Functor to create manipulation operations with a storage backend *)
module Make (Storage : Management.Physical.S) = struct
type storage = Storage.t
- type nonrec error = error
+ type error = Error.t
- let of_string_error s = StorageError s
+ let of_string_error s = Error.StorageError s
let ( let* ) = Result.bind
let fold_result (f : 'a -> 'b -> ('a, error) Result.t) (init : 'a)
@@ -86,16 +87,40 @@ module Make (Storage : Management.Physical.S) = struct
f a x)
(Ok init) xs
- (* Constraint evaluation context *)
-
(** Build an eval_context closed over a specific (storage, db) snapshot. All
relation lookups resolve against this exact database version. *)
let build_eval_context (storage : storage) (db : Management.Database.t) :
Constraint.eval_context =
+ let load_value attr_hash =
+ match Storage.load_raw storage attr_hash with
+ | Error _ | Ok None -> None
+ | Ok (Some vbytes) ->
+ Some (Marshal.from_bytes vbytes 0 : Conventions.AbstractValue.t)
+ in
+ let rec load_attrs acc = function
+ | [] -> Some (List.rev acc)
+ | (name, attr_hash) :: rest -> (
+ match load_value attr_hash with
+ | None -> None
+ | Some value -> load_attrs ((name, value) :: acc) rest)
+ in
+ let load_tuple hash =
+ match Storage.load_raw storage hash with
+ | Error _ | Ok None -> None
+ | Ok (Some bytes) ->
+ load_attrs [] (Storable.Tuple.of_bytes bytes).Storable.Tuple.attributes
+ in
+ let rec load_all acc = function
+ | [] -> Some (List.rev acc)
+ | h :: rest -> (
+ match load_tuple h with
+ | None -> None
+ | Some pairs -> load_all (pairs :: acc) rest)
+ in
let check_membership rel_name bound_pairs =
match Management.Database.get_relation db rel_name with
| None -> false
- | Some rel -> (
+ | Some rel ->
let attributes =
List.fold_left
(fun acc (k, v) ->
@@ -105,38 +130,7 @@ module Make (Storage : Management.Physical.S) = struct
let tuple =
Tuple.Materialized { Tuple.relation = rel_name; attributes }
in
- if not (rel.membership_criteria tuple) then false
- else
- (* For stored relations, check if a matching tuple actually exists *)
- match rel.tree with
- | None ->
- (* No tree = ephemeral/generator relation, membership_criteria suffices *)
- true
- | Some tree ->
- let hashes = Merkle.keys tree in
- List.exists
- (fun h ->
- match Storage.load_raw storage h with
- | Error _ | Ok None -> false
- | Ok (Some bytes) ->
- let stored = Storable.Tuple.of_bytes bytes in
- (* Check if all bound pairs match the stored tuple's attributes *)
- List.for_all
- (fun (bk, bv) ->
- List.exists
- (fun (sk, sh) ->
- sk = bk
- &&
- match Storage.load_raw storage sh with
- | Error _ | Ok None -> false
- | Ok (Some vbytes) ->
- let sv : Conventions.AbstractValue.t =
- Marshal.from_bytes vbytes 0
- in
- Stdlib.( = ) sv bv)
- stored.Storable.Tuple.attributes)
- bound_pairs)
- hashes)
+ rel.membership_criteria (tree_of_db db) tuple
in
let iterate_finite rel_name =
match Management.Database.get_relation db rel_name with
@@ -144,39 +138,11 @@ module Make (Storage : Management.Physical.S) = struct
| Some rel -> (
match rel.cardinality with
| Conventions.Cardinality.Finite _
- | Conventions.Cardinality.ConstrainedFinite -> (
- match rel.tree with
- | None -> Some []
- | Some tree ->
- let hashes = Merkle.keys tree in
- let rec load_all acc = function
- | [] -> Some (List.rev acc)
- | h :: rest -> (
- match Storage.load_raw storage h with
- | Error _ -> None
- | Ok None -> None
- | Ok (Some bytes) -> (
- let stored = Storable.Tuple.of_bytes bytes in
- let rec load_attrs attr_acc = function
- | [] -> Some (List.rev attr_acc)
- | (name, attr_hash) :: attr_rest -> (
- match Storage.load_raw storage attr_hash with
- | Error _ | Ok None -> None
- | Ok (Some vbytes) ->
- let value : Conventions.AbstractValue.t =
- Marshal.from_bytes vbytes 0
- in
- load_attrs
- ((name, value) :: attr_acc)
- attr_rest)
- in
- match
- load_attrs [] stored.Storable.Tuple.attributes
- with
- | None -> None
- | Some pairs -> load_all (pairs :: acc) rest))
- in
- load_all [] hashes)
+ | Conventions.Cardinality.ConstrainedFinite ->
+ let hashes =
+ match rel.tree with None -> [] | Some tree -> Merkle.keys tree
+ in
+ load_all [] hashes
| _ -> None)
in
{ Constraint.check_membership; iterate_finite }
@@ -187,7 +153,7 @@ module Make (Storage : Management.Physical.S) = struct
let store_relation (storage : storage) (relation : Relation.t) :
(unit, error) Result.t =
match relation.hash with
- | None -> Error (StorageError "Relation has no hash")
+ | None -> Error (Error.StorageError "Relation has no hash")
| Some rel_hash -> (
let tree_keys =
match relation.tree with None -> [] | Some tree -> Merkle.keys tree
@@ -203,7 +169,7 @@ module Make (Storage : Management.Physical.S) = struct
match
Storage.store_raw storage rel_hash (Storable.Relation.to_bytes stored)
with
- | Error _ -> Error (StorageError "Failed to store relation")
+ | Error _ -> Error (Error.StorageError "Failed to store relation")
| Ok () -> Ok ())
(** Store a database state to storage *)
@@ -232,14 +198,14 @@ module Make (Storage : Management.Physical.S) = struct
match
Storage.store_raw storage db.hash (Storable.Database.to_bytes stored)
with
- | Error _ -> Error (StorageError "Failed to store database")
+ | Error _ -> Error (Error.StorageError "Failed to store database")
| Ok () -> Ok ()
(** Load a relation state from storage by hash *)
let load_relation (storage : storage) (rel_hash : Conventions.Hash.t) :
(Relation.t option, error) Result.t =
match Storage.load_raw storage rel_hash with
- | Error _ -> Error (StorageError "Failed to load relation")
+ | Error _ -> Error (Error.StorageError "Failed to load relation")
| Ok None -> Ok None
| Ok (Some bytes) ->
let stored = Storable.Relation.of_bytes bytes in
@@ -252,8 +218,8 @@ module Make (Storage : Management.Physical.S) = struct
Relation.make ~hash:(Some rel_hash) ~name:stored.name
~schema:stored.schema ~tree:(Some tree) ~constraints:None
~cardinality:stored.cardinality ~generator:None
- ~membership_criteria:(build_membership_criteria stored.schema)
- ~provenance:(build_base_provenance stored.schema stored.name)
+ ~membership_criteria:(build_membership_criteria ~name:stored.name stored.schema)
+ ~provenance:(Relation.Provenance.Base stored.name)
~lineage:(Relation.Lineage.Base stored.name)
in
Ok (Some relation)
@@ -264,7 +230,7 @@ module Make (Storage : Management.Physical.S) = struct
let load_database (storage : storage) (db_hash : Conventions.Hash.t) :
(Management.Database.t option, error) Result.t =
match Storage.load_raw storage db_hash with
- | Error _ -> Error (StorageError "Failed to load database")
+ | Error _ -> Error (Error.StorageError "Failed to load database")
| Ok None -> Ok None
| Ok (Some bytes) ->
let stored = Storable.Database.of_bytes bytes in
@@ -278,7 +244,8 @@ module Make (Storage : Management.Physical.S) = struct
| (name, rel_hash) :: rest -> (
let* rel_opt = load_relation storage rel_hash in
match rel_opt with
- | None -> Error (StorageError ("Relation not found: " ^ name))
+ | None ->
+ Error (Error.StorageError ("Relation not found: " ^ name))
| Some rel -> load_relations ((name, rel) :: acc) rest)
in
let* relation_list = load_relations [] stored.relations in
@@ -307,9 +274,9 @@ module Make (Storage : Management.Physical.S) = struct
(** Create a new empty relation with the given schema *)
let create_relation_raw (storage : storage) (db : Management.Database.t)
~(name : string) ~(schema : Schema.t) :
- (Management.Database.t * Relation.t) result =
+ (Management.Database.t * Relation.t, Error.t) Result.t =
if Management.Database.has_relation db name then
- Error (RelationAlreadyExists name)
+ Error (Error.RelationAlreadyExists name)
else
let tree = Merkle.empty in
let relation_hash = Hashing.hash_relation ~name ~schema ~tree in
@@ -317,8 +284,8 @@ module Make (Storage : Management.Physical.S) = struct
Relation.make ~hash:(Some relation_hash) ~name ~schema ~tree:(Some tree)
~constraints:None ~cardinality:(Conventions.Cardinality.Finite 0)
~generator:None
- ~membership_criteria:(build_membership_criteria schema)
- ~provenance:(build_base_provenance schema name)
+ ~membership_criteria:(build_membership_criteria ~name schema)
+ ~provenance:(Relation.Provenance.Base name)
~lineage:(Relation.Lineage.Base name)
in
let new_db = Management.Database.add_relation db ~relation in
@@ -342,7 +309,7 @@ module Make (Storage : Management.Physical.S) = struct
in
match Storage.store_raw storage value_hash value_bytes with
| Error _ ->
- Error (StorageError ("Failed to store attribute: " ^ name))
+ Error (Error.StorageError ("Failed to store attribute: " ^ name))
| Ok () -> store_all ((name, value_hash) :: acc) rest)
in
store_all [] attrs
@@ -351,7 +318,7 @@ module Make (Storage : Management.Physical.S) = struct
let load_tuple (storage : storage) (tuple_hash : Conventions.Hash.t) :
(Tuple.materialized option, error) Result.t =
match Storage.load_raw storage tuple_hash with
- | Error _ -> Error (StorageError "Failed to load tuple")
+ | Error _ -> Error (Error.StorageError "Failed to load tuple")
| Ok None -> Ok None
| Ok (Some tuple_bytes) ->
let stored = Storable.Tuple.of_bytes tuple_bytes in
@@ -360,8 +327,10 @@ module Make (Storage : Management.Physical.S) = struct
| (name, attr_hash) :: rest -> (
match Storage.load_raw storage attr_hash with
| Error _ ->
- Error (StorageError ("Failed to load attribute: " ^ name))
- | Ok None -> Error (StorageError ("Attribute not found: " ^ name))
+ Error
+ (Error.StorageError ("Failed to load attribute: " ^ name))
+ | Ok None ->
+ Error (Error.StorageError ("Attribute not found: " ^ name))
| Ok (Some value_bytes) ->
let value : Conventions.AbstractValue.t =
Marshal.from_bytes value_bytes 0
@@ -385,7 +354,7 @@ module Make (Storage : Management.Physical.S) = struct
| hash :: rest -> (
let* tup_opt = load_tuple storage hash in
match tup_opt with
- | None -> Error (TupleNotFound hash)
+ | None -> Error (Error.TupleNotFound hash)
| Some tuple -> load_all (tuple :: acc) rest)
in
load_all [] hashes
@@ -396,7 +365,8 @@ module Make (Storage : Management.Physical.S) = struct
| Some tup -> on_some tup
| None ->
Error
- (StorageError ("Tuple missing for referenced hash: " ^ tuple_hash))
+ (Error.StorageError
+ ("Tuple missing for referenced hash: " ^ tuple_hash))
let transition_attrs (tuple : Tuple.materialized) =
Tuple.AttributeMap.bindings tuple.attributes
@@ -404,7 +374,7 @@ module Make (Storage : Management.Physical.S) = struct
let ensure_relation_exists (db : Management.Database.t) name =
if Management.Database.has_relation db name then Ok ()
- else Error (RelationNotFound name)
+ else Error (Error.RelationNotFound name)
let bump_cardinality delta = function
| Conventions.Cardinality.Finite n ->
@@ -414,7 +384,13 @@ module Make (Storage : Management.Physical.S) = struct
let rehashed_relation_with_tree ~name ~schema (relation : Relation.t) tree
cardinality =
let hash = Hashing.hash_relation ~name ~schema ~tree in
- { relation with hash = Some hash; tree = Some tree; cardinality }
+ {
+ relation with
+ hash = Some hash;
+ tree = Some tree;
+ cardinality;
+ membership_criteria = build_membership_criteria ~name schema;
+ }
let validate_tuple_constraints (storage : storage)
(db : Management.Database.t) (relation : Relation.t)
@@ -428,14 +404,15 @@ module Make (Storage : Management.Physical.S) = struct
in
match eval_result with
| Ok true -> Ok ()
- | Ok false -> Error (ConstraintViolation "Constraint not satisfied")
+ | Ok false -> Error (Error.ConstraintViolation "Constraint not satisfied")
| Error (Constraint.ConstraintFailures failures) ->
let msg =
String.concat "; "
(List.map (fun (n, _) -> "constraint " ^ n ^ " violated") failures)
in
- Error (ConstraintViolation msg)
- | Error _ -> Error (ConstraintViolation "Constraint evaluation failed")
+ Error (Error.ConstraintViolation msg)
+ | Error _ ->
+ Error (Error.ConstraintViolation "Constraint evaluation failed")
let store_tuple_payload storage tuple_hash (tuple : Tuple.materialized) =
let* attr_hashes = store_attributes storage tuple in
@@ -444,23 +421,25 @@ module Make (Storage : Management.Physical.S) = struct
in
let tuple_bytes = Storable.Tuple.to_bytes stored_tuple in
match Storage.store_raw storage tuple_hash tuple_bytes with
- | Error _ -> Error (StorageError "Failed to store tuple")
+ | Error _ -> Error (Error.StorageError "Failed to store tuple")
| Ok () -> Ok ()
(* Cascade constraint helpers *)
let polarity_triggered_by kind pol =
match (kind, pol) with
- | Insert, (Constraint.Negative | Constraint.Both) -> true
- | Delete, (Constraint.Positive | Constraint.Both) -> true
+ | Constraint.Insert, (Constraint.Negative | Constraint.Both) -> true
+ | Constraint.Delete, (Constraint.Positive | Constraint.Both) -> true
| _ -> false
- let cascade_violation_msg kind dep_rel cname constrained_name =
+ let cascade_violation_msg kind target_relation cname constrained_name =
let verb =
- match kind with Insert -> "inserting into" | Delete -> "deleting from"
+ match kind with
+ | Constraint.Insert -> "inserting into"
+ | Constraint.Delete -> "deleting from"
in
- Printf.sprintf "cascade: %s %s violates constraint %s on %s" verb dep_rel
- cname constrained_name
+ Printf.sprintf "cascade: %s %s violates constraint %s on %s" verb
+ target_relation cname constrained_name
let is_deferred (db : Management.Database.t) rel_name cname =
List.exists
@@ -477,13 +456,15 @@ module Make (Storage : Management.Physical.S) = struct
| None -> false)
filter
- let recheck_one storage ctx mctx cname cbody constrained_name filter
- (h : Conventions.Hash.t) : unit result =
+ let recheck_one storage ctx (mctx : Constraint.mutation_context) cname cbody
+ constrained_name filter (h : Conventions.Hash.t) : (unit, error) Result.t
+ =
with_loaded_tuple storage h ~on_some:(fun tup ->
if not (needs_recheck filter tup) then Ok ()
else
let cbody' =
- Constraint.substitute_transition cbody mctx.dep_rel mctx.transition
+ Constraint.substitute_transition cbody mctx.target_relation
+ mctx.transition
in
match
Constraint.evaluate_first_failure ctx tup [ (cname, cbody') ]
@@ -491,21 +472,21 @@ module Make (Storage : Management.Physical.S) = struct
| Ok true -> Ok ()
| Ok false | Error _ ->
Error
- (ConstraintViolation
- (cascade_violation_msg mctx.kind mctx.dep_rel cname
+ (Error.ConstraintViolation
+ (cascade_violation_msg mctx.kind mctx.target_relation cname
constrained_name)))
- let check_one_constraint storage ctx db mctx constrained_name constrained_rel
- (cname, cbody) : unit result =
+ let check_one_constraint storage ctx db (mctx : Constraint.mutation_context)
+ constrained_name constrained_rel (cname, cbody) : (unit, error) Result.t =
if is_deferred db constrained_name cname then Ok ()
else
let pols = Constraint.polarity_of cbody in
- match Constraint.polarity_find mctx.dep_rel pols with
+ match Constraint.polarity_find mctx.target_relation pols with
| None -> Ok ()
| Some pol when not (polarity_triggered_by mctx.kind pol) -> Ok ()
| Some _ ->
let filter =
- Constraint.focused_filter cbody mctx.dep_rel mctx.transition
+ Constraint.focused_filter cbody mctx.target_relation mctx.transition
in
let hashes =
match constrained_rel.Relation.tree with
@@ -517,13 +498,14 @@ module Make (Storage : Management.Physical.S) = struct
recheck_one storage ctx mctx cname cbody constrained_name filter h)
() hashes
- (** Scan every relation in [db] for constraints that reference [mctx.dep_rel]
- with a polarity matched by the mutation kind, then re-evaluate those
- constraints against the affected tuples in each constrained relation.
+ (** Scan every relation in [db] for constraints that reference
+ [mctx.target_relation] with a polarity matched by the mutation kind, then
+ re-evaluate those constraints against the affected tuples in each
+ constrained relation.
Returns [Ok ()] when all cascade checks pass. *)
let check_cascade_constraints (storage : storage) (db : Management.Database.t)
- (mctx : mutation_context) : unit result =
+ (mctx : Constraint.mutation_context) =
let ctx = build_eval_context storage db in
Management.Database.RelationMap.fold
(fun constrained_name constrained_rel acc ->
@@ -541,11 +523,13 @@ module Make (Storage : Management.Physical.S) = struct
(** Insert a tuple into a relation, storing it in the backend *)
let create_tuple (storage : storage) (db : Management.Database.t)
(relation : Relation.t) (tuple : Tuple.materialized) :
- (Management.Database.t * Relation.t * Conventions.Hash.t) result =
+ (Management.Database.t * Relation.t * Conventions.Hash.t, error) Result.t
+ =
let name = relation.name in
let* () = ensure_relation_exists db name in
- if not (relation.membership_criteria (Tuple.Materialized tuple)) then
- Error (ConstraintViolation "Tuple does not satisfy membership criteria")
+ if not (relation.membership_criteria (tree_of_db db) (Tuple.Materialized tuple)) then
+ Error
+ (Error.ConstraintViolation "Tuple does not satisfy membership criteria")
else
let* () = validate_tuple_constraints storage db relation tuple in
let tuple_hash = Hashing.hash_tuple tuple in
@@ -553,7 +537,7 @@ module Make (Storage : Management.Physical.S) = struct
match relation.tree with Some t -> t | None -> Merkle.empty
in
if Merkle.member tuple_hash current_tree then
- Error (DuplicateTuple tuple_hash)
+ Error (Error.DuplicateTuple tuple_hash)
else
let* () = store_tuple_payload storage tuple_hash tuple in
let new_tree = Merkle.insert tuple_hash current_tree in
@@ -568,9 +552,9 @@ module Make (Storage : Management.Physical.S) = struct
let* () =
check_cascade_constraints storage new_db
{
- dep_rel = name;
+ target_relation = name;
transition = transition_attrs tuple;
- kind = Insert;
+ kind = Constraint.Insert;
}
in
let* () = store_relation storage new_relation in
@@ -580,7 +564,9 @@ module Make (Storage : Management.Physical.S) = struct
(** Insert multiple tuples into a relation *)
let create_tuples (storage : storage) (db : Management.Database.t)
(relation : Relation.t) (tuples : Tuple.materialized list) :
- (Management.Database.t * Relation.t * Conventions.Hash.t list) result =
+ ( Management.Database.t * Relation.t * Conventions.Hash.t list,
+ error )
+ Result.t =
let rec insert_all db rel hashes = function
| [] -> Ok (db, rel, List.rev hashes)
| tuple :: rest ->
@@ -592,16 +578,16 @@ module Make (Storage : Management.Physical.S) = struct
(** Remove a tuple from a relation by its hash *)
let retract_tuple (storage : storage) (db : Management.Database.t)
(relation : Relation.t) ~(tuple_hash : Conventions.Hash.t) :
- (Management.Database.t * Relation.t) result =
+ (Management.Database.t * Relation.t, error) Result.t =
let name = relation.name in
let* () = ensure_relation_exists db name in
let* current_tree =
match relation.tree with
- | None -> Error (TupleNotFound tuple_hash)
+ | None -> Error (Error.TupleNotFound tuple_hash)
| Some tree -> Ok tree
in
if not (Merkle.member tuple_hash current_tree) then
- Error (TupleNotFound tuple_hash)
+ Error (Error.TupleNotFound tuple_hash)
else
(* Note: We don't delete from storage - content-addressed storage is append-only.
The tuple data remains for historical queries / garbage collection later. *)
@@ -618,9 +604,9 @@ module Make (Storage : Management.Physical.S) = struct
with_loaded_tuple storage tuple_hash ~on_some:(fun deleted_tuple ->
check_cascade_constraints storage new_db
{
- dep_rel = name;
+ target_relation = name;
transition = transition_attrs deleted_tuple;
- kind = Delete;
+ kind = Constraint.Delete;
})
in
let* () = store_relation storage new_relation in
@@ -724,7 +710,8 @@ module Make (Storage : Management.Physical.S) = struct
let get_required (db : Management.Database.t) rel_name =
match Management.Database.get_relation db rel_name with
| Some rel -> Ok rel
- | None -> Error (StorageError ("Catalog relation missing: " ^ rel_name))
+ | None ->
+ Error (Error.StorageError ("Catalog relation missing: " ^ rel_name))
in
let* relation_rel = get_required db Prelude.Catalog.relation_rel_name in
let* db, _, _ =
@@ -804,9 +791,9 @@ module Make (Storage : Management.Physical.S) = struct
[sakura:relation] and [sakura:attribute] in the system catalog. *)
let create_relation (storage : storage) (db : Management.Database.t)
~(name : string) ~(schema : Schema.t) :
- (Management.Database.t * Relation.t) result =
+ (Management.Database.t * Relation.t, error) Result.t =
if Management.Database.has_relation db name then
- Error (RelationAlreadyExists name)
+ Error (Error.RelationAlreadyExists name)
else
let tree = Merkle.empty in
let relation_hash = Hashing.hash_relation ~name ~schema ~tree in
@@ -814,8 +801,8 @@ module Make (Storage : Management.Physical.S) = struct
Relation.make ~hash:(Some relation_hash) ~name ~schema ~tree:(Some tree)
~constraints:None ~cardinality:(Conventions.Cardinality.Finite 0)
~generator:None
- ~membership_criteria:(build_membership_criteria schema)
- ~provenance:(build_base_provenance schema name)
+ ~membership_criteria:(build_membership_criteria ~name schema)
+ ~provenance:(Relation.Provenance.Base name)
~lineage:(Relation.Lineage.Base name)
in
let new_db = Management.Database.add_relation db ~relation in
@@ -827,11 +814,11 @@ module Make (Storage : Management.Physical.S) = struct
(** Create an immutable/generator-based relation *)
let create_immutable_relation (storage : storage) (db : Management.Database.t)
~(name : string) ~(schema : Schema.t) ~(generator : Generator.t)
- ~(membership_criteria : Tuple.t -> bool)
+ ~(membership_criteria : (string -> Merkle.t option) -> Tuple.t -> bool)
~(cardinality : Conventions.Cardinality.t) :
- (Management.Database.t * Relation.t) result =
+ (Management.Database.t * Relation.t, error) Result.t =
if Management.Database.has_relation db name then
- Error (RelationAlreadyExists name)
+ Error (Error.RelationAlreadyExists name)
else
let relation_hash =
Hashing.hash_relation ~name ~schema ~tree:Merkle.empty
@@ -840,8 +827,7 @@ module Make (Storage : Management.Physical.S) = struct
Relation.make ~hash:(Some relation_hash) ~name ~schema
~tree:None (* No tree for generator-based relations *)
~constraints:None ~cardinality ~generator:(Some generator)
- ~membership_criteria
- ~provenance:(build_base_provenance schema name)
+ ~membership_criteria ~provenance:(Relation.Provenance.Base name)
~lineage:(Relation.Lineage.Base name)
in
let new_db = Management.Database.add_relation db ~relation in
@@ -853,9 +839,9 @@ module Make (Storage : Management.Physical.S) = struct
(** Remove a relation from the database. Also removes its entries from
[sakura:relation] and [sakura:attribute]. *)
let retract_relation (storage : storage) (db : Management.Database.t)
- ~(name : string) : Management.Database.t result =
+ ~(name : string) : (Management.Database.t, error) Result.t =
match Management.Database.get_relation db name with
- | None -> Error (RelationNotFound name)
+ | None -> Error (Error.RelationNotFound name)
| Some relation ->
let new_db = Management.Database.remove_relation db ~name in
(* Update catalog on the db state that no longer contains the retracted relation *)
@@ -865,10 +851,11 @@ module Make (Storage : Management.Physical.S) = struct
(** Clear all tuples from a relation (truncate) *)
let clear_relation (storage : storage) (db : Management.Database.t)
- (relation : Relation.t) : (Management.Database.t * Relation.t) result =
+ (relation : Relation.t) :
+ (Management.Database.t * Relation.t, error) Result.t =
let name = relation.name in
match Management.Database.get_relation db name with
- | None -> Error (RelationNotFound name)
+ | None -> Error (Error.RelationNotFound name)
| Some _ ->
let new_tree = Merkle.empty in
let new_hash =
@@ -880,6 +867,8 @@ module Make (Storage : Management.Physical.S) = struct
hash = Some new_hash;
tree = Some new_tree;
cardinality = Conventions.Cardinality.Finite 0;
+ membership_criteria =
+ build_membership_criteria ~name relation.schema;
}
in
let new_db =
@@ -894,9 +883,9 @@ module Make (Storage : Management.Physical.S) = struct
let update_relation_constraints (storage : storage)
(db : Management.Database.t) ~(relation_name : string)
~(constraints : Relation.RelationConstraint.t) :
- (Management.Database.t * Relation.t) result =
+ (Management.Database.t * Relation.t, error) Result.t =
match Management.Database.get_relation db relation_name with
- | None -> Error (RelationNotFound relation_name)
+ | None -> Error (Error.RelationNotFound relation_name)
| Some relation ->
let merged_constraints =
match relation.constraints with
@@ -1012,7 +1001,7 @@ module Make (Storage : Management.Physical.S) = struct
| Ok true -> Ok ()
| Ok false | Error _ ->
Error
- (ConstraintViolation
+ (Error.ConstraintViolation
(Printf.sprintf
"deferred constraint %s on %s violated"
entry.constraint_name entry.relation_name))))
@@ -1037,6 +1026,3 @@ module Make (Storage : Management.Physical.S) = struct
let* () = store_database storage db' in
Ok db'
end
-
-module Memory = Make (Management.Physical.Memory)
-(** Default instance using in-memory storage for convenience/testing *)
diff --git a/lib/manipulation.mli b/lib/manipulation.mli
deleted file mode 100644
index 2a52b5c..0000000
--- a/lib/manipulation.mli
+++ /dev/null
@@ -1,137 +0,0 @@
-type error =
- | RelationNotFound of string
- | RelationAlreadyExists of string
- | TupleNotFound of Conventions.Hash.t
- | DuplicateTuple of Conventions.Hash.t
- | ConstraintViolation of string
- | StorageError of string
-
-val string_of_error : error -> string
-val sexp_of_error : error -> Sexplib.Sexp.t
-
-type 'a result = ('a, error) Result.t
-
-module Schema = Schema
-
-val hash_tuple : Tuple.materialized -> Conventions.Hash.t
-
-module Make (Storage : Management.Physical.S) : sig
- type storage = Storage.t
- type nonrec error = error
-
- val of_string_error : string -> error
-
- val store_database :
- storage -> Management.Database.t -> (unit, error) Result.t
-
- val load_relation :
- storage -> Conventions.Hash.t -> (Relation.t option, error) Result.t
-
- val load_database :
- storage ->
- Conventions.Hash.t ->
- (Management.Database.t option, error) Result.t
-
- val load_tuple :
- storage -> Conventions.Hash.t -> (Tuple.materialized option, error) Result.t
-
- val load_tuples :
- storage ->
- Conventions.Hash.t list ->
- (Tuple.materialized list, error) Result.t
-
- val create_tuple :
- storage ->
- Management.Database.t ->
- Relation.t ->
- Tuple.materialized ->
- (Management.Database.t * Relation.t * Conventions.Hash.t, error) Result.t
-
- val create_tuples :
- storage ->
- Management.Database.t ->
- Relation.t ->
- Tuple.materialized list ->
- ( Management.Database.t * Relation.t * Conventions.Hash.t list,
- error )
- Result.t
-
- val retract_tuple :
- storage ->
- Management.Database.t ->
- Relation.t ->
- tuple_hash:Conventions.Hash.t ->
- (Management.Database.t * Relation.t, error) Result.t
-
- val register_domain :
- storage ->
- Management.Database.t ->
- Domain.t ->
- (Management.Database.t, error) Result.t
-
- val create_database :
- storage -> name:string -> (Management.Database.t, error) Result.t
-
- val database_history : Management.Database.t -> Conventions.Hash.t list
-
- val create_relation :
- storage ->
- Management.Database.t ->
- name:string ->
- schema:Schema.t ->
- (Management.Database.t * Relation.t, error) Result.t
-
- val create_immutable_relation :
- storage ->
- Management.Database.t ->
- name:string ->
- schema:Schema.t ->
- generator:Generator.t ->
- membership_criteria:(Tuple.t -> bool) ->
- cardinality:Conventions.Cardinality.t ->
- (Management.Database.t * Relation.t, error) Result.t
-
- val retract_relation :
- storage ->
- Management.Database.t ->
- name:string ->
- Management.Database.t result
-
- val clear_relation :
- storage ->
- Management.Database.t ->
- Relation.t ->
- (Management.Database.t * Relation.t, error) Result.t
-
- val register_constraint :
- storage ->
- Management.Database.t ->
- constraint_name:string ->
- relation_name:string ->
- body:Constraint.t ->
- (Management.Database.t, error) Result.t
-
- val tuple_hashes : Relation.t -> Conventions.Hash.t list
- val get_relation : Management.Database.t -> name:string -> Relation.t option
- val tuple_count : Relation.t -> int
- val tuple_exists : Relation.t -> Conventions.Hash.t -> bool
-
- val attach_constraint :
- storage ->
- Management.Database.t ->
- constraint_name:string ->
- relation_name:string ->
- body:Constraint.t ->
- timing:Constraint.timing ->
- (Management.Database.t, error) Result.t
-
- val check_deferred_constraints :
- storage -> Management.Database.t -> (unit, error) Result.t
-
- val commit :
- storage -> Management.Database.t -> (Management.Database.t, error) Result.t
-end
-
-module Memory : sig
- include module type of Make (Management.Physical.Memory)
-end