Skip to content

Commit 25449fc

Browse files
committed
add database inspect chunk and chunk group commands
1 parent 62ccbaa commit 25449fc

12 files changed

Lines changed: 321 additions & 37 deletions

File tree

lang/en_us.yml

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,30 @@ prime_backup:
213213
title: 'Blob {}'
214214
id: 'ID: {}'
215215
storage_method: 'Storage method: {}'
216-
hash: 'Hash: {}'
216+
hash: 'Hash: {} ({})'
217217
compress: 'Compress: {}'
218218
raw_size: 'Raw size: {} ({})'
219219
stored_size: 'Stored size: {} ({})'
220220
chunk_count: 'Chunk group count: {}; Chunk count: {}, samples:'
221221
chunk_sample: 'offset={}, len={}, hash={}'
222222
used_by: 'Associated file count: {}. Samples:'
223223
file_sample: 'fileset {}, path: {}'
224+
db_inspect_chunk:
225+
name: inspect chunk
226+
title: 'Chunk {}'
227+
id: 'ID: {}'
228+
hash: 'Hash: {} ({})'
229+
compress: 'Compress: {}'
230+
raw_size: 'Raw size: {} ({})'
231+
stored_size: 'Stored size: {} ({})'
232+
db_inspect_chunk_group:
233+
name: inspect chunk group
234+
title: 'Chunk group {}'
235+
id: 'ID: {}'
236+
hash: 'Hash: {} ({})'
237+
chunk_count: 'Chunk count: {}'
238+
chunk_raw_size_sum: 'Chunk raw size sum: {} ({})'
239+
chunk_stored_size_sum: 'Chunk stored size sum: {} ({})'
224240
db_inspect_file:
225241
name: inspect file
226242
title: 'File {1} in fileset {0}'
@@ -258,6 +274,7 @@ prime_backup:
258274
fileset_id: Click to inspect fileset {}
259275
blob_hash: Click to inspect blob {}
260276
chunk_hash: Click to inspect chunk {}
277+
chunk_group_hash: Click to inspect chunk group {}
261278
db_migrate_compress_method:
262279
name: migrate compress method
263280
missing_library:
@@ -456,7 +473,9 @@ prime_backup:
456473
§7{prefix} database inspect file §6<backup_id> §3<file_path>§r: Inspect the internal data of the given file
457474
§7{prefix} database inspect file2 §3<fileset_id> §3<file_path>§r: Inspect the internal data of the given file in fileset
458475
§7{prefix} database inspect fileset §3<fileset_id>§r: Inspect the internal data of the given fileset
459-
§7{prefix} database inspect blob §d<blob_hash>§r: Inspect the internal data of the given blob
476+
§7{prefix} database inspect blob §d<id_or_hash>§r: Inspect the internal data of the given blob
477+
§7{prefix} database inspect chunk §d<id_or_hash>§r: Inspect the internal data of the given chunk
478+
§7{prefix} database inspect chunk_group §d<id_or_hash>§r: Inspect the internal data of the given chunk group
460479
§7{prefix} database delete file §6<backup_id> §3<file_path> §7[--recursive]§r: Delete given file in the given backup. If §7[--recursive]§r and it's a directory, delete everything inside it
461480
§7{prefix} database validate §a<part>§r: Validate the correctness of contents in the database. Might take a long time
462481
§7{prefix} database vacuum§r: Compact the SQLite database manually, to reduce the size of the database file
@@ -466,7 +485,7 @@ prime_backup:
466485
{scheduled_compact_notes}
467486
§d[Arguments]§r
468487
§3<file_path>§r: Path of the file related to the backup source root
469-
§d<blob_hash>§r: Hash value of the blob. Can be a prefix of the hash string as long as it's unique
488+
§d<id_or_hash>§r: The ID of the object, or the hash of the object. It can be a prefix of the hash string as long as it's unique
470489
§d<compress_method>§r: Available options: {compress_methods}
471490
§d<hash_method>§r: Available options: {hash_methods}
472491
§a<part>§r:
@@ -582,8 +601,12 @@ prime_backup:
582601
blob_hash_not_unique.candidates: 'Found at least {} candidates: {}'
583602
chunk_id_not_found: Chunk with id {} does not exist
584603
chunk_hash_not_found: Chunk with hash {} does not exist
604+
chunk_hash_not_unique: 'The given hash {} cannot uniquely identify a chunk'
605+
chunk_hash_not_unique.candidates: 'Found at least {} candidates: {}'
585606
chunk_group_id_not_found: Chunk group with id {} does not exist
586607
chunk_group_hash_not_found: Chunk group with hash {} does not exist
608+
chunk_group_hash_not_unique: 'The given hash {} cannot uniquely identify a chunk group'
609+
chunk_group_hash_not_unique.candidates: 'Found at least {} candidates: {}'
587610
db_locked: Task {} execution error cuz the database is locked. Please retry later
588611
initializing: Plugin initializing
589612
disabled: Plugin disabled

lang/zh_cn.yml

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,30 @@ prime_backup:
213213
title: '数据对象{}'
214214
id: 'ID: {}'
215215
storage_method: '存储方法: {}'
216-
hash: '哈希: {}'
216+
hash: '哈希: {} ({})'
217217
compress: '压缩方法: {}'
218218
raw_size: '原始大小: {} ({})'
219219
stored_size: '储存大小: {} ({})'
220220
chunk_count: '数据块组数: {}; 数据块数: {}, 样本:'
221221
chunk_sample: '偏移={}, 长度={}, 哈希={}'
222222
used_by: '关联文件数: {}。样本:'
223223
file_sample: '文件集{}, 路径: {}'
224+
db_inspect_chunk:
225+
name: 审查数据块
226+
title: '数据块{}'
227+
id: 'ID: {}'
228+
hash: '哈希: {} ({})'
229+
compress: '压缩方法: {}'
230+
raw_size: '原始大小: {} ({})'
231+
stored_size: '储存大小: {} ({})'
232+
db_inspect_chunk_group:
233+
name: 审查数据块组
234+
title: '数据块组{}'
235+
id: 'ID: {}'
236+
hash: '哈希: {} ({})'
237+
chunk_count: '数据块数: {}'
238+
chunk_raw_size_sum: '数据块原始大小总和: {} ({})'
239+
chunk_stored_size_sum: '数据块储存大小总和: {} ({})'
224240
db_inspect_file:
225241
name: 审查文件
226242
title: '文件集{0}的文件{1}'
@@ -257,7 +273,8 @@ prime_backup:
257273
backup_id: 点击以审查备份{}
258274
fileset_id: 点击以审查文件集{}
259275
blob_hash: 点击以审查数据对象{}
260-
chunk_hash: 点击以审查数据块对象{}
276+
chunk_hash: 点击以审查数据块{}
277+
chunk_group_hash: 点击以审查数据块组{}
261278
db_migrate_compress_method:
262279
name: 压缩方法迁移
263280
missing_library:
@@ -456,7 +473,9 @@ prime_backup:
456473
§7{prefix} database inspect file §6<备份ID> §3<文件路径>§r: 审查给定文件的原始信息
457474
§7{prefix} database inspect file2 §3<文件集ID> §3<文件路径>§r: 审查给定文件集中文件的原始信息
458475
§7{prefix} database inspect fileset §3<文件集ID>§r: 审查给定文件集的原始信息
459-
§7{prefix} database inspect blob §d<哈希值>§r: 审查给定数据对象的原始信息
476+
§7{prefix} database inspect blob §d<ID或哈希>§r: 审查给定数据对象的原始信息
477+
§7{prefix} database inspect chunk §d<ID或哈希>§r: 审查给定数据块的原始信息
478+
§7{prefix} database inspect chunk_group §d<ID或哈希>§r: 审查给定数据块组的原始信息
460479
§7{prefix} database delete file §6<备份ID> §3<文件路径> §7[--recursive]§r: 删除给定备份中的某个文件。若带有§7--recursive§r且目标为文件夹,则递归删除其内部的所有内容
461480
§7{prefix} database validate §a<组件>§r: 验证数据库内容的正确性。耗时可能较长
462481
§7{prefix} database vacuum§r: 手动执行SQLite数据库的文件整理操作,减少数据库文件的体积
@@ -466,7 +485,7 @@ prime_backup:
466485
{scheduled_compact_notes}
467486
§d【参数帮助】§r
468487
§3<文件路径>§r: 要审查的文件的路径,相对备份根目录
469-
§d<哈希值>§r: 要审查的数据对象的哈希值。可以是哈希串的前缀,只要唯一即可
488+
§d<ID或哈希>§r: 要审查的对象的ID或哈希值。也可谓哈希串的前缀,只要确保唯一即可
470489
§d<压缩方法>§r: 可用选项: {compress_methods}
471490
§d<哈希算法>§r: 可用选项: {hash_methods}
472491
§a<组件>§r:
@@ -582,8 +601,12 @@ prime_backup:
582601
blob_hash_not_unique.candidates: '找到了至少{}个可能的数据对象: {}'
583602
chunk_id_not_found: ID为{}的数据块不存在
584603
chunk_hash_not_found: 哈希值为{}的数据块不存在
604+
chunk_hash_not_unique: '给定的哈希值{}无法唯一确定一个数据块'
605+
chunk_hash_not_unique.candidates: '找到了至少{}个可能的数据块: {}'
585606
chunk_group_id_not_found: ID为{}的数据块组不存在
586607
chunk_group_hash_not_found: 哈希值为{}的数据块组不存在
608+
chunk_group_hash_not_unique: '给定的哈希值{}无法唯一确定一个数据块组'
609+
chunk_group_hash_not_unique.candidates: '找到了至少{}个可能的数据块组: {}'
587610
db_locked: 任务{}执行失败, 数据库已被锁定。请稍候再试
588611
initializing: 插件初始化中
589612
disabled: 插件已禁用

prime_backup/action/get_blob_action.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,7 @@ def _do_get_blob(self, session: DbSession) -> schema.Blob:
7171
if len(blobs) == 0:
7272
raise BlobHashNotFound(self.blob_hash_prefix)
7373
elif len(blobs) > 1:
74-
raise BlobHashNotUnique(self.blob_hash_prefix, list(sorted(map(BlobInfo.of, blobs))))
74+
def get_hash_for_sort(b: 'BlobInfo'):
75+
return b.hash
76+
raise BlobHashNotUnique(self.blob_hash_prefix, sorted(map(BlobInfo.of, blobs), key=get_hash_for_sort))
7577
return blobs[0]

prime_backup/action/get_chunk_action.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from prime_backup.action import Action
77
from prime_backup.db.access import DbAccess
8+
from prime_backup.exceptions import ChunkHashNotFound, ChunkHashNotUnique
89
from prime_backup.types.chunk_info import OffsetChunkInfo, ChunkInfo
910

1011

@@ -38,6 +39,27 @@ def run(self) -> ChunkInfo:
3839
return ChunkInfo.of(chunk)
3940

4041

42+
class GetChunkByHashPrefixAction(Action[ChunkInfo]):
43+
def __init__(self, chunk_hash_prefix: str):
44+
super().__init__()
45+
self.chunk_hash_prefix = chunk_hash_prefix
46+
47+
@override
48+
def run(self) -> ChunkInfo:
49+
"""
50+
:raise: ChunkHashNotFound or ChunkHashNotUnique
51+
"""
52+
with DbAccess.open_session() as session:
53+
chunks = session.list_chunk_with_hash_prefix(self.chunk_hash_prefix, limit=3)
54+
if len(chunks) == 0:
55+
raise ChunkHashNotFound(self.chunk_hash_prefix)
56+
elif len(chunks) > 1:
57+
def get_hash_for_sort(b: 'ChunkInfo'):
58+
return b.hash
59+
raise ChunkHashNotUnique(self.chunk_hash_prefix, sorted(map(ChunkInfo.of, chunks), key=get_hash_for_sort))
60+
return ChunkInfo.of(chunks[0])
61+
62+
4163
class GetBlobChunksAction(Action[List[OffsetChunkInfo]]):
4264
def __init__(self, blob_id: int, *, limit: Optional[int] = None):
4365
super().__init__()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from typing_extensions import override
2+
3+
from prime_backup.action import Action
4+
from prime_backup.db.access import DbAccess
5+
from prime_backup.exceptions import ChunkHashNotFound, ChunkGroupHashNotUnique
6+
from prime_backup.types.chunk_group_info import ChunkGroupInfo
7+
8+
9+
class GetChunkGroupByIdAction(Action[ChunkGroupInfo]):
10+
def __init__(self, chunk_group_id: int):
11+
super().__init__()
12+
self.chunk_group_id = chunk_group_id
13+
14+
@override
15+
def run(self) -> ChunkGroupInfo:
16+
"""
17+
:raise: ChunkIdNotFound
18+
"""
19+
with DbAccess.open_session() as session:
20+
chunk_group = session.get_chunk_group_by_id(self.chunk_group_id)
21+
return ChunkGroupInfo.of(chunk_group)
22+
23+
24+
class GetChunkGroupByHashAction(Action[ChunkGroupInfo]):
25+
def __init__(self, chunk_group_hash: str):
26+
super().__init__()
27+
self.chunk_group_hash = chunk_group_hash
28+
29+
@override
30+
def run(self) -> ChunkGroupInfo:
31+
"""
32+
:raise: ChunkHashNotFound
33+
"""
34+
with DbAccess.open_session() as session:
35+
chunk_group = session.get_chunk_group_by_hash(self.chunk_group_hash)
36+
return ChunkGroupInfo.of(chunk_group)
37+
38+
39+
class GetChunkGroupByHashPrefixAction(Action[ChunkGroupInfo]):
40+
def __init__(self, chunk_group_hash_prefix: str):
41+
super().__init__()
42+
self.chunk_group_hash_prefix = chunk_group_hash_prefix
43+
44+
@override
45+
def run(self) -> ChunkGroupInfo:
46+
"""
47+
:raise: ChunkHashNotFound or ChunkHashNotUnique
48+
"""
49+
with DbAccess.open_session() as session:
50+
chunk_groups = session.list_chunk_group_with_hash_prefix(self.chunk_group_hash_prefix, limit=3)
51+
if len(chunk_groups) == 0:
52+
raise ChunkHashNotFound(self.chunk_group_hash_prefix)
53+
elif len(chunk_groups) > 1:
54+
def get_hash_for_sort(b: 'ChunkGroupInfo'):
55+
return b.hash
56+
raise ChunkGroupHashNotUnique(self.chunk_group_hash_prefix, sorted(map(ChunkGroupInfo.of, chunk_groups), key=get_hash_for_sort))
57+
return ChunkGroupInfo.of(chunk_groups[0])

