srtree-db-0.1.2.0: src/Algorithm/EqSat/Storage/Schema.hs
{-# LANGUAGE OverloadedStrings #-}
-- | Schema for persisting srtree e-graphs.
--
-- Two logical sections, potentially in separate DB files:
-- * E-graph section (dataset-agnostic, read-only during fitting):
-- @meta@, @enode@, @enode_child@, @eclass@, @eclass_node@,
-- @cstore_page@, @frontier@
-- * Dataset-fit section (per-dataset, write-heavy during fitting):
-- @dataset@, @dataset_fit@, @expression_index@
--
-- 'egraphSchemaSQL' is the DDL for the egraph DB.
-- 'fitSchemaSQL' is the DDL for a per-dataset fit DB (no FK to eclass).
module Algorithm.EqSat.Storage.Schema
( egraphSchemaSQL
, fitSchemaSQL
, createSchema
, createSchemaFit
) where
import Data.Text (Text)
import Algorithm.EqSat.Storage.Backend (SqlBackend(..))
-- | DDL for the egraph database.
-- Full schema including dataset tables for backward compatibility with importEqs.
egraphSchemaSQL :: [Text]
egraphSchemaSQL =
[ "CREATE TABLE IF NOT EXISTS meta ("
<> " key TEXT PRIMARY KEY,"
<> " value TEXT NOT NULL)"
, "CREATE TABLE IF NOT EXISTS enode ("
<> " key TEXT PRIMARY KEY,"
<> " op TEXT NOT NULL,"
<> " op_detail TEXT,"
<> " a INTEGER,"
<> " b INTEGER,"
<> " x REAL)"
, "CREATE TABLE IF NOT EXISTS enode_child ("
<> " enode_key TEXT NOT NULL REFERENCES enode(key) ON DELETE CASCADE,"
<> " child_eid INTEGER NOT NULL,"
<> " cnt INTEGER NOT NULL DEFAULT 1,"
<> " PRIMARY KEY (enode_key, child_eid))"
, "CREATE TABLE IF NOT EXISTS eclass ("
<> " eid INTEGER PRIMARY KEY,"
<> " canonical INTEGER NOT NULL,"
<> " height INTEGER NOT NULL DEFAULT 0)"
, "CREATE TABLE IF NOT EXISTS eclass_node ("
<> " eid INTEGER NOT NULL REFERENCES eclass(eid) ON DELETE CASCADE,"
<> " enode_key TEXT NOT NULL REFERENCES enode(key) ON DELETE CASCADE,"
<> " PRIMARY KEY (eid, enode_key))"
, "CREATE TABLE IF NOT EXISTS cstore_page ("
<> " key INTEGER PRIMARY KEY,"
<> " blob BLOB NOT NULL)"
, "CREATE TABLE IF NOT EXISTS frontier ("
<> " eid INTEGER PRIMARY KEY REFERENCES eclass(eid) ON DELETE CASCADE,"
<> " updated_at TEXT)"
, "CREATE TABLE IF NOT EXISTS dataset ("
<> " id INTEGER PRIMARY KEY,"
<> " name TEXT NOT NULL UNIQUE,"
<> " created TEXT)"
, "CREATE TABLE IF NOT EXISTS dataset_fit ("
<> " dataset_id INTEGER NOT NULL REFERENCES dataset(id) ON DELETE CASCADE,"
<> " eid INTEGER NOT NULL REFERENCES eclass(eid) ON DELETE CASCADE,"
<> " fitness REAL,"
<> " dl REAL,"
<> " theta TEXT,"
<> " size INTEGER NOT NULL DEFAULT 0,"
<> " evaluated INTEGER NOT NULL DEFAULT 0,"
<> " fitted INTEGER NOT NULL DEFAULT 0,"
<> " stale INTEGER NOT NULL DEFAULT 0,"
<> " updated_at TEXT,"
<> " PRIMARY KEY (dataset_id, eid))"
, "CREATE TABLE IF NOT EXISTS expression_index ("
<> " expression_key TEXT PRIMARY KEY,"
<> " eclass INTEGER NOT NULL REFERENCES eclass(eid) ON DELETE CASCADE,"
<> " dataset_id INTEGER REFERENCES dataset(id) ON DELETE CASCADE,"
<> " first_seen TEXT)"
]
-- | DDL for a per-dataset fit database.
-- No FK to eclass (e-graph lives in a separate DB).
fitSchemaSQL :: [Text]
fitSchemaSQL =
[ "CREATE TABLE IF NOT EXISTS dataset ("
<> " id INTEGER PRIMARY KEY,"
<> " name TEXT NOT NULL UNIQUE,"
<> " created TEXT)"
, "CREATE TABLE IF NOT EXISTS dataset_fit ("
<> " dataset_id INTEGER NOT NULL REFERENCES dataset(id) ON DELETE CASCADE,"
<> " eid INTEGER NOT NULL,"
<> " fitness REAL,"
<> " dl REAL,"
<> " theta TEXT,"
<> " size INTEGER NOT NULL DEFAULT 0,"
<> " evaluated INTEGER NOT NULL DEFAULT 0,"
<> " fitted INTEGER NOT NULL DEFAULT 0,"
<> " stale INTEGER NOT NULL DEFAULT 0,"
<> " updated_at TEXT,"
<> " PRIMARY KEY (dataset_id, eid))"
, "CREATE TABLE IF NOT EXISTS expression_index ("
<> " expression_key TEXT PRIMARY KEY,"
<> " eclass INTEGER NOT NULL,"
<> " dataset_id INTEGER REFERENCES dataset(id) ON DELETE CASCADE,"
<> " first_seen TEXT)"
]
-- | Create (or ensure) the egraph schema on the given backend.
createSchema :: SqlBackend db => db -> IO ()
createSchema = createSchemaDb
-- | Create (or ensure) the fit schema on the given backend.
createSchemaFit :: SqlBackend db => db -> IO ()
createSchemaFit = createSchemaDbFit