DATA MODULE OCAML "some-module"
TABLE test_table {
id INT PRIMARY KEY,
some_text TEXT DEFAULT 'hello',
}
open! Context
open! Db_types
let define_data () =
mk_test_table ~id:123 () |> def_test_table;
mk_test_table ~id:123 ~some_text:"overriden" () |> def_test_table;
()
TABLE server {
hostname TEXT PRIMARY KEY,
}
TABLE server_volume {
volume_name TEXT PRIMARY KEY CHILD OF server,
mountpoint TEXT,
}
DATA server(hostname) {
server-a WITH server_volume {
pgtest1, '/srv/volumes/pgtest1';
};
server-b WITH server_volume {
pgtest1, '/srv/volumes/pgtest1';
};
}
TABLE db_deployment {
deployment_name TEXT PRIMARY KEY,
}
TABLE db_deployment_instance {
deployment_id INT PRIMARY KEY CHILD OF db_deployment,
db_server REF FOREIGN CHILD server_volume,
CHECK { deployment_id > 0 },
}
DATA STRUCT db_deployment [
{
deployment_name: foo WITH db_deployment_instance [
{
deployment_id: 1,
db_server: server-a=>pgtest1,
},
{
deployment_id: 2,
db_server: server-b=>pgtest1,
},
]
}
]
TABLE server {
hostname TEXT PRIMARY KEY,
ssh_interface REF CHILD network_interface,
}
TABLE network {
network_name TEXT PRIMARY KEY,
cidr TEXT,
}
TABLE network_interface {
if_name TEXT PRIMARY KEY CHILD OF server,
if_network REF network,
if_ip TEXT,
if_subnet_mask_cidr INT,
}
DATA STRUCT network [
{
network_name: lan,
cidr: '10.16.0.0/12',
}
]
DATA server(hostname, ssh_interface) {
server-a, eth0 WITH network_interface {
eth0, lan, 10.17.0.10, 24;
};
}
TABLE existant_parent {
some_key TEXT PRIMARY KEY,
spec_child REF CHILD existant_child_2,
}
TABLE existant_child {
some_child_key TEXT PRIMARY KEY CHILD OF existant_parent,
}
TABLE existant_child_2 {
some_child_key_2 TEXT PRIMARY KEY CHILD OF existant_child,
}
DATA existant_parent {
outer_val, inner_val=>henloz WITH existant_child {
inner_val WITH existant_child_2 {
henlo
}
}
}
TABLE server {
hostname TEXT PRIMARY KEY,
tld REF tld DETACHED DEFAULT,
fqdn TEXT GENERATED AS { hostname .. "." .. tld },
}
TABLE tld {
domain TEXT PRIMARY KEY,
}
DEFAULTS {
// defines default for table 'server' and column 'tld'.
// you cannot define defaults for non existing tables
// and column must be marked as detached default
server.tld epl-infra.net,
}
// now we can define data with detached default
DATA STRUCT server {
hostname: server-a
}
DATA tld {
epl-infra.net;
}
| application | monitoring | database access | queues | logging | proxying | load balancing | .. | feature 1000th |
|-------------+------------+-----------------+--------+---------+----------+----------------+----+----------------|
| app 1 | | | | | | | | |
| app 2 | | | | | | | | |
| app 3 | | | | | | | | |
| ... | | | | | | | | |
| app 1000th | | | | | | | | |
TABLE server {
hostname TEXT PRIMARY KEY,
}
TABLE reserved_port {
number INT PRIMARY KEY CHILD OF server,
}
TABLE docker_container {
name TEXT PRIMARY KEY CHILD OF server,
}
TABLE docker_container_port {
port_name TEXT PRIMARY KEY CHILD OF docker_container,
reserved_port REF reserved_port,
}
DATA server {
epyc-1
}
DATA STRUCT docker_container {
hostname: epyc-1,
name: postgres
WITH docker_container_port {
port_name: main_port,
reserved_port: 5432
}
}
NonExistingForeignKey { table_with_foreign_key: "docker_container_port", foreign_key_column: "reserved_port", referred_table: "reserved_port", referred_table_column: "number", key_value: "5432" }
DATA reserved_port {
epyc-1, 5432
}
FoundDuplicateChildPrimaryKeySet { table_name: "reserved_port", columns: "(hostname, number)", duplicate_values: "(epyc-1, 5432)" }
INCLUDE LUA {
data('reserved_port', { hostname = 'epyc-1', number = 5432 })
}
__do_not_refer_to_this_internal_value_in_your_code_dumbo__ = {}
function data(targetTable, newRow)
local queue = __do_not_refer_to_this_internal_value_in_your_code_dumbo__
if queue[targetTable] == nil then
queue[targetTable] = {}
end
-- we simply accumulate values to insert in lua runtime and then process
-- them in one go in rust
table.insert(queue[targetTable], newRow)
end
INCLUDE LUA {
function docker_container(hostname, containerName, portName, portValue)
data('docker_container', {
hostname = hostname,
name = containerName,
})
data('docker_container_port', {
hostname = hostname,
name = containerName,
port_name = portName,
reserved_port = portValue
})
data('reserved_port', {
hostname = hostname,
number = portValue,
})
end
}
INCLUDE LUA {
docker_container('epyc-1', 'postgres', 'main_port', 5432)
}
INCLUDE LUA "lib.lua"
TABLE server {
hostname TEXT PRIMARY KEY,
ram_mb INT,
}
TABLE disks {
disk_id TEXT PRIMARY KEY CHILD OF server,
size_bytes INT,
}
DATA server {
my-precious-epyc1, 4096;
my-precious-epyc2, 8192;
}
INSERT INTO ...
statement.
DATA disks(hostname, disk_id, size_bytes) {
my-precious-epyc1, root-disk, 1000000000000;
my-precious-epyc2, root-disk, 1500000000000;
}
(hostname, disk_id, size_bytes)
is explicit here but as we've seen with server default tuple order is assumed if ommited.
DATA server {
my-precious-epyc1, 4096 WITH disks {
root-disk, 1000000000000;
},
my-precious-epyc2, 8192 WITH disks {
root-disk, 1500000000000;
},
}
DATA STRUCT server [
{
hostname: my-precious-epyc1, ram_mb: 4096 WITH disks {
disk_id: root-disk,
size_bytes: 1000000000000,
},
},
{
hostname: my-precious-epyc1, ram_mb: 4096 WITH disks [{
disk_id: root-disk,
size_bytes: 1500000000000,
}]
}
]
PROOF "no disks exist less than 10 gigabytes" NONE EXIST OF disks {
SELECT rowid
FROM disks
WHERE size_bytes < 10000000000
}
TABLE disks {
disk_id TEXT PRIMARY KEY CHILD OF server,
size_bytes INT,
CHECK { size_bytes >= 10000000000 }
}
TABLE disks {
disk_id TEXT PRIMARY KEY CHILD OF server,
size_bytes INT,
size_mb INT GENERATED AS { size_bytes / 1000000 },
CHECK { size_bytes >= 10000000000 }
}
PROOF "no disks exist less than 10 gigabytes, doesn't work today" NONE EXIST OF disks DATALOG {
OUTPUT(Offender) :- t_disks__size_mb(Size, Offender), Size < 10000.
}
PROOF "just check that no disks with name 'doofus' exists, works" NONE EXIST OF disks DATALOG {
OUTPUT(Offender) :- t_disks__disk_id("doofus", Offender).
}
edendb example.edl --rust-output-directory .
// Test db content
const DB_BYTES: &[u8] = include_bytes!("edb_data.bin");
lazy_static!{
pub static ref DB: Database = Database::deserialize(DB_BYTES).unwrap();
}
// Table row pointer types
#[derive(Copy, Clone, Debug, serde::Deserialize)]
pub struct TableRowPointerServer(usize);
#[derive(Copy, Clone, Debug, serde::Deserialize)]
pub struct TableRowPointerDisks(usize);
// Table struct types
#[derive(Debug)]
pub struct TableRowServer {
pub hostname: ::std::string::String,
pub ram_mb: i64,
pub children_disks: Vec<TableRowPointerDisks>,
}
#[derive(Debug)]
pub struct TableRowDisks {
pub disk_id: ::std::string::String,
pub size_bytes: i64,
pub size_mb: i64,
pub parent: TableRowPointerServer,
}
// Table definitions
pub struct TableDefinitionServer {
rows: Vec<TableRowServer>,
c_hostname: Vec<::std::string::String>,
c_ram_mb: Vec<i64>,
c_children_disks: Vec<Vec<TableRowPointerDisks>>,
}
pub struct TableDefinitionDisks {
rows: Vec<TableRowDisks>,
c_disk_id: Vec<::std::string::String>,
c_size_bytes: Vec<i64>,
c_size_mb: Vec<i64>,
c_parent: Vec<TableRowPointerServer>,
}
// Database definition
pub struct Database {
server: TableDefinitionServer,
disks: TableDefinitionDisks,
}
// Database implementation
impl Database {
pub fn server(&self) -> &TableDefinitionServer {
&self.server
}
pub fn disks(&self) -> &TableDefinitionDisks {
&self.disks
}
pub fn deserialize(compressed: &[u8]) -> Result<Database, Box<dyn ::std::error::Error>> {
// boring deserialization stuff
...
}
}
// Table definition implementations
impl TableDefinitionServer {
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn rows_iter(&self) -> impl ::std::iter::Iterator<Item = TableRowPointerServer> {
(0..self.rows.len()).map(|idx| {
TableRowPointerServer(idx)
})
}
pub fn row(&self, ptr: TableRowPointerServer) -> &TableRowServer {
&self.rows[ptr.0]
}
pub fn c_hostname(&self, ptr: TableRowPointerServer) -> &::std::string::String {
&self.c_hostname[ptr.0]
}
pub fn c_ram_mb(&self, ptr: TableRowPointerServer) -> i64 {
self.c_ram_mb[ptr.0]
}
pub fn c_children_disks(&self, ptr: TableRowPointerServer) -> &[TableRowPointerDisks] {
&self.c_children_disks[ptr.0]
}
}
impl TableDefinitionDisks {
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn rows_iter(&self) -> impl ::std::iter::Iterator<Item = TableRowPointerDisks> {
(0..self.rows.len()).map(|idx| {
TableRowPointerDisks(idx)
})
}
pub fn row(&self, ptr: TableRowPointerDisks) -> &TableRowDisks {
&self.rows[ptr.0]
}
pub fn c_disk_id(&self, ptr: TableRowPointerDisks) -> &::std::string::String {
&self.c_disk_id[ptr.0]
}
pub fn c_size_bytes(&self, ptr: TableRowPointerDisks) -> i64 {
self.c_size_bytes[ptr.0]
}
pub fn c_size_mb(&self, ptr: TableRowPointerDisks) -> i64 {
self.c_size_mb[ptr.0]
}
pub fn c_parent(&self, ptr: TableRowPointerDisks) -> TableRowPointerServer {
self.c_parent[ptr.0]
}
}
TABLE disk_manufacturer {
model TEXT PRIMARY KEY,
}
DATA EXCLUSIVE disk_manufacturer {
intel;
crucial;
}
DATA EXCLUSIVE
says, that this data can be only defined once. Otherwise, we could keep filling up this enum with many statements in different places, this effectively makes this table an enum.
TABLE disks {
disk_id TEXT PRIMARY KEY CHILD OF server,
size_bytes INT,
size_mb INT GENERATED AS { size_bytes / 1000000 },
make REF disk_manufacturer,
CHECK { size_bytes >= 10000000000 }
}
REF disk_manufacturer
means this column has to refer by primary key to a single row in disk_manufacturer, you can never add invalid disk manufacturer value in the disk table or compiler yells.
DATA STRUCT server [
{
hostname: my-precious-epyc1, ram_mb: 4096 WITH disks {
disk_id: root-disk,
size_bytes: 1000000000000,
make: intel,
},
},
{
hostname: my-precious-epyc2, ram_mb: 8192 WITH disks [{
disk_id: root-disk,
size_bytes: 1500000000000,
make: intel,
},{
disk_id: data-disk,
size_bytes: 1200000000000,
make: crucial,
}]
}
]
#[macro_use]
extern crate lazy_static;
#[allow(dead_code)]
mod database;
fn main() {
let db = &database::DB;
db.disks().rows_iter().filter(|i| {
// we only care about intel disks
db.disk_manufacturer().c_model(db.disks().c_make(*i)) == "intel"
}).for_each(|intel_disk| {
let parent = db.disks().c_parent(intel_disk);
let children_disks = db.server().c_children_disks(parent);
for pair in children_disks.windows(2) {
match (
db.disk_manufacturer().c_model(db.disks().c_make(pair[0])).as_str(),
db.disk_manufacturer().c_model(db.disks().c_make(pair[1])).as_str(),
) {
("intel", "crucial") | ("crucial", "intel") => {
panic!("Are you kidding me bro?")
}
_ => {}
}
}
});
}