Skip to main content

TableSpec

Struct TableSpec 

Source
#[non_exhaustive]
pub struct TableSpec { pub name: Vec<u8>, pub prefix: Vec<u8>, pub pk: Vec<u8>, pub columns: Vec<(Vec<u8>, ValType)>, pub indexes: Vec<TableIndex>, pub orderpaths: Vec<OrderPath>, pub window: Option<WindowSpec>, pub autodeclare: usize, pub auto_added: Vec<Vec<u8>>, }
Expand description

One declared table.

A declaration callers assemble (or parse_table_declare parses) and the auto loop edits: its rules span fields — an index must name a declared column, a window needs an ascending access path — so they are checked where a table is admitted (TableSpec::validate, run by TableCatalog::create and TableSpec::compile), not per field.

use kevy_index::{IndexKind, TableIndex, TableSpec, ValType};
let mut t = TableSpec::default();
t.name = b"t".to_vec();
t.prefix = b"t:".to_vec();
t.pk = b"id".to_vec();
t.columns = vec![(b"id".to_vec(), ValType::I64), (b"at".to_vec(), ValType::I64)];
t.indexes.push(TableIndex::new("at", IndexKind::Range));
assert_eq!(t.compile()?[0].name(), b"t.at");

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§name: Vec<u8>

Unique catalog name.

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?;
assert_eq!(t.name, b"users");
assert_eq!(t.compile()?[0].name(), b"users.age", "compiled paths are named under the table");
§prefix: Vec<u8>

Key-prefix domain the table’s rows live under.

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?;
assert_eq!(t.prefix, b"user:");
assert_eq!(t.compile()?[0].prefix(), b"user:", "every path indexes the rows under it");
§pk: Vec<u8>

Primary-key column (documentation + VERIFY surface; rows are addressed by their key, exactly as today).

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?;
assert_eq!(t.pk, b"id");
§columns: Vec<(Vec<u8>, ValType)>

Declared columns with their scalar types, declaration order.

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?;
assert_eq!(t.columns, [(b"id".to_vec(), ValType::I64), (b"age".to_vec(), ValType::I64)]);
assert_eq!(t.column_type(b"age"), Some(ValType::I64));
assert_eq!(t.column_type(b"city"), None);
§indexes: Vec<TableIndex>

Declared secondary indexes.

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?;
assert_eq!(t.indexes, [TableIndex::new("age", IndexKind::Range)]);
§orderpaths: Vec<OrderPath>

Declared composite-sort paths.

let t = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range ORDERPATH oldest ON age DESC THEN id")?;
assert_eq!(t.orderpaths.len(), 1);
assert_eq!(t.compile()?.len(), 2, "one index and one order path");
§window: Option<WindowSpec>

Optional sliding hot window (WINDOW <col> SPAN <n> BUCKET <n>).

assert_eq!(declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?.window, None);
let t = declare("TABLE.DECLARE ev PREFIX ev: PK at COLUMN at i64 INDEX at range WINDOW at SPAN 3600 BUCKET 60")?;
assert_eq!(t.window, Some(WindowSpec::new("at", 3600, 60)));
§autodeclare: usize

AUTODECLARE <n>: how many paths the engine may declare for this table from observed refusals (0 = the loop is off, the default). Building is addition-safe — the worst case is bounded wasted memory; dropping stays a human act.

