-- | The schema of the LeiosDB partition files. -- -- The immutable partition holds a subset of the volatile one: the same -- tables with fewer 'ebs' columns, and none of the volatile-only tables and -- indexes. -- -- Every statement is @IF NOT EXISTS@, so the schemas are idempotent: -- @initialiseLeiosDbFiles@ applies them on every handle creation. module Ouroboros.Consensus.Storage.LeiosDB.Impl.SQLite.Schema ( sql_schema_vol , sql_schema_imm ) where -- | Schema of the volatile partition (@leios.vol.db@): 'sql_schema_imm' with -- the columns and objects that track completeness and GC. sql_schema_vol :: String sql_schema_vol :: String sql_schema_vol = [String] -> String unlines ( [String] -> [String] ebsTable [ -- NULL = body not downloaded, >0 = txs missing, 0 = just completed, <0 = notified String " missingTxCount INTEGER," , -- 0 = volatile, 1 = certified/pinned awaiting copy, -- 2 = copied to the immutable partition (evictable), -- 3 = marked for GC, awaiting the sweeper String " status INTEGER NOT NULL DEFAULT 0," ] [String] -> [String] -> [String] forall a. Semigroup a => a -> a -> a <> [String] sharedTables ) String -> String -> String forall a. Semigroup a => a -> a -> a <> String sql_schema_gc -- | GC-only objects of the volatile partition, part of 'sql_schema_vol'. sql_schema_gc :: String sql_schema_gc :: String sql_schema_gc = [String] -> String unlines [ -- What the mark scan reads; marking removes the row from it, so -- each row is marked at most once. String "CREATE INDEX IF NOT EXISTS idx_ebs_sweepable ON ebs(ebSlot) WHERE status IN (0, 2);" , -- What the sweeper's batch pick reads. String "CREATE INDEX IF NOT EXISTS idx_ebs_markedForGc ON ebs(ebHashBytes) WHERE status = 3;" , -- Pinned EBs awaiting the copy into the immutable partition; see -- 'sql_next_pinned_eb'. String "CREATE INDEX IF NOT EXISTS idx_ebs_pinned ON ebs(ebSlot) WHERE status = 1;" ] -- | Schema of the immutable partition (@leios.imm.db@): the subset of -- 'sql_schema_vol' that the copier writes and the fallback reads use. The -- tables it keeps have the volatile columns, so the fallback reads reuse the -- volatile SQL verbatim and the copy is a server-side @INSERT ... SELECT@ -- over ATTACH. -- -- Left out, since EBs land here complete and are never collected: -- @ebs.missingTxCount@, @ebs.status@ and the GC indexes. sql_schema_imm :: String sql_schema_imm :: String sql_schema_imm = [String] -> String unlines ([String] -> String) -> [String] -> String forall a b. (a -> b) -> a -> b $ [String] -> [String] ebsTable [] [String] -> [String] -> [String] forall a. Semigroup a => a -> a -> a <> [String] sharedTables -- | The 'ebs' table and its hash index, with the given columns added after -- the ones both partitions have. -- -- The volatile columns go in here rather than through a later -- @ALTER TABLE ... ADD COLUMN@: that has no @IF NOT EXISTS@, so it would -- fail the second time the schema is applied. ebsTable :: [String] -> [String] ebsTable :: [String] -> [String] ebsTable [String] extraColumns = [ String "CREATE TABLE IF NOT EXISTS ebs (" , String " ebSlot INTEGER NOT NULL," , String " ebHashBytes BLOB NOT NULL," , String " ebBytesSize INTEGER NOT NULL," ] [String] -> [String] -> [String] forall a. Semigroup a => a -> a -> a <> [String] extraColumns [String] -> [String] -> [String] forall a. Semigroup a => a -> a -> a <> [ String " PRIMARY KEY (ebSlot, ebHashBytes)" , String ");" , -- Lookups by hash alone, e.g. 'sql_imm_filter_present'. String "CREATE INDEX IF NOT EXISTS idx_ebs_ebHashBytes ON ebs(ebHashBytes);" ] -- | The tables that are identical in both partitions. -- -- Tx bytes are owned by the referencing EB: 'ebTxBytes' shares the -- @(ebHashBytes, txOffset)@ key with 'ebTxs', so writing and evicting an EB's -- closure touches one contiguous key range, and a tx shared by two EBs is -- stored twice, deliberately. A row is allocated as a @zeroblob@ of the -- declared size when the body arrives and filled in place when the tx does -- (@filled = 1@). It never changes size, so SQLite overwrites it in place, -- without moving rows between B-tree pages. sharedTables :: [String] = [ String "CREATE TABLE IF NOT EXISTS ebTxs (" , String " ebHashBytes BLOB NOT NULL," , String " txOffset INTEGER NOT NULL," , String " txHashBytes BLOB NOT NULL," , String " txBytesSize INTEGER NOT NULL," , String " PRIMARY KEY (ebHashBytes, txOffset)" , String ");" , String "CREATE TABLE IF NOT EXISTS ebTxBytes (" , String " ebHashBytes BLOB NOT NULL," , String " txOffset INTEGER NOT NULL," , String " filled INTEGER NOT NULL DEFAULT 0," , -- The blob is the rightmost column, so probing @filled@ never touches -- overflow pages. String " txBytes BLOB NOT NULL," , String " PRIMARY KEY (ebHashBytes, txOffset)" , String ");" ]