Добавлен таймаут для запросов в БД. Запрет на обновление таблиц в БД, кроме разрешенных полей
This commit is contained in:
		
							parent
							
								
									4f735abae7
								
							
						
					
					
						commit
						9ba43b0e38
					
				
					 5 changed files with 121 additions and 44 deletions
				
			
		| 
						 | 
					@ -32,13 +32,15 @@ public:
 | 
				
			||||||
	this(
 | 
						this(
 | 
				
			||||||
		string database,
 | 
							string database,
 | 
				
			||||||
		bool zstd = false,
 | 
							bool zstd = false,
 | 
				
			||||||
 | 
							int busyTimeout = 3000,
 | 
				
			||||||
 | 
							ubyte maxRetries = 3,
 | 
				
			||||||
		size_t minSize = 256,
 | 
							size_t minSize = 256,
 | 
				
			||||||
		size_t normalSize = 512,
 | 
							size_t normalSize = 512,
 | 
				
			||||||
		size_t maxSize = 1024,
 | 
							size_t maxSize = 1024,
 | 
				
			||||||
		size_t maskS = 0xFF,
 | 
							size_t maskS = 0xFF,
 | 
				
			||||||
		size_t maskL = 0x0F
 | 
							size_t maskL = 0x0F
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		_db = new DBLite(database);
 | 
							_db = new DBLite(database, busyTimeout, maxRetries);
 | 
				
			||||||
		_zstd = zstd;
 | 
							_zstd = zstd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_minSize = minSize;
 | 
							_minSize = minSize;
 | 
				
			||||||
| 
						 | 
					@ -50,20 +52,18 @@ public:
 | 
				
			||||||
		_cdc = new CDC(_minSize, _normalSize, _maxSize, _maskS, _maskL);
 | 
							_cdc = new CDC(_minSize, _normalSize, _maxSize, _maskS, _maskL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t newSnapshot(string filePath, const(ubyte)[] data, string label = string.init)
 | 
						size_t newSnapshot(string label, const(ubyte)[] data, string description = string.init)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ubyte[32] hashSource = digest!SHA256(data);
 | 
							ubyte[32] sha256 = digest!SHA256(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Если последний снимок файла соответствует текущему
 | 
							// Если последний снимок файла соответствует текущему состоянию
 | 
				
			||||||
		if (_db.isLast(filePath, hashSource)) return 0;
 | 
							if (_db.isLast(label, sha256)) return 0;
 | 
				
			||||||
		// Разбить на фрагменты
 | 
					 | 
				
			||||||
		auto chunks = _cdc.split(data);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Snapshot snapshot;
 | 
							Snapshot snapshot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		snapshot.filePath = filePath;
 | 
					 | 
				
			||||||
		snapshot.fileSha256 = hashSource;
 | 
					 | 
				
			||||||
		snapshot.label = label;
 | 
							snapshot.label = label;
 | 
				
			||||||
 | 
							snapshot.sha256 = sha256;
 | 
				
			||||||
 | 
							snapshot.description = description;
 | 
				
			||||||
		snapshot.sourceLength = data.length;
 | 
							snapshot.sourceLength = data.length;
 | 
				
			||||||
		snapshot.algoMin = _minSize;
 | 
							snapshot.algoMin = _minSize;
 | 
				
			||||||
		snapshot.algoNormal = _normalSize;
 | 
							snapshot.algoNormal = _normalSize;
 | 
				
			||||||
| 
						 | 
					@ -92,6 +92,9 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		blob.zstd = _zstd;
 | 
							blob.zstd = _zstd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Разбить на фрагменты
 | 
				
			||||||
 | 
							auto chunks = _cdc.split(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Запись фрагментов в БД
 | 
							// Запись фрагментов в БД
 | 
				
			||||||
		foreach (chunk; chunks)
 | 
							foreach (chunk; chunks)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -150,7 +153,7 @@ public:
 | 
				
			||||||
			enforce(chunk.size == bytes.length, "Оригинальный размер не соответствует ожидаемому");
 | 
								enforce(chunk.size == bytes.length, "Оригинальный размер не соответствует ожидаемому");
 | 
				
			||||||
			content ~= bytes;
 | 
								content ~= bytes;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		enforce(snapshot.fileSha256 == digest!SHA256(content), "Хеш-сумма файла не совпадает");
 | 
							enforce(snapshot.sha256 == digest!SHA256(content), "Хеш-сумма файла не совпадает");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return content;
 | 
							return content;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,18 +7,45 @@ import arsd.sqlite;
 | 
				
			||||||
import std.file : exists;
 | 
					import std.file : exists;
 | 
				
			||||||
import std.exception : enforce;
 | 
					import std.exception : enforce;
 | 
				
			||||||
import std.conv : to;
 | 
					import std.conv : to;
 | 
				
			||||||
import std.string : join, replace;
 | 
					import std.string : join, replace, toLower;
 | 
				
			||||||
 | 
					import std.algorithm : canFind;
 | 
				
			||||||
 | 
					import std.format : format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final class DBLite : Sqlite
 | 
					final class DBLite : Sqlite
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	string _dbPath;
 | 
						string _dbPath;
 | 
				
			||||||
 | 
						ubyte _maxRetries;
 | 
				
			||||||
	// _scheme
 | 
						// _scheme
 | 
				
			||||||
	mixin(import("scheme.d"));
 | 
						mixin(import("scheme.d"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SqliteResult sql(T...)(string queryText, T args)
 | 
						SqliteResult sql(T...)(string queryText, T args)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return cast(SqliteResult) query(queryText, args);
 | 
							if (_maxRetries == 0) {
 | 
				
			||||||
 | 
								return cast(SqliteResult) query(queryText, args);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							string msg;
 | 
				
			||||||
 | 
							ubyte tryNo = _maxRetries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (tryNo) {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									return cast(SqliteResult) query(queryText, args);
 | 
				
			||||||
 | 
								} catch (DatabaseException e) {
 | 
				
			||||||
 | 
									msg = e.msg;
 | 
				
			||||||
 | 
									if (msg.toLower.canFind("locked", "busy")) {
 | 
				
			||||||
 | 
										if (--tryNo == 0) {
 | 
				
			||||||
 | 
											throw new Exception(
 | 
				
			||||||
 | 
												"Не удалось выполнить подключение к базе данных после %d неудачных попыток: %s"
 | 
				
			||||||
 | 
												.format(_maxRetries, msg)
 | 
				
			||||||
 | 
											);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							throw new Exception(msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Проверка БД на наличие существующих в ней необходимых таблиц
 | 
						// Проверка БД на наличие существующих в ней необходимых таблиц
 | 
				
			||||||
| 
						 | 
					@ -64,31 +91,34 @@ private:
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	this(string database)
 | 
						this(string database, int busyTimeout, ubyte maxRetries)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		_dbPath = database;
 | 
							_dbPath = database;
 | 
				
			||||||
		super(database);
 | 
							super(database);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		check();
 | 
							check();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_maxRetries = maxRetries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		query("PRAGMA journal_mode=WAL");
 | 
							query("PRAGMA journal_mode=WAL");
 | 
				
			||||||
		query("PRAGMA synchronous=NORMAL");
 | 
							query("PRAGMA synchronous=NORMAL");
 | 
				
			||||||
		query("PRAGMA foreign_keys=ON");
 | 
							query("PRAGMA foreign_keys=ON");
 | 
				
			||||||
 | 
							query("PRAGMA busy_timeout=%d".format(busyTimeout));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void beginImmediate()
 | 
						void beginImmediate()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		query("BEGIN IMMEDIATE");
 | 
							sql("BEGIN IMMEDIATE");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void commit()
 | 
						void commit()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		query("COMMIT");
 | 
							sql("COMMIT");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void rollback()
 | 
						void rollback()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		query("ROLLBACK");
 | 
							sql("ROLLBACK");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	long addSnapshot(Snapshot snapshot)
 | 
						long addSnapshot(Snapshot snapshot)
 | 
				
			||||||
| 
						 | 
					@ -96,9 +126,9 @@ public:
 | 
				
			||||||
		auto queryResult = sql(
 | 
							auto queryResult = sql(
 | 
				
			||||||
			q{
 | 
								q{
 | 
				
			||||||
				INSERT INTO snapshots(
 | 
									INSERT INTO snapshots(
 | 
				
			||||||
					file_path,
 | 
					 | 
				
			||||||
					file_sha256,
 | 
					 | 
				
			||||||
					label,
 | 
										label,
 | 
				
			||||||
 | 
										sha256,
 | 
				
			||||||
 | 
										description,
 | 
				
			||||||
					source_length,
 | 
										source_length,
 | 
				
			||||||
					algo_min,
 | 
										algo_min,
 | 
				
			||||||
					algo_normal,
 | 
										algo_normal,
 | 
				
			||||||
| 
						 | 
					@ -109,9 +139,9 @@ public:
 | 
				
			||||||
				) VALUES (?,?,?,?,?,?,?,?,?,?)
 | 
									) VALUES (?,?,?,?,?,?,?,?,?,?)
 | 
				
			||||||
				RETURNING id
 | 
									RETURNING id
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			snapshot.filePath,
 | 
								snapshot.label,
 | 
				
			||||||
			snapshot.fileSha256[],
 | 
								snapshot.sha256[],
 | 
				
			||||||
			snapshot.label.length ? snapshot.label : null,
 | 
								snapshot.description.length ? snapshot.description : null,
 | 
				
			||||||
			snapshot.sourceLength,
 | 
								snapshot.sourceLength,
 | 
				
			||||||
			snapshot.algoMin,
 | 
								snapshot.algoMin,
 | 
				
			||||||
			snapshot.algoNormal,
 | 
								snapshot.algoNormal,
 | 
				
			||||||
| 
						 | 
					@ -157,17 +187,17 @@ public:
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool isLast(string filePath, ubyte[] fileSha256) {
 | 
						bool isLast(string label, ubyte[] sha256) {
 | 
				
			||||||
		auto queryResult = sql(
 | 
							auto queryResult = sql(
 | 
				
			||||||
			q{
 | 
								q{
 | 
				
			||||||
				SELECT COALESCE(
 | 
									SELECT COALESCE(
 | 
				
			||||||
					(SELECT (file_path = ? AND file_sha256 = ?)
 | 
										(SELECT (label = ? AND sha256 = ?)
 | 
				
			||||||
					FROM snapshots
 | 
										FROM snapshots
 | 
				
			||||||
					ORDER BY created_utc DESC
 | 
										ORDER BY created_utc DESC
 | 
				
			||||||
					LIMIT 1),
 | 
										LIMIT 1),
 | 
				
			||||||
					0
 | 
										0
 | 
				
			||||||
				) AS is_last;
 | 
									) AS is_last;
 | 
				
			||||||
			}, filePath, fileSha256
 | 
								}, label, sha256
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!queryResult.empty())
 | 
							if (!queryResult.empty())
 | 
				
			||||||
| 
						 | 
					@ -175,14 +205,14 @@ public:
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Snapshot[] getSnapshots(string filePath)
 | 
						Snapshot[] getSnapshots(string label)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto queryResult = sql(
 | 
							auto queryResult = sql(
 | 
				
			||||||
			q{
 | 
								q{
 | 
				
			||||||
				SELECT id, file_path, file_sha256, label, created_utc, source_length,
 | 
									SELECT id, label, sha256, description, created_utc, source_length,
 | 
				
			||||||
					algo_min, algo_normal, algo_max, mask_s, mask_l, status
 | 
										algo_min, algo_normal, algo_max, mask_s, mask_l, status
 | 
				
			||||||
				FROM snapshots WHERE file_path = ?
 | 
									FROM snapshots WHERE label = ?
 | 
				
			||||||
			}, filePath
 | 
								}, label
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Snapshot[] snapshots;
 | 
							Snapshot[] snapshots;
 | 
				
			||||||
| 
						 | 
					@ -192,9 +222,9 @@ public:
 | 
				
			||||||
			Snapshot snapshot;
 | 
								Snapshot snapshot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			snapshot.id = row["id"].to!long;
 | 
								snapshot.id = row["id"].to!long;
 | 
				
			||||||
			snapshot.filePath = row["file_path"].to!string;
 | 
					 | 
				
			||||||
			snapshot.fileSha256 = cast(ubyte[]) row["file_sha256"].dup;
 | 
					 | 
				
			||||||
			snapshot.label = row["label"].to!string;
 | 
								snapshot.label = row["label"].to!string;
 | 
				
			||||||
 | 
								snapshot.sha256 = cast(ubyte[]) row["sha256"].dup;
 | 
				
			||||||
 | 
								snapshot.description = row["description"].to!string;
 | 
				
			||||||
			snapshot.createdUtc = toDateTime(row["created_utc"].to!string);
 | 
								snapshot.createdUtc = toDateTime(row["created_utc"].to!string);
 | 
				
			||||||
			snapshot.sourceLength = row["source_length"].to!long;
 | 
								snapshot.sourceLength = row["source_length"].to!long;
 | 
				
			||||||
			snapshot.algoMin = row["algo_min"].to!long;
 | 
								snapshot.algoMin = row["algo_min"].to!long;
 | 
				
			||||||
| 
						 | 
					@ -214,7 +244,7 @@ public:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto queryResult = sql(
 | 
							auto queryResult = sql(
 | 
				
			||||||
			q{
 | 
								q{
 | 
				
			||||||
				SELECT id, file_path, file_sha256, label, created_utc, source_length,
 | 
									SELECT id, label, sha256, description, created_utc, source_length,
 | 
				
			||||||
					algo_min, algo_normal, algo_max, mask_s, mask_l, status
 | 
										algo_min, algo_normal, algo_max, mask_s, mask_l, status
 | 
				
			||||||
				FROM snapshots WHERE id = ?
 | 
									FROM snapshots WHERE id = ?
 | 
				
			||||||
			}, id
 | 
								}, id
 | 
				
			||||||
| 
						 | 
					@ -227,9 +257,9 @@ public:
 | 
				
			||||||
			auto data = queryResult.front();
 | 
								auto data = queryResult.front();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			snapshot.id = data["id"].to!long;
 | 
								snapshot.id = data["id"].to!long;
 | 
				
			||||||
			snapshot.filePath = data["file_path"].to!string;
 | 
					 | 
				
			||||||
			snapshot.fileSha256 = cast(ubyte[]) data["file_sha256"].dup;
 | 
					 | 
				
			||||||
			snapshot.label = data["label"].to!string;
 | 
								snapshot.label = data["label"].to!string;
 | 
				
			||||||
 | 
								snapshot.sha256 = cast(ubyte[]) data["sha256"].dup;
 | 
				
			||||||
 | 
								snapshot.description = data["description"].to!string;
 | 
				
			||||||
			snapshot.createdUtc = toDateTime(data["created_utc"].to!string);
 | 
								snapshot.createdUtc = toDateTime(data["created_utc"].to!string);
 | 
				
			||||||
			snapshot.sourceLength = data["source_length"].to!long;
 | 
								snapshot.sourceLength = data["source_length"].to!long;
 | 
				
			||||||
			snapshot.algoMin = data["algo_min"].to!long;
 | 
								snapshot.algoMin = data["algo_min"].to!long;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,12 +6,12 @@ auto _scheme = [
 | 
				
			||||||
		CREATE TABLE IF NOT EXISTS snapshots (
 | 
							CREATE TABLE IF NOT EXISTS snapshots (
 | 
				
			||||||
			-- идентификатор снимка
 | 
								-- идентификатор снимка
 | 
				
			||||||
			id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
								id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
			-- путь к исходному файлу
 | 
					 | 
				
			||||||
			file_path TEXT,
 | 
					 | 
				
			||||||
			-- SHA-256 всего файла (BLOB(32))
 | 
					 | 
				
			||||||
			file_sha256 BLOB NOT NULL CHECK (length(file_sha256) = 32),
 | 
					 | 
				
			||||||
			-- метка/название снимка
 | 
								-- метка/название снимка
 | 
				
			||||||
			label TEXT,
 | 
								label TEXT NOT NULL,
 | 
				
			||||||
 | 
								-- SHA-256 всего файла (BLOB(32))
 | 
				
			||||||
 | 
								sha256 BLOB NOT NULL CHECK (length(sha256) = 32),
 | 
				
			||||||
 | 
								-- Комментарий/описание
 | 
				
			||||||
 | 
								description TEXT DEFAULT NULL,
 | 
				
			||||||
			-- время создания (UTC)
 | 
								-- время создания (UTC)
 | 
				
			||||||
			created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
 | 
								created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
 | 
				
			||||||
			-- длина исходного файла в байтах
 | 
								-- длина исходного файла в байтах
 | 
				
			||||||
| 
						 | 
					@ -88,9 +88,9 @@ auto _scheme = [
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	q{
 | 
						q{
 | 
				
			||||||
		-- Индекс для запросов вида: WHERE file_path=? AND file_sha256=?
 | 
							-- Индекс для запросов вида: WHERE label=? AND sha256=?
 | 
				
			||||||
		CREATE INDEX IF NOT EXISTS idx_snapshots_path_sha
 | 
							CREATE INDEX IF NOT EXISTS idx_snapshots_path_sha
 | 
				
			||||||
			ON snapshots(file_path, file_sha256)
 | 
								ON snapshots(label, sha256)
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	q{
 | 
						q{
 | 
				
			||||||
		-- Индекс для обратного поиска использования blob по sha256
 | 
							-- Индекс для обратного поиска использования blob по sha256
 | 
				
			||||||
| 
						 | 
					@ -203,7 +203,51 @@ auto _scheme = [
 | 
				
			||||||
		CREATE TRIGGER IF NOT EXISTS trg_sc_block_update
 | 
							CREATE TRIGGER IF NOT EXISTS trg_sc_block_update
 | 
				
			||||||
		BEFORE UPDATE ON snapshot_chunks
 | 
							BEFORE UPDATE ON snapshot_chunks
 | 
				
			||||||
		BEGIN
 | 
							BEGIN
 | 
				
			||||||
			SELECT RAISE(ABORT, "snapshot_chunks: UPDATE запрещён; используйте DELETE + INSERT");
 | 
								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
 | 
							END
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,9 @@ enum SnapshotStatus : int
 | 
				
			||||||
struct Snapshot
 | 
					struct Snapshot
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	long id;
 | 
						long id;
 | 
				
			||||||
	string filePath;
 | 
					 | 
				
			||||||
	ubyte[32] fileSha256;
 | 
					 | 
				
			||||||
	string label;
 | 
						string label;
 | 
				
			||||||
 | 
						ubyte[32] sha256;
 | 
				
			||||||
 | 
						string description;
 | 
				
			||||||
	DateTime createdUtc;
 | 
						DateTime createdUtc;
 | 
				
			||||||
	long sourceLength;
 | 
						long sourceLength;
 | 
				
			||||||
	long algoMin;
 | 
						long algoMin;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ void main()
 | 
				
			||||||
	cas.newSnapshot("/tmp/text", cast(ubyte[]) read("/tmp/text"));
 | 
						cas.newSnapshot("/tmp/text", cast(ubyte[]) read("/tmp/text"));
 | 
				
			||||||
	// import std.stdio : writeln;
 | 
						// import std.stdio : writeln;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// writeln(cas.getSnapshotList("/tmp/text"));
 | 
						writeln(cas.getSnapshotList("/tmp/text"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// writeln(cas.getVersion);
 | 
						// writeln(cas.getVersion);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue