1
0
Fork 0
forked from dlang/cdcdb

Compare commits

...
Sign in to create a new pull request.

3 commits

8 changed files with 127 additions and 33 deletions

View file

@ -1,5 +1,11 @@
# Changelog
## [0.1.1] - 2025-09-14
### Added
- Table `labels` for snapshot labels.
### Fixed
- Improved data integrity with label normalization.
## [0.1.0] — 2025-09-13
### Added
- SQLite-backed snapshot library with content-defined chunking (FastCDC).

View file

@ -1,6 +1,12 @@
# Changelog
## [0.1.0] — 2025-09-13
## [0.1.1] - 2025-09-14
### Added
- Таблица `labels` для меток снимков.
### Fixed
- Улучшена целостность данных при нормализации меток.
## [0.1.0] - 2025-09-13
### Added
- Библиотека для снимков данных на базе SQLite с контентно-зависимым разбиением (FastCDC).
- Дедупликация по SHA-256 чанков, опциональная компрессия Zstd.

View file

@ -92,7 +92,7 @@ chmod +x ./tools/gen.d
```json
"dependencies": {
"cdcdb": "~>0.1.0"
"cdcdb": "~>0.1"
}
```
* **Build**: `dub build`.

View file

@ -89,7 +89,7 @@ chmod +x ./tools/gen.d
- **В `dub.json`**:
```json
"dependencies": {
"cdcdb": "~>0.1.0"
"cdcdb": "~>0.1"
}
```
- **Сборка**: `dub build`.

View file

