cdcdb/source/cdcdb/scheme.d

253 lines
8.9 KiB
D
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

auto _scheme = [
q{
-- ------------------------------------------------------------
-- Таблица snapshots
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS snapshots (
-- идентификатор снимка
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- метка/название снимка
label TEXT NOT NULL,
-- SHA-256 всего файла (BLOB(32))
sha256 BLOB NOT NULL CHECK (length(sha256) = 32),
-- Комментарий/описание
description TEXT DEFAULT NULL,
-- время создания (UTC)
created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
-- длина исходного файла в байтах
source_length INTEGER NOT NULL,
-- FastCDC: минимальный размер чанка
algo_min INTEGER NOT NULL,
-- FastCDC: целевой размер чанка
algo_normal INTEGER NOT NULL,
-- FastCDC: максимальный размер чанка
algo_max INTEGER NOT NULL,
-- FastCDC: маска S
mask_s INTEGER NOT NULL,
-- FastCDC: маска L
mask_l INTEGER NOT NULL,
-- 0=pending, 1=ready
status INTEGER NOT NULL DEFAULT 0
CHECK (status IN (0,1))
)
},
q{
-- ------------------------------------------------------------
-- Таблица blobs
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS blobs (
-- SHA-256 исходного содержимого (BLOB(32))
sha256 BLOB PRIMARY KEY CHECK (length(sha256) = 32),
-- SHA-256 сжатого содержимого (BLOB(32)) или NULL
z_sha256 BLOB,
-- размер исходного содержимого, байт
size INTEGER NOT NULL,
-- размер сжатого содержимого, байт
z_size INTEGER NOT NULL,
-- байты (сжатые при zstd=1, иначе исходные)
content BLOB NOT NULL,
-- время создания записи (UTC)
created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
-- время последней ссылки (UTC)
last_seen_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
-- число ссылок из snapshot_chunks
refcount INTEGER NOT NULL DEFAULT 0,
-- 0=нет сжатия, 1=zstd
zstd INTEGER NOT NULL DEFAULT 0
CHECK (zstd IN (0,1)),
CHECK (refcount >= 0),
CHECK (
(zstd = 1 AND length(content) = z_size) OR
(zstd = 0 AND length(content) = size)
),
CHECK (z_sha256 IS NULL OR length(z_sha256) = 32)
)
},
q{
-- ------------------------------------------------------------
-- Таблица snapshot_chunks
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS snapshot_chunks (
-- FK -> snapshots.id
snapshot_id INTEGER NOT NULL,
-- порядковый номер чанка в снимке
chunk_index INTEGER NOT NULL,
-- смещение чанка в исходном файле, байт
offset INTEGER,
-- FK -> blobs.sha256 (BLOB(32))
sha256 BLOB NOT NULL,
PRIMARY KEY (snapshot_id, chunk_index),
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE,
FOREIGN KEY (sha256)
REFERENCES blobs(sha256)
ON UPDATE RESTRICT
ON DELETE RESTRICT
)
},
q{
-- Индекс для запросов вида: WHERE label=? AND sha256=?
CREATE INDEX IF NOT EXISTS idx_snapshots_path_sha
ON snapshots(label, sha256)
},
q{
-- Индекс для обратного поиска использования blob по sha256
CREATE INDEX IF NOT EXISTS idx_snapshot_chunks_sha
ON snapshot_chunks(sha256)
},
// ------------------------------------------------------------
// Триггеры на поддержание refcount и статуса снимка
// ------------------------------------------------------------
q{
-- AFTER INSERT: увеличить refcount и обновить last_seen_utc
CREATE TRIGGER IF NOT EXISTS trg_snapshot_chunks_ai
AFTER INSERT ON snapshot_chunks
BEGIN
UPDATE blobs
SET refcount = refcount + 1,
last_seen_utc = CURRENT_TIMESTAMP
WHERE sha256 = NEW.sha256;
END
},
q{
-- AFTER DELETE: уменьшить refcount и удалить blob при refcount <= 0
CREATE TRIGGER IF NOT EXISTS trg_snapshot_chunks_ad
AFTER DELETE ON snapshot_chunks
BEGIN
UPDATE blobs
SET refcount = refcount - 1
WHERE sha256 = OLD.sha256;
DELETE FROM blobs
WHERE sha256 = OLD.sha256
AND refcount <= 0;
END
},
q{
-- AFTER UPDATE OF sha256: корректировка счётчиков при смене ссылки
CREATE TRIGGER IF NOT EXISTS trg_snapshot_chunks_au
AFTER UPDATE OF sha256 ON snapshot_chunks
FOR EACH ROW
WHEN NEW.sha256 <> OLD.sha256
BEGIN
UPDATE blobs
SET refcount = refcount - 1
WHERE sha256 = OLD.sha256;
DELETE FROM blobs
WHERE sha256 = OLD.sha256
AND refcount <= 0;
UPDATE blobs
SET refcount = refcount + 1,
last_seen_utc = CURRENT_TIMESTAMP
WHERE sha256 = NEW.sha256;
END
},
q{
-- AFTER INSERT: установить status=1 при совпадении суммы размеров с source_length
CREATE TRIGGER IF NOT EXISTS trg_snapshots_mark_ready
AFTER INSERT ON snapshot_chunks
BEGIN
UPDATE snapshots
SET status = 1
WHERE id = NEW.snapshot_id
AND (SELECT COALESCE(SUM(b.size),0)
FROM snapshot_chunks sc
JOIN blobs b ON b.sha256 = sc.sha256
WHERE sc.snapshot_id = NEW.snapshot_id)
= (SELECT source_length FROM snapshots WHERE id = NEW.snapshot_id);
END
},
q{
-- AFTER DELETE: установить status=0 для снимка, из которого удалён чанк
CREATE TRIGGER IF NOT EXISTS trg_snapshots_mark_pending
AFTER DELETE ON snapshot_chunks
BEGIN
UPDATE snapshots
SET status = 0
WHERE id = OLD.snapshot_id;
END
},
q{
-- Проверка порядка индексов и непрерывности смещений.
CREATE TRIGGER IF NOT EXISTS trg_sc_before_insert
BEFORE INSERT ON snapshot_chunks
BEGIN
-- Ожидаемое значение: max(chunk_index)+1 для данного snapshot_id (или текущий при первой вставке).
SELECT CASE
WHEN NEW.chunk_index <> COALESCE(
(SELECT MAX(chunk_index) FROM snapshot_chunks WHERE snapshot_id = NEW.snapshot_id),
NEW.chunk_index - 1
) + 1
THEN RAISE(ABORT, "snapshot_chunks: индекс chunk_index должен быть непрерывным и только возрастающим")
END;
-- Проверка: offset равен сумме размеров предыдущих чанков.
SELECT CASE
WHEN NEW.offset <> (
SELECT COALESCE(SUM(b.size), 0)
FROM snapshot_chunks sc
JOIN blobs b ON b.sha256 = sc.sha256
WHERE sc.snapshot_id = NEW.snapshot_id
AND sc.chunk_index < NEW.chunk_index
)
THEN RAISE(ABORT, "snapshot_chunks: offset должен равняться сумме размеров предыдущих чанков")
END;
END
},
q{
-- Запрет обновления строк состава; использовать DELETE + INSERT.
CREATE TRIGGER IF NOT EXISTS trg_sc_block_update
BEFORE UPDATE ON snapshot_chunks
BEGIN
SELECT RAISE(ABORT, "snapshot_chunks: UPDATE запрещён");
END
},
q{
-- ------------------------------------------------------------
-- snapshots: можно менять только status
-- ------------------------------------------------------------
CREATE TRIGGER IF NOT EXISTS trg_snapshots_block_update
BEFORE UPDATE ON snapshots
FOR EACH ROW
WHEN
NEW.id IS NOT OLD.id OR
NEW.label IS NOT OLD.label OR
NEW.sha256 IS NOT OLD.sha256 OR
NEW.description IS NOT OLD.description OR
NEW.created_utc IS NOT OLD.created_utc OR
NEW.source_length IS NOT OLD.source_length OR
NEW.algo_min IS NOT OLD.algo_min OR
NEW.algo_normal IS NOT OLD.algo_normal OR
NEW.algo_max IS NOT OLD.algo_max OR
NEW.mask_s IS NOT OLD.mask_s OR
NEW.mask_l IS NOT OLD.mask_l
-- status менять разрешено, поэтому его не сравниваем
BEGIN
SELECT RAISE(ABORT, "snapshots: разрешён UPDATE только поля status");
END
},
q{
-- ------------------------------------------------------------
-- blobs: можно менять только last_seen_utc и refcount
-- ------------------------------------------------------------
CREATE TRIGGER IF NOT EXISTS trg_blobs_block_update
BEFORE UPDATE ON blobs
FOR EACH ROW
WHEN
NEW.sha256 IS NOT OLD.sha256 OR
NEW.z_sha256 IS NOT OLD.z_sha256 OR
NEW.size IS NOT OLD.size OR
NEW.z_size IS NOT OLD.z_size OR
NEW.content IS NOT OLD.content OR
NEW.created_utc IS NOT OLD.created_utc OR
NEW.zstd IS NOT OLD.zstd
-- last_seen_utc и refcount менять разрешено
BEGIN
SELECT RAISE(ABORT, "blobs: разрешён UPDATE только полей last_seen_utc и refcount");
END
}
];