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?")
}
_ => {}
}
}
});
}
struct LogMessageOfBuyingEggsInAStore {
user_id @0 :UInt32;
number @1 :UInt32;
price @2 :Float32;
}
"User {user_id} bought {number} eggs in a store for price {price}"
api.log_buying_eggs_in_a_store(price: 1.23, quantity: 7)