prime_backup/db/session.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from prime_backup.db import schema, db_constants
1616
from prime_backup.db.values import FileRole, BackupTagDict, OffsetChunk, OffsetChunkGroup, BlobStorageMethod, ChunkGroupChunkBindingIdentifier, BlobChunkGroupBindingIdentifier, FileIdentifier
17-
from prime_backup.exceptions import BackupNotFound, BackupFileNotFound, BlobHashNotFound, PrimeBackupError, FilesetNotFound, FilesetFileNotFound, BlobIdNotFound, ChunkHashNotFound, ChunkIdNotFound, ChunkGroupChunkBindingNotFound, BlobChunkGroupBindingNotFound
17+
from prime_backup.exceptions import BackupNotFound, BackupFileNotFound, BlobHashNotFound, PrimeBackupError, FilesetNotFound, FilesetFileNotFound, BlobIdNotFound, ChunkHashNotFound, ChunkIdNotFound, ChunkGroupChunkBindingNotFound, BlobChunkGroupBindingNotFound, ChunkGroupIdNotFound, ChunkGroupHashNotFound
1818
from prime_backup.types.backup_filter import BackupFilter, BackupTagFilter, BackupSortOrder
1919
from prime_backup.utils import collection_utils, db_utils, validation_utils
2020

@@ -380,6 +380,10 @@ def list_chunks(self, limit: Optional[int] = None, offset: Optional[int] = None)
380380
s = s.offset(offset)
381381
return _list_it(self.session.execute(s).scalars().all())
382382

