[docs]classBlob(Base):__tablename__="blobs"# Wide enough for every identifier this table holds, which is not one scheme: message blobs are# keyed by a sha1 hexdigest (40 characters) and HTTP cache entries by hishel, which uses sha256# (64). PostgreSQL and MySQL enforce the declared width, so sizing this to one of the two makes# the other fail its insert.id=mapped_column(String(64),primary_key=True,unique=True)data=mapped_column(LargeBinary())content_type=mapped_column(String(64))created_at=mapped_column(TIMESTAMP(timezone=True),server_default=func.now())
[docs]classBlobsRepository(Repository[Blob]):Type=Blobdef_orphans_subquery(self):"""Blobs paired with the number of messages referencing them, so zero means unreferenced."""MH=aliased(Message,name="mh")MB=aliased(Message,name="mb")return(select(Blob.id,func.count(MH.id)+func.count(MB.id)).select_from(Blob).outerjoin(MH,MH.headers==Blob.id).outerjoin(MB,MB.body==Blob.id).group_by(Blob.id).subquery())
[docs]defselect_orphans(self):"""The ids `delete_orphans` would remove. Deleting blobs with a bulk statement leaves anything caching their existence, such as `SqlBlobStorage.seen`, believing rows are there that are not. The caller needs to know which ids went, so the count, the selection and the deletion all read from one definition of "orphan" and cannot drift apart. """subquery=self._orphans_subquery()returnselect(subquery.c.id).where(subquery.c[1]==0)