aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/abstract/Schematics.ml8
-rw-r--r--lib/kernel/SubstantialRelation.ml64
-rw-r--r--lib/kernel/SubstantialRelation.mli17
3 files changed, 83 insertions, 6 deletions
diff --git a/lib/abstract/Schematics.ml b/lib/abstract/Schematics.ml
new file mode 100644
index 0000000..9a98a13
--- /dev/null
+++ b/lib/abstract/Schematics.ml
@@ -0,0 +1,8 @@
+module type SCHEMA = sig
+ type t
+ type attribute
+ val attributes : t -> (string * attribute) BatFingerTree.t
+ val project : t -> string BatFingerTree.t -> (t, Concepts.Condition.condition) result
+ val rename : t -> (string * string) BatFingerTree.t -> (t, Concepts.Condition.condition) result
+ val merge : t -> t -> (t, Concepts.Condition.condition) result
+end
diff --git a/lib/kernel/SubstantialRelation.ml b/lib/kernel/SubstantialRelation.ml
index eebae3e..b0c8a51 100644
--- a/lib/kernel/SubstantialRelation.ml
+++ b/lib/kernel/SubstantialRelation.ml
@@ -1,4 +1,7 @@
-module Make (S : Abstract.Storage.STORAGE) = struct
+module Make (S : Abstract.Storage.STORAGE) (Schematics : sig
+ include Protocols.Schematics.S
+ val of_blob : Concepts.Blob.t -> (Schema.t, Concepts.Condition.condition) result
+end) = struct
module SI = Storage.Make (S)
module Error = struct
@@ -107,6 +110,45 @@ module Make (S : Abstract.Storage.STORAGE) = struct
let* present = TupleSet.lookup tx tuple node in
Ok (Option.is_some present)
+ let heading_value tx relation =
+ let open Utilities.Result in
+ let* data = SI.get_req tx (S.Hash relation.heading) in
+ Schematics.of_blob data
+
+ let enumerate storage relation =
+ let open Utilities.Result in
+ let* tx = S.start storage in
+ let* node = tuple_node tx relation in
+ (* TODO: `TupleSet.keys` walks the whole Merkle structure eagerly,
+ before the first tuple is yielded. Fine for this sketch; a real
+ implementation should walk it lazily instead (its own small
+ generator, or an explicit stack over Merkle.node) so that
+ stacking e.g. `restrict` on a huge relation does not pay for the
+ entire scan before the first `next`. *)
+ let* keys = TupleSet.keys tx node in
+ let remaining = ref keys in
+ let closed = ref false in
+ let close () = if not !closed then begin closed := true; ignore (S.abort tx) end in
+ let produce ~yield =
+ let rec loop () =
+ match BatFingerTree.front !remaining with
+ | None ->
+ close ();
+ Ok ()
+ | Some (rest, addr) ->
+ remaining := rest;
+ match SI.get_req tx (S.Hash addr) |> fmap Concepts.Tuple.Representation.of_blob with
+ | Error _ as e ->
+ close ();
+ e
+ | Ok tuple ->
+ yield tuple;
+ loop ()
+ in
+ loop ()
+ in
+ Ok (Generator.cursor_of ~on_release:close produce)
+
class relation storage value =
object (self)
inherit Lifecycle.null
@@ -115,12 +157,24 @@ module Make (S : Abstract.Storage.STORAGE) = struct
method heading = Ok relation.heading
method predicate = Ok relation.predicate
method local_constraints = Ok relation.local_constraints
- method tuples = Ok relation.tuples
- method contains tuple =
- SI.with_transaction storage (fun tx -> contains_tuple tx relation tuple)
+ (* The protocol takes a tuple, not bytes: encoding is this object's business, and a caller
+ that had to produce the exact stored bytes would have to know this encoding to do it. *)
+ method contains (tuple : Concepts.Tuple.t) =
+ SI.with_transaction storage (fun tx ->
+ contains_tuple tx relation (Concepts.Tuple.Representation.to_blob tuple) )
+
+ method schema = SI.with_transaction storage (fun tx -> heading_value tx relation)
+
+ (* A substantial relation is finitely enumerable, so it carries [Enumerable] as well as
+ [Relation]. A procedural relation would carry only the latter, which is how an evaluator
+ discovers it cannot iterate one -- see [Protocols.Enumerable]. The context is accepted and
+ unused here: this enumeration reads through its own cursor-lifetime transaction and needs
+ no name resolution, but cancellation should eventually be checked between tuples. *)
+ method enumerate (_ : Protocols.Context.t) = enumerate storage relation
- method protocols : Protocols.Handle.protocol list = Protocols.[Relation.make self]
+ method protocols : Protocols.Handle.protocol list =
+ [Protocols.Relation.make self; Protocols.Enumerable.make self; Schematics.make self]
method hash = hash relation
end
diff --git a/lib/kernel/SubstantialRelation.mli b/lib/kernel/SubstantialRelation.mli
index 9e61411..866c3a0 100644
--- a/lib/kernel/SubstantialRelation.mli
+++ b/lib/kernel/SubstantialRelation.mli
@@ -1,4 +1,7 @@
-module Make (S : Abstract.Storage.STORAGE) : sig
+module Make (S : Abstract.Storage.STORAGE) (Schematics : sig
+ include Protocols.Schematics.S
+ val of_blob : Concepts.Blob.t -> (Schema.t, Concepts.Condition.condition) result
+end) : sig
type t
val encode : t -> Concepts.Blob.t
@@ -46,4 +49,16 @@ module Make (S : Abstract.Storage.STORAGE) : sig
val contains_tuple :
S.transaction -> t -> Concepts.Blob.t -> (bool, Concepts.Condition.condition) result
+
+ (** decodes the heading at [heading relation] -- the body of
+ [Schematics.schema] for this kind of relation. *)
+ val heading_value : S.transaction -> t -> (Schematics.Schema.t, Concepts.Condition.condition) result
+
+ (** a handle carrying [Protocols.Cursor] over every tuple currently
+ asserted, decoded via [Concepts.Tuple.Representation.of_blob] --
+ the body of [Protocols.Relation.enumerate] for this kind of
+ relation. Opens its own storage transaction, held for the
+ cursor's whole lifetime rather than just this call -- see
+ [Kernel.Generator] and docs/design/evaluator.md, "Shape". *)
+ val enumerate : S.connection -> t -> (Protocols.Handle.t, Concepts.Condition.condition) result
end