383+
def list_chunk_with_hash_prefix(self, hash_prefix: str, limit: int) -> List[schema.Chunk]:
384+
s = select(schema.Chunk).where(schema.Chunk.hash.startswith(hash_prefix, autoescape=True)).limit(limit)
385+
return _list_it(self.session.execute(s).scalars().all())
386+
383387
def iterate_chunk_batch(self, *, batch_size: int) -> Iterator[List[schema.Chunk]]:
384388
limit, offset = batch_size, 0
385389
while True:
@@ -430,11 +434,26 @@ def create_and_add_chunk_group(self, **kwargs: Unpack[CreateChunkGroupKwargs]) -
430434
def get_chunk_group_count(self) -> int:
431435
return _int_or_0(self.session.execute(select(func.count()).select_from(schema.ChunkGroup)).scalar_one())
432436

433-
def get_chunk_group_opt(self, h: str) -> Optional[schema.ChunkGroup]:
437+
def get_chunk_group_by_id_opt(self, chunk_group_id: int) -> Optional[schema.ChunkGroup]:
438+
return self.session.get(schema.ChunkGroup, chunk_group_id)
439+
440+
def get_chunk_group_by_id(self, chunk_group_id: int) -> schema.ChunkGroup:
441+
chunk_group = self.get_chunk_group_by_id_opt(chunk_group_id)
442+
if chunk_group is None:
443+
raise ChunkGroupIdNotFound(chunk_group_id)
444+
return chunk_group
445+
446+
def get_chunk_group_by_hash_opt(self, h: str) -> Optional[schema.ChunkGroup]:
434447
return self.session.execute(
435448
select(schema.ChunkGroup).where(schema.ChunkGroup.hash == h)
436449
).scalars().one_or_none()
437450

451+
def get_chunk_group_by_hash(self, h: str) -> schema.ChunkGroup:
452+
chunk_group = self.get_chunk_group_by_hash_opt(h)
453+
if chunk_group is None:
454+
raise ChunkGroupHashNotFound(h)
455+
return chunk_group
456+
438457
def get_chunk_groups_by_ids(self, chunk_group_ids: List[int]) -> Dict[int, Optional[schema.ChunkGroup]]:
439458
"""
440459
:return: a dict, id -> optional chunk group. All given ids are in the dict
@@ -463,6 +482,10 @@ def list_chunk_groups(self, limit: Optional[int] = None, offset: Optional[int] =
463482
s = s.offset(offset)
464483
return _list_it(self.session.execute(s).scalars().all())
465484

485+
def list_chunk_group_with_hash_prefix(self, hash_prefix: str, limit: int) -> List[schema.ChunkGroup]:
486+
s = select(schema.ChunkGroup).where(schema.ChunkGroup.hash.startswith(hash_prefix, autoescape=True)).limit(limit)
487+
return _list_it(self.session.execute(s).scalars().all())
488+
466489
def iterate_chunk_group_batch(self, *, batch_size: int) -> Iterator[List[schema.ChunkGroup]]:
467490
limit, offset = batch_size, 0
468491
while True:

prime_backup/exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
if TYPE_CHECKING:
44
from prime_backup.types.blob_info import BlobInfo
5+
from prime_backup.types.chunk_group_info import ChunkGroupInfo
6+
from prime_backup.types.chunk_info import ChunkInfo
57

68

79
class PrimeBackupError(Exception):
@@ -81,6 +83,13 @@ def __init__(self, chunk_hash: str):
8183
self.chunk_hash = chunk_hash
8284

8385

86+
class ChunkHashNotUnique(PrimeBackupError):
87+
def __init__(self, chunk_hash_prefix: str, candidates: List['ChunkInfo']):
88+
super().__init__()
89+
self.chunk_hash_prefix = chunk_hash_prefix
90+
self.candidates = candidates
91+
92+
8493
class ChunkGroupNotFound(PrimeBackupError):
8594
pass
8695

@@ -97,6 +106,13 @@ def __init__(self, chunk_group_hash: str):
97106
self.chunk_group_hash = chunk_group_hash
98107

99108

109+
class ChunkGroupHashNotUnique(PrimeBackupError):
110+
def __init__(self, chunk_group_hash_prefix: str, candidates: List['ChunkGroupInfo']):
111+
super().__init__()
112+
self.chunk_group_hash_prefix = chunk_group_hash_prefix
113+
self.candidates = candidates
114+
115+
100116
class ChunkGroupChunkBindingNotFound(PrimeBackupError):
101117
def __init__(self, chunk_group_id: int, chunk_offset: int):
102118
super().__init__()

0 commit comments

Comments
 (0)