#[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
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: usizeAUTODECLARE <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
impl TableSpec
Sourcepub fn apply_auto(&mut self, e: &AdviseEntry) -> Option<Vec<u8>>
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");Sourcepub fn narrow_advice(&self, margin: Option<i64>) -> Option<String>
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
impl TableSpec
Sourcepub fn compile(&self) -> Result<Vec<IndexSpec>, TableError>
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");Sourcepub fn column_type(&self, col: &[u8]) -> Option<ValType>
pub fn column_type(&self, col: &[u8]) -> Option<ValType>
The declared type of col, if declared.
Sourcepub fn sans_auto(&self) -> TableSpec
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).
Sourcepub fn validate(&self) -> Result<(), TableError>
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).