@ -104,7 +104,7 @@ private:
{
SqliteResult queryResult = sql(
q{
WITH required(name) AS (VALUES ("snapshots"), ("blobs"), ("snapshot_chunks"))
WITH required(name) AS (VALUES ("snapshots"), ("blobs"), ("snapshot_chunks"), ("labels"))
SELECT name AS missing_table
FROM required
WHERE NOT EXISTS (
@ -122,11 +122,11 @@ private:
missingTables ~= row["missing_table"].to!string;
}
enforce(missingTables.length == 0 || missingTables.length == 3,
enforce(missingTables.length == 0 || missingTables.length == 4,
"Database is corrupted. Missing tables: " ~ missingTables.join(", ")
);
if (missingTables.length == 3)
if (missingTables.length == 4)
{
foreach (schemeQuery; _scheme)
{
@ -176,10 +176,14 @@ public:
auto queryResult = sql(
q{
SELECT COALESCE(
(SELECT (label = ? AND sha256 = ?)
FROM snapshots
ORDER BY created_utc DESC
LIMIT 1),
(
SELECT (s.sha256 = ?2)
FROM snapshots s
JOIN labels l ON l.id = s.label
WHERE l.name = ?1
ORDER BY s.created_utc DESC
LIMIT 1
),
0
) AS is_last;
}, label, sha256
@ -205,7 +209,10 @@ public:
mask_s,
mask_l,
status
) VALUES (?,?,?,?,?,?,?,?,?,?)
)
SELECT
(SELECT id FROM labels WHERE name = ?),
?,?,?,?,?,?,?,?,?
RETURNING id
},
snapshot.label,
@ -247,6 +254,18 @@ public:
return !queryResult.empty();
}
bool addLabel(string name)
{
auto queryResult = sql(
q{
INSERT INTO labels (name) VALUES (?)
ON CONFLICT(name) DO NOTHING
}, name
);
return !queryResult.empty();
}
bool addSnapshotChunk(DBSnapshotChunk snapshotChunk)
{
auto queryResult = sql(
@ -268,9 +287,22 @@ public:
{
auto queryResult = sql(
q{
SELECT id, label, sha256, description, created_utc, source_length,
algo_min, algo_normal, algo_max, mask_s, mask_l, status
FROM snapshots WHERE id = ?
SELECT
s.id,
l.name label,
s.sha256,
s.description,
s.created_utc,
s.source_length,
s.algo_min,
s.algo_normal,
s.algo_max,
s.mask_s,
s.mask_l,
s.status
FROM snapshots s
JOIN labels l ON l.id = s.label
WHERE s.id = ?
}, id
);
@ -301,9 +333,22 @@ public:
{
auto queryResult = sql(
q{
SELECT id, label, sha256, description, created_utc, source_length,
algo_min, algo_normal, algo_max, mask_s, mask_l, status
FROM snapshots WHERE (length(?) = 0 OR label = ?1);
SELECT
s.id,
l.name label,
s.sha256,
s.description,
s.created_utc,
s.source_length,
s.algo_min,
s.algo_normal,
s.algo_max,
s.mask_s,
s.mask_l,
s.status
FROM snapshots s
JOIN labels l ON l.id = s.label AND (length(?) = 0 OR l.name = ?1)
ORDER BY s.created_utc, s.id;
}, label
);
@ -379,7 +424,7 @@ public:
auto queryResult = sql(
q{
DELETE FROM snapshots
WHERE label = ?
WHERE label = (SELECT id FROM labels WHERE name = ?)
RETURNING 1;
}, label);

View file

@ -1,4 +1,20 @@
auto _scheme = [
q{
-- ------------------------------------------------------------
-- Таблица labels
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS labels (
-- идентификатор метки
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- имя метки
name TEXT NOT NULL UNIQUE
)
},
q{
-- Индекс по имени метки
CREATE INDEX IF NOT EXISTS idx_labels_name
ON labels(name)
},
q{
-- ------------------------------------------------------------
-- Таблица snapshots
@ -7,7 +23,7 @@ auto _scheme = [
-- идентификатор снимка
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- метка/название снимка
label TEXT NOT NULL,
label INTEGER NOT NULL,
-- SHA-256 всего файла (BLOB(32))
sha256 BLOB NOT NULL CHECK (length(sha256) = 32),
-- Комментарий/описание
@ -28,7 +44,11 @@ auto _scheme = [
mask_l INTEGER NOT NULL,
-- 0=pending, 1=ready
status INTEGER NOT NULL DEFAULT 0
CHECK (status IN (0,1))
CHECK (status IN (0,1)),
FOREIGN KEY (label)
REFERENCES labels(id)
ON UPDATE CASCADE
ON DELETE CASCADE
)
},
q{
@ -249,5 +269,20 @@ auto _scheme = [
BEGIN
SELECT RAISE(ABORT, "blobs: разрешён UPDATE только полей last_seen_utc и refcount");
END
},
q{
-- ------------------------------------------------------------
-- Удаление записи из labels, если удалён последний snapshot
-- ------------------------------------------------------------
CREATE TRIGGER IF NOT EXISTS trg_snapshots_delete_label
AFTER DELETE ON snapshots
FOR EACH ROW
BEGIN
DELETE FROM labels
WHERE id = OLD.label
AND NOT EXISTS (
SELECT 1 FROM snapshots WHERE label = OLD.label
);
END;
}
];

View file

@ -112,18 +112,6 @@ public:
if (_db.isLast(label, sha256))
return null;
DBSnapshot dbSnapshot;
dbSnapshot.label = label;
dbSnapshot.sha256 = sha256;
dbSnapshot.description = description;
dbSnapshot.sourceLength = data.length;
dbSnapshot.algoMin = _minSize;
dbSnapshot.algoNormal = _normalSize;
dbSnapshot.algoMax = _maxSize;
dbSnapshot.maskS = _maskS;
dbSnapshot.maskL = _maskL;
_db.beginImmediate();
bool ok;
@ -138,6 +126,20 @@ public:
_db.commit();
}
_db.addLabel(label);
DBSnapshot dbSnapshot;
dbSnapshot.label = label;
dbSnapshot.sha256 = sha256;
dbSnapshot.description = description;
dbSnapshot.sourceLength = data.length;
dbSnapshot.algoMin = _minSize;
dbSnapshot.algoNormal = _normalSize;
dbSnapshot.algoMax = _maxSize;
dbSnapshot.maskS = _maskS;
dbSnapshot.maskL = _maskL;
auto idSnapshot = _db.addSnapshot(dbSnapshot);
DBSnapshotChunk dbSnapshotChunk;

View file

@ -1,3 +1,3 @@
module cdcdb.version_;
enum cdcdbVersion = "0.1.0";
enum cdcdbVersion = "0.1.1";