macro_rules! ts_glob {
() => {
"[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9][0-9][0-9][0-9]Z"
};
}
macro_rules! canonical_ts_check {
($($col:literal),+ $(,)?) => {
concat!("CHECK (", $( $col, " GLOB '", ts_glob!(), "' AND ", )+ "1)")
};
}
macro_rules! weight_check {
() => {
"CHECK (weight >= 0.0 AND weight < 9e999 AND typeof(weight) = 'real')"
};
}
pub const WEIGHT_CHECK: &str = weight_check!();
macro_rules! abort_single_open {
() => {
"macrame: edge already has an open interval; retire it first"
};
}
macro_rules! abort_monotonic_ra {
() => {
"macrame: concept recorded_at must be strictly increasing"
};
}
macro_rules! abort_delete_guard {
() => {
"macrame: physical delete blocked outside archive session"
};
}
pub const ABORT_SINGLE_OPEN: &str = abort_single_open!();
pub const ABORT_MONOTONIC_RA: &str = abort_monotonic_ra!();
pub const ABORT_DELETE_GUARD: &str = abort_delete_guard!();
pub const ARCHIVE_SESSION_MARKER: &str = "macrame_archive_session";
pub const CREATE_CONCEPTS_TABLE: &str = concat!(
r#"
CREATE TABLE IF NOT EXISTS concepts (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
embedding_model TEXT,
valid_from TEXT NOT NULL,
valid_to TEXT NOT NULL DEFAULT '9999-12-31T23:59:59.999999Z',
recorded_at TEXT NOT NULL,
retired INTEGER NOT NULL DEFAULT 0,
"#,
canonical_ts_check!("valid_from", "valid_to", "recorded_at"),
r#"
);
"#
);
pub const CREATE_LINKS_TABLE: &str = concat!(
r#"
CREATE TABLE IF NOT EXISTS links (
source_id TEXT NOT NULL REFERENCES concepts(id),
target_id TEXT NOT NULL REFERENCES concepts(id),
edge_type TEXT NOT NULL,
valid_from TEXT NOT NULL,
recorded_at TEXT NOT NULL,
valid_to TEXT NOT NULL DEFAULT '9999-12-31T23:59:59.999999Z',
weight REAL NOT NULL DEFAULT 1.0,
properties TEXT NOT NULL DEFAULT '{}',
PRIMARY KEY (source_id, target_id, edge_type, valid_from, recorded_at),
"#,
weight_check!(),
r#",
"#,
canonical_ts_check!("valid_from", "valid_to", "recorded_at"),
r#"
);
"#
);
pub const CREATE_LINKS_CURRENT_TABLE: &str = concat!(
r#"
CREATE TABLE IF NOT EXISTS links_current (
source_id TEXT NOT NULL,
target_id TEXT NOT NULL,
edge_type TEXT NOT NULL,
valid_from TEXT NOT NULL,
valid_to TEXT NOT NULL,
weight REAL NOT NULL,
properties TEXT NOT NULL,
recorded_at TEXT NOT NULL,
PRIMARY KEY (source_id, target_id, edge_type, valid_from),
"#,
canonical_ts_check!("valid_from", "valid_to", "recorded_at"),
r#"
);
"#
);
pub const CREATE_TRANSACTION_LOG_TABLE: &str = concat!(
r#"
CREATE TABLE IF NOT EXISTS transaction_log (
seq_id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT NOT NULL,
entity_id TEXT NOT NULL,
operation TEXT NOT NULL,
payload TEXT NOT NULL,
recorded_at TEXT NOT NULL,
"#,
canonical_ts_check!("recorded_at"),
r#"
);
"#
);
pub fn create_embeddings_table(model: &crate::vector::ModelName, dim: usize) -> String {
format!(
"CREATE TABLE IF NOT EXISTS {table} (
concept_id TEXT PRIMARY KEY REFERENCES concepts(id),
embedding F32_BLOB({dim}) NOT NULL
);",
table = model.table(),
)
}
pub fn create_embeddings_index(model: &crate::vector::ModelName) -> String {
format!(
"CREATE INDEX IF NOT EXISTS {index} ON {table} (libsql_vector_idx(embedding));",
index = model.index(),
table = model.table(),
)
}
pub const CREATE_ANALYTICS_ANNOTATIONS_TABLE: &str = concat!(
r#"
CREATE TABLE IF NOT EXISTS analytics_annotations (
concept_id TEXT NOT NULL REFERENCES concepts(id),
label TEXT NOT NULL,
value TEXT NOT NULL,
computed_at TEXT NOT NULL,
PRIMARY KEY (concept_id, label),
"#,
canonical_ts_check!("computed_at"),
r#"
);
"#
);
pub const CREATE_CONCEPTS_FTS: &str = r#"
CREATE VIRTUAL TABLE IF NOT EXISTS concepts_fts USING fts5(
title,
content,
content='concepts',
content_rowid='rowid'
);
"#;
pub const VERIFY_CONCEPTS_FTS: &str =
"INSERT INTO concepts_fts (concepts_fts) VALUES ('integrity-check');";
pub const REBUILD_CONCEPTS_FTS: &str =
"INSERT INTO concepts_fts (concepts_fts) VALUES ('rebuild');";
pub const CREATE_INDICES: &[&str] = &[
"CREATE INDEX IF NOT EXISTS idx_annotations_label ON analytics_annotations (label);",
"CREATE INDEX IF NOT EXISTS idx_lc_traversal_cover ON links_current \
(source_id, valid_from, valid_to, weight, edge_type, target_id);",
"CREATE INDEX IF NOT EXISTS idx_lc_tgt_active ON links_current (target_id, valid_to);",
"CREATE INDEX IF NOT EXISTS idx_lc_open_interval ON links_current \
(source_id, target_id, edge_type, valid_to, valid_from);",
"CREATE INDEX IF NOT EXISTS idx_txlog_time ON transaction_log (recorded_at);",
"CREATE INDEX IF NOT EXISTS idx_txlog_entity ON transaction_log (entity_id);",
];
pub const CREATE_TRIGGERS: &[&str] = &[
r#"
CREATE TRIGGER IF NOT EXISTS trg_links_current_sync
AFTER INSERT ON links
BEGIN
INSERT INTO links_current
(source_id, target_id, edge_type, valid_from, valid_to,
weight, properties, recorded_at)
VALUES
(NEW.source_id, NEW.target_id, NEW.edge_type, NEW.valid_from,
NEW.valid_to, NEW.weight, NEW.properties, NEW.recorded_at)
ON CONFLICT(source_id, target_id, edge_type, valid_from) DO UPDATE SET
valid_to = excluded.valid_to,
weight = excluded.weight,
properties = excluded.properties,
recorded_at = excluded.recorded_at
WHERE excluded.recorded_at > links_current.recorded_at;
END;
"#,
concat!(
r#"
CREATE TRIGGER IF NOT EXISTS trg_links_single_open
BEFORE INSERT ON links
WHEN NEW.valid_to = '9999-12-31T23:59:59.999999Z'
AND EXISTS (
SELECT 1 FROM links_current
WHERE source_id = NEW.source_id
AND target_id = NEW.target_id
AND edge_type = NEW.edge_type
AND valid_from <> NEW.valid_from
AND valid_to = '9999-12-31T23:59:59.999999Z'
)
BEGIN
SELECT RAISE(ABORT, '"#,
abort_single_open!(),
r#"');
END;
"#
),
concat!(
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_monotonic_ra
BEFORE UPDATE ON concepts
WHEN NEW.recorded_at <= OLD.recorded_at
BEGIN
SELECT RAISE(ABORT, '"#,
abort_monotonic_ra!(),
r#"');
END;
"#
),
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_log_insert
AFTER INSERT ON concepts
BEGIN
INSERT INTO transaction_log (table_name, entity_id, operation, payload, recorded_at)
VALUES ('concepts', NEW.id, 'I',
json_object('v', 2, 'title', NEW.title, 'content', NEW.content,
'valid_from', NEW.valid_from, 'valid_to', NEW.valid_to,
'retired', NEW.retired,
'embedding_model', NEW.embedding_model),
NEW.recorded_at);
END;
"#,
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_log_update
AFTER UPDATE ON concepts
BEGIN
INSERT INTO transaction_log (table_name, entity_id, operation, payload, recorded_at)
VALUES ('concepts', NEW.id, 'U',
json_object('v', 2, 'title', NEW.title, 'content', NEW.content,
'valid_from', NEW.valid_from, 'valid_to', NEW.valid_to,
'retired', NEW.retired,
'embedding_model', NEW.embedding_model),
NEW.recorded_at);
END;
"#,
r#"
CREATE TRIGGER IF NOT EXISTS trg_links_log_insert
AFTER INSERT ON links
BEGIN
INSERT INTO transaction_log (table_name, entity_id, operation, payload, recorded_at)
VALUES ('links',
NEW.source_id || '|' || NEW.target_id || '|' || NEW.edge_type || '|' || NEW.valid_from,
'I',
json_object('v', 1, 'source_id', NEW.source_id, 'target_id', NEW.target_id,
'edge_type', NEW.edge_type, 'valid_from', NEW.valid_from,
'valid_to', NEW.valid_to, 'weight', NEW.weight,
'properties', json(NEW.properties)),
NEW.recorded_at);
END;
"#,
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_guard_delete
BEFORE DELETE ON concepts
BEGIN
SELECT RAISE(ABORT, 'macrame: concepts are never physically archived (D-022)');
END;
"#,
concat!(
r#"
CREATE TRIGGER IF NOT EXISTS trg_links_guard_delete
BEFORE DELETE ON links
WHEN NOT EXISTS (
SELECT 1 FROM sqlite_master
WHERE type = 'table' AND name = 'macrame_archive_session'
)
BEGIN
SELECT RAISE(ABORT, '"#,
abort_delete_guard!(),
r#"');
END;
"#
),
concat!(
r#"
CREATE TRIGGER IF NOT EXISTS trg_txlog_guard_delete
BEFORE DELETE ON transaction_log
WHEN NOT EXISTS (
SELECT 1 FROM sqlite_master
WHERE type = 'table' AND name = 'macrame_archive_session'
)
BEGIN
SELECT RAISE(ABORT, '"#,
abort_delete_guard!(),
r#"');
END;
"#
),
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_fts_insert
AFTER INSERT ON concepts
BEGIN
INSERT INTO concepts_fts (rowid, title, content)
VALUES (NEW.rowid, NEW.title, NEW.content);
END;
"#,
r#"
CREATE TRIGGER IF NOT EXISTS trg_concepts_fts_update
AFTER UPDATE ON concepts
BEGIN
INSERT INTO concepts_fts (concepts_fts, rowid, title, content)
VALUES ('delete', OLD.rowid, OLD.title, OLD.content);
INSERT INTO concepts_fts (rowid, title, content)
VALUES (NEW.rowid, NEW.title, NEW.content);
END;
"#,
];
#[cfg(test)]
mod tests {
use crate::util::timestamp::{CANONICAL_TS_GLOB, OPEN_SENTINEL};
#[test]
fn ddl_glob_matches_the_rust_canonical_pattern() {
assert_eq!(format!("'{}'", ts_glob!()), CANONICAL_TS_GLOB);
}
#[test]
fn ddl_defaults_use_the_canonical_sentinel() {
for ddl in [
super::CREATE_CONCEPTS_TABLE,
super::CREATE_LINKS_TABLE,
super::CREATE_LINKS_CURRENT_TABLE,
super::CREATE_TRANSACTION_LOG_TABLE,
] {
assert!(
!ddl.contains("9999-12-31T23:59:59Z"),
"DDL still carries the pre-0.5.4 second-precision sentinel: {ddl}"
);
}
for trigger in super::CREATE_TRIGGERS {
assert!(
!trigger.contains("9999-12-31T23:59:59Z"),
"trigger still carries the pre-0.5.4 sentinel: {trigger}"
);
}
assert!(super::CREATE_LINKS_TABLE.contains(OPEN_SENTINEL));
}
#[test]
fn every_abort_message_appears_in_a_trigger() {
for msg in [
super::ABORT_SINGLE_OPEN,
super::ABORT_MONOTONIC_RA,
super::ABORT_DELETE_GUARD,
] {
assert!(
super::CREATE_TRIGGERS.iter().any(|t| t.contains(msg)),
"no trigger emits {msg:?}, so its typed error is unreachable"
);
}
}
}