use rusqlite::Connection;
use crate::error::Result;
pub fn create_schema(conn: &Connection) -> Result<()> {
let tx = conn.unchecked_transaction()?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_entities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL,
name TEXT NOT NULL,
properties TEXT, -- JSON
created_at INTEGER DEFAULT (strftime('%s', 'now')),
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
)
"#,
[],
)?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_relations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_id INTEGER NOT NULL,
target_id INTEGER NOT NULL,
rel_type TEXT NOT NULL,
weight REAL DEFAULT 1.0,
properties TEXT, -- JSON
created_at INTEGER DEFAULT (strftime('%s', 'now')),
FOREIGN KEY (source_id) REFERENCES kg_entities(id) ON DELETE CASCADE,
FOREIGN KEY (target_id) REFERENCES kg_entities(id) ON DELETE CASCADE
)
"#,
[],
)?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_vectors (
entity_id INTEGER NOT NULL PRIMARY KEY,
vector BLOB NOT NULL,
dimension INTEGER NOT NULL,
created_at INTEGER DEFAULT (strftime('%s', 'now')),
FOREIGN KEY (entity_id) REFERENCES kg_entities(id) ON DELETE CASCADE
)
"#,
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_entities_type ON kg_entities(entity_type)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_entities_name ON kg_entities(name)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_relations_source ON kg_relations(source_id)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_relations_target ON kg_relations(target_id)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_relations_type ON kg_relations(rel_type)",
[],
)?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_hyperedges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hyperedge_type TEXT NOT NULL,
entity_ids TEXT NOT NULL,
weight REAL DEFAULT 1.0,
arity INTEGER NOT NULL,
properties TEXT,
created_at INTEGER DEFAULT (strftime('%s', 'now')),
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
)
"#,
[],
)?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_hyperedge_entities (
hyperedge_id INTEGER NOT NULL,
entity_id INTEGER NOT NULL,
position INTEGER NOT NULL,
PRIMARY KEY (hyperedge_id, entity_id),
FOREIGN KEY (hyperedge_id) REFERENCES kg_hyperedges(id) ON DELETE CASCADE,
FOREIGN KEY (entity_id) REFERENCES kg_entities(id) ON DELETE CASCADE
)
"#,
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_hyperedges_type ON kg_hyperedges(hyperedge_type)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_hyperedges_arity ON kg_hyperedges(arity)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_he_entities_entity ON kg_hyperedge_entities(entity_id)",
[],
)?;
tx.execute(
"CREATE INDEX IF NOT EXISTS idx_he_entities_hyperedge ON kg_hyperedge_entities(hyperedge_id)",
[],
)?;
tx.execute(
r#"
CREATE TABLE IF NOT EXISTS kg_turboquant_cache (
id INTEGER PRIMARY KEY CHECK (id = 1),
index_blob BLOB NOT NULL,
vector_count INTEGER NOT NULL
)
"#,
[],
)?;
tx.commit()?;
Ok(())
}
pub fn schema_exists(conn: &Connection) -> Result<bool> {
let mut stmt = conn
.prepare("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='kg_entities'")?;
let count: i64 = stmt.query_row([], |row| row.get(0))?;
Ok(count > 0)
}