Rel
database modelling conventionsThere's more than one way to model your database in OCaml with Rel
. The following defines a simple conventions you can follow.
These conventions are followed by the rel-sqlite3
tool when it outputs the OCaml code needed to support interaction with a pre-existing database schema, except for the naming conventions which respect those found in the schema.
If you are not using a pre-existing schema:
Given a table named n with columns c0, c1, … define a module N
(n capitalized) for it. This module should have
N.t
for representing table rows.N.row
constructor for the row with arguments in the order of columns.N.v
constructor with labelled arguments.N.c
i projecting the corresponding column ci from N.t
values.N.c'
i of type Rel.Col.t
for each corresponding column ci.N.table
of type Rel.Table.t
that defines the table.Note that once you have modelled your tables you can use Rel.Sql.create_schema
to output the corresponding schema in SQL's data definition language.
Consider a person
table which has three columns id
and first_name
and last_name
columns. The following interface represents such a table according to the convention.
(** Persons. *)
module Person : sig
type id = int
(** The type for person identifiers. *)
type t
(** The type for persons. *)
val v : id:id -> first_name:string -> last_name:string -> t
(** [v ~id ~first_name ~last_name] is a person with given attributes.
See accessors for semantics. *)
val row : int -> string -> string -> t
(** [row] is unlabelled {!v}. *)
val id : t -> id
(** [id p] is the unique identifier of [p]. *)
val first_name : t -> string
(** [first_name p] is the first name of [p]. *)
val last_name : t -> string
(** [last_name p] is the last name of [p]. *)
(** {1:table Table} *)
open Rel
val id' : (t, id) Col.t
(** [id'] is the {!id} column. *)
val first_name' : (t, string) Col.t
(** [first_name'] is the {!first_name} column. *)
val last_name' : (t, string) Col.t
(** [last_name'] is the {!last_name} column. *)
val table : t Table.t
(** [table] is the person table. *)
end
The simplest way of implementing this signature is by using OCaml records. For example:
module Person = struct
type id = int
type t = { id : id; first_name : string; last_name : string }
let v ~id ~first_name ~last_name = { id; first_name; last_name }
let row id first_name last_name = { id; first_name; last_name }
let id r = r.id
let first_name r = r.first_name
let last_name r = r.last_name
open Rel
let id' = Col.v "id" ~params:[Col.Primary_key] Type.Int id
let first_name' = Col.v "first_name" Type.Text first_name
let last_name' = Col.v "last_name" Type.Text last_name
let table =
Table.v "person" Row.(unit row * id' * first_name' * last_name')
end