aboutsummaryrefslogblamecommitdiffstats
path: root/docs/relational-algebra.org
blob: 54f28c5e90b93fbb5e763a43c3f0d8e704c44218 (plain) (tree)






























































































































































































                                                                            
* Relational Algebra

Relational algebra is simply that, an algebra. It manipulates objects
in its space and outputs another element of the same space. SQL is
often seem as an attempt to encode the relational algebra, however it
lacks the ability to represent its objects from its algebra by a
substitution and weakening statics, which typed lambda calculus
can. It also cannot separate properly the boundaries of its
sublanguages, which are all mashed together in a huge mess. Our DRL
(Data Retrieval Language) is concerned with relational algebra
operations on relations (that is, no such thing as "denormalized
relations" should be accepted) in first order predicate
logic. =lib/algebra.ml= implements the core transformations that query
and manipulate relations. It's essentially an OCaml functor that works
with any storage backend, producing =ephemeral relations=.

* Ephemeral Relations

Ephemeral relations are generated objects that do not persist its own
tuples in storage. In other words, they are objects that are fully
relations themselves, but their data retrieval is by composition atop
of other base relations. Saying that a relation is *stored* is
logical-physical confusion (LPC). Only its data is. Since we are
attempting to apply a theory to a computer, we cannot resort however
to infinite relations where each transformation by constraints is
given by the axiom of choice over a substitution static, but we
simulate such by composition of defined relations over our DML
sublanguage; it's a compromise for simplicity. In a sense, the name
*ephemeral relation* is a contradiction in terms, but it is our way of
calling for the composition nature of such relations, while trying to
get away from R-tables as a representation, which are not relations,
only a tabular view of data; extension.

* Implementation: The Functor Pattern

=algebra.ml= is structured as an OCaml functor:

#+begin_src ocaml
module Make(Storage : Management.Physical.S) = struct
  (* implementation *)
end

module Memory = Make(Management.Physical.Memory)
#+end_src

This design allows the relational algebra to be agnostic of the
storage backend. The same algebra operations work with in-memory
storage, disk storage, or any future backend that implements the
=Physical.S= signature.

* Generator-Based Lazy Evaluation

Rather than materializing entire relations, =algebra.ml= uses lazy
generators (=Generator.t=, a function from position to the next value)
to produce tuples on demand. This avoids the overhead of computing and
storing all intermediate results.

** Core Generator Utilities

- =to_generator rel= :: Converts a relation into a generator. If the
  relation is already ephemeral (has a generator), returns it; if
  stored, loads tuples from the hash tree and wraps them in a
  generator.

- =drain gen= :: Exhausts a generator completely into a list. Used
  when an operation needs all tuples at once (e.g., for the right side
  of a join).

- =list_generator ts= :: Creates a generator from a concrete tuple
  list.

- =of_generator ~name ~schema gen= :: Wraps a generator with metadata
  (name, schema, constraints, cardinality) to create an ephemeral
  =Relation.t=. All algebraic operations produce relations this way.

* The Operators

Each operator accepts one or two relations and returns a new ephemeral
relation whose generator lazily computes the result.

** Selection (σ): Filtering by Predicate

#+begin_src ocaml
select_fn storage predicate rel
#+end_src

Filters tuples that satisfy the boolean predicate. The schema and
constraints are preserved unchanged. The resulting generator only
yields tuples for which =predicate t = true=.

** Projection (π): Attribute Subset

#+begin_src ocaml
project storage attrs rel
#+end_src

Keeps only the specified attributes. The schema is reduced to match.
Importantly, constraints are filtered: only constraints that reference
only the projected attributes are retained. Constraints mentioning
dropped attributes are discarded to avoid invalidity.

** Rename (ρ): Attribute Renaming

#+begin_src ocaml
rename storage renames rel
#+end_src

Applies a list of =(old_name, new_name)= pairs to rename attributes.
The schema is updated, and all constraints have their variable names
updated to match the renamed attributes.

** Equijoin (⋈): Relation Joining

#+begin_src ocaml
equijoin storage join_attrs left right
#+end_src

Joins two relations on matching values of =join_attrs=. The operation:
1. Drains the right relation (materializes all right tuples)
2. Streams the left relation through a generator
3. For each left tuple, filters right tuples matching on the join attributes
4. Merges matching tuples by combining their attribute maps

The resulting schema is the left schema plus non-join attributes from
the right (avoiding duplicates). Constraints from both relations are
merged if they coexist.

** Union (∪): Relation Combination

#+begin_src ocaml
union storage rel1 rel2
#+end_src

Chains two generators: first exhausts =rel1=, then =rel2=. The schema
is from the left relation. Constraints are dropped conservatively
(constraints only hold if both inputs agree on them).

** Difference (−): Set Subtraction

#+begin_src ocaml
diff storage rel1 rel2
#+end_src

Yields tuples from =rel1= that do not appear in =rel2=. Drains =rel2=
to materialize it for comparison, then streams =rel1= and filters out
matching tuples (using attribute equality). Constraints from =rel1=
are preserved.

** Take (τ): Cardinality Limiting

#+begin_src ocaml
take storage n rel
#+end_src

Limits output to =n= tuples. The resulting relation's cardinality is
marked as =Finite n=. Schema and constraints are preserved.

* Constraint Propagation

Constraints attached to relations describe membership predicates on
tuples. As operations transform relations, constraints must be
updated:

- *Preserved* (select): Filtering does not invalidate constraints;
  they still apply to the output.
- *Filtered* (project): Constraints mentioning dropped attributes are
  removed.
- *Renamed* (rename): Constraint variable names are updated to match
  attribute renames.
- *Merged* (join, union): Constraints from both input relations are
  combined, or dropped if uncertain (conservative approach).
- *Preserved* (diff, take): Constraints from the input relation carry
  forward.

* Materialization

For cases where a concrete tuple list is needed (rather than a lazy
generator), the =materialize= function exhausts an ephemeral relation
into =Tuple.materialized list=. This is rarely needed in typical
algebraic compositions, but useful for final results or debugging.

#+begin_src ocaml
materialize storage rel : Tuple.materialized list
#+end_src

* Constant Relations

The =const_relation= utility creates a single-tuple relation from a
list of key-value pairs. Useful for embedding literal values into
algebraic expressions.