assert_eq!(declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range")?.autodeclare, 0, "off unless declared");
assert_eq!(declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range AUTODECLARE 3")?.autodeclare, 3);
§auto_added: Vec<Vec<u8>>

The paths the auto loop has declared, in declaration order — its spent budget, and the auto marker IDX.LIST shows. Runtime provenance, not declaration intent: equality checks that answer “is this the same declaration?” must ignore it (see Self::sans_auto).

let declared = declare("TABLE.DECLARE users PREFIX user: PK id COLUMN id i64 COLUMN age i64 INDEX age range AUTODECLARE 2")?;
// the engine added an index on id, and records that it did
let mut grown = declared.clone();
grown.indexes.push(TableIndex::new("id", IndexKind::Unique));
grown.auto_added.push(b"users.id".to_vec());
assert_eq!(grown.sans_auto(), declared, "what the human declared is unchanged");

Implementations§

Source§

impl TableSpec

Source

pub fn apply_auto(&mut self, e: &AdviseEntry) -> Option<Vec<u8>>

Apply one refused family to the table’s declaration — the auto half of the loop, shared by both faces so they cannot derive different declarations. Returns the ledger entry recorded in auto_added (path or path#field); None when the budget is spent, the shape is not one a table declaration serves (MATCH stays advise-only — a text index carries knobs the loop must not pick), the name does not ground, or it is already declared.

use kevy_index::{AdviseEntry, AdviseShape, parse_table_declare};
let mut t = parse_table_declare(&[
    b"TABLE.DECLARE", b"t", b"PREFIX", b"t:", b"PK", b"id", b"COLUMN", b"id", b"i64",
    b"COLUMN", b"at", b"i64", b"AUTODECLARE", b"1",
])?;
let e = AdviseEntry::new("t.at", AdviseShape::Range, 16);
assert_eq!(t.apply_auto(&e), Some(b"t.at".to_vec()));
assert_eq!(t.apply_auto(&e), None, "the budget of one is spent");
Source

pub fn narrow_advice(&self, margin: Option<i64>) -> Option<String>

The window-narrowing advice for one windowed table, given the smallest probe margin any query on one of its paths has shown: Some when every observed query kept more than a bucket of margin, so SPAN can shrink by the bucket-aligned amount. Advise-only — the window synthesis point; the engine never narrows on its own.

use kevy_index::parse_table_declare;
let t = parse_table_declare(&[
    b"TABLE.DECLARE", b"t", b"PREFIX", b"t:", b"PK", b"id", b"COLUMN", b"id", b"i64",
    b"COLUMN", b"at", b"i64", b"INDEX", b"at", b"range",
    b"WINDOW", b"at", b"SPAN", b"100", b"BUCKET", b"10",
])?;
assert_eq!(t.narrow_advice(None), None, "no query observed yet");
assert!(t.narrow_advice(Some(37)).is_some_and(|a| a.contains("SPAN 70")));
Source§

impl TableSpec

Source

pub fn compile(&self) -> Result<Vec<IndexSpec>, TableError>

Compile a table into its access paths: each INDEX col KIND becomes an IndexSpec named <table>.<col> on the table’s prefix (FIELD col, TYPE from the column decl, VALUES typed from the column decls); each ORDERPATH becomes a composite Range IndexSpec named <table>.<orderpath>. Pure — the SINGLE compilation both the server and the embedded store install.

Validates first, itself. The 4.0 shape took “a validated table” on trust and cashed that trust as expect("validated") — and the typed embedded face never called validate() at all, so a spec whose ORDERPATH named an undeclared column panicked in here, on a consumer’s boot path, and restart-looped their container (dogfood F9). An invariant a function needs is one it establishes: admission has exactly one authority now, and it is this signature. The wire path’s second validation costs microseconds.

use kevy_index::parse_table_declare;
let t = parse_table_declare(&[
    b"TABLE.DECLARE", b"t", b"PREFIX", b"t:", b"PK", b"id", b"COLUMN", b"id", b"i64",
    b"COLUMN", b"at", b"i64", b"INDEX", b"at", b"range",
])?;
let compiled = t.compile()?;
assert_eq!(compiled[0].name(), b"t.at");
Source

pub fn column_type(&self, col: &[u8]) -> Option<ValType>

The declared type of col, if declared.

Source

pub fn sans_auto(&self) -> TableSpec

This declaration with the auto loop’s runtime additions removed — what the human actually declared. ENSURE-style “is this the same declaration?” comparisons go through here, so paths the engine added never read as drift. Entries are path names (a whole auto index/orderpath) or path#field (an auto VALUES column on a human-declared index).

Source

pub fn validate(&self) -> Result<(), TableError>

Structural validation — every refusal named. Runs at parse time AND at catalog admission (a sidecar line re-validates on load).

Trait Implementations§

Source§

impl Clone for TableSpec

Source§

fn clone(&self) -> TableSpec

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TableSpec

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for TableSpec

Source§

fn default() -> TableSpec

Returns the “default value” for a type. Read more
Source§

impl Eq for TableSpec

Source§

impl Hash for TableSpec

Source§

fn hash<__H>(&self, state: &mut __H)
where __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for TableSpec

Source§

fn eq(&self, other: &TableSpec) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for TableSpec

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.