use crate::RowSource;
pub trait SysRowSink {
fn begin_row(&mut self, pk: &[u128], weight: i64);
fn put_u64(&mut self, v: u64);
fn put_string(&mut self, s: &str);
fn put_bytes(&mut self, b: &[u8]);
fn end_row(&mut self);
}
pub trait SysRow {
const FAMILY: u64;
fn write(&self, sink: &mut (impl SysRowSink + ?Sized), weight: i64);
}
macro_rules! sys_row {
(
$(#[$meta:meta])*
$FAMILY:ident, $shape:ident, $Row:ident $(<$lt:lifetime>)?, $Slot:ident -> $E:ty,
key { $($k:ident),+ $(,)? }
payload { $($(#[$pmeta:meta])* $p:ident: $tc:ident),+ $(,)? }
) => {
pub(crate) const $shape: crate::catalog::SysShape = crate::catalog::SysShape {
cols: &[
$(crate::catalog::col(stringify!($k), crate::TypeCode::U64),)+
$(crate::catalog::col(stringify!($p), crate::TypeCode::$tc),)+
],
key_len: [$(stringify!($k)),+].len(),
};
$(#[$meta])*
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct $Row $(<$lt>)? {
$(pub $k: u64,)+
$($(#[$pmeta])* pub $p: sys_row!(@ty $tc),)+
}
impl $(<$lt>)? SysRow for $Row $(<$lt>)? {
const FAMILY: u64 = crate::$FAMILY;
fn write(&self, sink: &mut (impl SysRowSink + ?Sized), weight: i64) {
sink.begin_row(&[$(self.$k as u128),+], weight);
$(sys_row!(@put sink, self.$p, $tc);)+
sink.end_row();
}
}
#[doc = concat!("Payload slots of [`", stringify!($Row), "`], in column order.")]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub enum $Slot {
$($(#[$pmeta])* $p,)+
}
impl $(<$lt>)? $Row $(<$lt>)? {
pub fn read<S: RowSource>(src: &$($lt)? S, row: usize) -> Result<Self, $E> {
let mut key = src
.get_pk_bytes(row)
.as_chunks::<8>()
.0
.iter()
.map(|w| u64::from_be_bytes(*w));
Ok($Row {
$($k: key.next().expect("a system key is one u64 word per key column"),)+
$($p: sys_row!(@get src, row, $Slot::$p as usize, $p, $tc),)+
})
}
}
};
(@ty U64) => { u64 };
(@ty String) => { &'a str };
(@ty Blob) => { &'a [u8] };
(@get $s:ident, $r:ident, $pi:expr, $p:ident, U64) => { crate::payload_u64($s, $r, $pi) };
(@get $s:ident, $r:ident, $pi:expr, $p:ident, Blob) => { crate::payload_bytes($s, $r, $pi) };
(@get $s:ident, $r:ident, $pi:expr, $p:ident, String) => {
crate::payload_str($s, $r, $pi).map_err(|_| format!("{} is not UTF-8", stringify!($p)))?
};
(@put $s:ident, $v:expr, U64) => { $s.put_u64($v) };
(@put $s:ident, $v:expr, String) => { $s.put_string($v) };
(@put $s:ident, $v:expr, Blob) => { $s.put_bytes($v) };
}
sys_row! {
SCHEMA_TAB, SCHEMA_TAB_SHAPE, SchemaTabRow<'a>, SchemaTabSlot -> String,
key { schema_id }
payload { name: String }
}
sys_row! {
TABLE_TAB, TABLE_TAB_SHAPE, TableTabRow<'a>, TableTabSlot -> String,
key { table_id }
payload {
schema_id: U64,
name: String,
pk_col_idx: U64,
flags: U64,
}
}
sys_row! {
VIEW_TAB, VIEW_TAB_SHAPE, ViewTabRow<'a>, ViewTabSlot -> String,
key { view_id }
payload {
schema_id: U64,
name: String,
pk_col_idx: U64,
capacity_bytes: U64,
delta_bytes: U64,
owner_view_id: U64,
pk_repeats: U64,
}
}
sys_row! {
COL_TAB, COL_TAB_SHAPE, ColTabRow<'a>, ColTabSlot -> String,
key { owner_id, col_idx }
payload {
name: String,
type_code: U64,
is_nullable: U64,
fk_table_id: U64,
fk_col_idx: U64,
is_hidden: U64,
scale: U64,
}
}
sys_row! {
IDX_TAB, IDX_TAB_SHAPE, IdxTabRow<'a>, IdxTabSlot -> String,
key { index_id }
payload {
owner_id: U64,
source_col_idx: U64,
name: String,
is_unique: U64,
}
}
sys_row! {
SEQ_TAB, SEQ_TAB_SHAPE, SeqTabRow, SeqTabSlot -> std::convert::Infallible,
key { seq_id }
payload { next_val: U64 }
}
sys_row! {
CIRCUIT_TAB, CIRCUIT_TAB_SHAPE, CircuitRow<'a>, CircuitSlot -> std::convert::Infallible,
key { view_id }
payload {
circuit: Blob,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FkRef {
pub table_id: u64,
pub col: u32,
}
impl<'a> ColTabRow<'a> {
pub fn of(owner_id: u64, col_idx: u64, col: &'a crate::ColumnDef, fk: Option<FkRef>) -> Self {
let (fk_table_id, fk_col_idx) = fk.map_or((0, 0), |fk| (fk.table_id, fk.col as u64));
ColTabRow {
owner_id,
col_idx,
name: &col.name,
type_code: col.ty.tc.as_wire() as u64,
is_nullable: col.is_nullable as u64,
fk_table_id,
fk_col_idx,
is_hidden: col.is_hidden as u64,
scale: col.ty.scale as u64,
}
}
pub fn column(&self) -> Result<(crate::ColumnDef, Option<FkRef>), String> {
let word = |field: &str, w: u64, max: u64| {
if w > max {
return Err(format!(
"column record carries {field} = {w}, past the {max} its stored width holds"
));
}
Ok(w)
};
let flag = |field: &str, w: u64| word(field, w, 1).map(|w| w == 1);
let code = word("type_code", self.type_code, u8::MAX as u64)? as u8;
let scale = word("scale", self.scale, u8::MAX as u64)? as u8;
let ty = crate::ColType::from_wire(code, scale)
.ok_or_else(|| format!("column record carries an invalid column type {code}/{scale}"))?;
let fk_col = word("fk_col_idx", self.fk_col_idx, u32::MAX as u64)? as u32;
let fk = match (self.fk_table_id, fk_col) {
(0, 0) => None,
(0, col) => return Err(format!("column record carries FK column {col} with no FK table")),
(table_id, col) => Some(FkRef { table_id, col }),
};
let def = crate::ColumnDef {
name: self.name.to_owned(),
ty,
is_nullable: flag("is_nullable", self.is_nullable)?,
is_hidden: flag("is_hidden", self.is_hidden)?,
};
Ok((def, fk))
}
}
#[cfg(test)]
#[path = "tests/sys_rows.rs"]
mod tests;