(十二)详解逻辑备份---大对象的导出
1 其他的数据导出---大对象的数据
除了表的数据之外,大对象的真实数据,也被作为数据处理。如上一节中2.4节,同时也列出了大对象数据被导出的方式。
每个大对象,也占据一个导出对象链表的节点,每个大对象的数据导出,也类似表的数据导出。
大对象数据的处理,与表的数据不同的之处,在于,大对象需要放到一个事务中处理。所以,其多出两个函数,一个叫startPtr,是开始事务处理的;一个叫endPtr,是结束事务处理的。
2 dumpBlobs函数细节
2.1 dumpBlobs函数是要导出大对象的数据。
2.2 类似导出表的数据,导出大对象,采取的同样是“cursor”方式。
2.3 遍历读出的每一大对象,得到其对象ID,即得到每一个大对象的标识,
2.4 然后,打开(调用lo_open函数)这个大对象。
2.5 打开大对象后,调用StartBlob函数,有一个函数指针的使用典范:
int
StartBlob(Archive *AHX, Oid oid)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
if (!AH->StartBlobPtr)
die_horribly(AH, modulename, "large-object output not supported in chosen format\n");
(*AH->StartBlobPtr) (AH, AH->currToc, oid); //函数指针的使用典范
return 1;
}
如果导出格式是二进制,则使用“pg_backup_custom.c”文件中对应的如下函数执行导出:
static void
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
{
if (oid == 0)
die_horribly(AH, modulename, "invalid OID for large object\n");
WriteInt(AH, oid);
_StartDataCompressor(AH, te);
}
2.6 循环读出本大对象的数据块,直到本大对象的所有数据读完。
2.7 调用EndBlob函数,
3 大对象导出其他关系
3.1 大对象数据导出的整体逻辑关系
3.1.1 当导出对象链表被导出后,WriteDataChunks被调用,用于导出数据
3.1.2 当数据是大对象的数据时,WriteDataChunks函数中通过函数指针,调用了“_StartBlobs”函数(以“pg_backup_custom.c”文件中被调用的函数为例)
3.1.3 之后,又一次通过函数指针,即传给“ArchiveEntry”的倒数第二个参数的“dumpFn”对应的“dumpBlobs”函数,执行导出所有大对象的数据
3.1.4 在“dumpBlobs”函数中,又一次通过函数指针,调用了“StartBlob->_StartBlob”
3.1.5 这样,所有大对象的数据就被一次导出了
3.2 大对象导出在备份文件中的位置是如何记载的?
如果导出格式是二进制,则使用“pg_backup_custom.c”文件中对应的如下函数执行导出:
static void
_StartBlobs(ArchiveHandle *AH, TocEntry *te)
{
lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
tctx->dataPos = _getFilePos(AH, ctx);
tctx->dataState = K_OFFSET_POS_SET;
_WriteByte(AH, BLK_BLOBS); /* Block type */
WriteInt(AH, te->dumpId); /* For sanity check */
}
注意,此处调用_getFilePos函数给tctx->dataPos赋值,而tctx是te的formatData,所以,te上会记载导出数据的在导出文件中的位置。而我们曾经在本系列的第二篇“(二)体系结构“中提出”问题二:为什么又写一遍?”,根本原因就和此有关。
数据对象在导出时,分为两部分,一是“数据对象被抽象后的元信息”、一是“真正的数据”,而数据在导出文件中的位置,被记载到“数据对象被抽象后的元信息”中,但是本位置信息只有在导出数据时,才能得到具体的值(_StartBlobs中即可得到位置信息),所以,记载位置的链表信息,会被重新写出一遍。
1 其他的数据导出---大对象的数据
除了表的数据之外,大对象的真实数据,也被作为数据处理。如上一节中2.4节,同时也列出了大对象数据被导出的方式。
每个大对象,也占据一个导出对象链表的节点,每个大对象的数据导出,也类似表的数据导出。
大对象数据的处理,与表的数据不同的之处,在于,大对象需要放到一个事务中处理。所以,其多出两个函数,一个叫startPtr,是开始事务处理的;一个叫endPtr,是结束事务处理的。
2 dumpBlobs函数细节
2.1 dumpBlobs函数是要导出大对象的数据。
2.2 类似导出表的数据,导出大对象,采取的同样是“cursor”方式。
2.3 遍历读出的每一大对象,得到其对象ID,即得到每一个大对象的标识,
2.4 然后,打开(调用lo_open函数)这个大对象。
2.5 打开大对象后,调用StartBlob函数,有一个函数指针的使用典范:
int
StartBlob(Archive *AHX, Oid oid)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
if (!AH->StartBlobPtr)
die_horribly(AH, modulename, "large-object output not supported in chosen format\n");
(*AH->StartBlobPtr) (AH, AH->currToc, oid); //函数指针的使用典范
return 1;
}
如果导出格式是二进制,则使用“pg_backup_custom.c”文件中对应的如下函数执行导出:
static void
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
{
if (oid == 0)
die_horribly(AH, modulename, "invalid OID for large object\n");
WriteInt(AH, oid);
_StartDataCompressor(AH, te);
}
2.6 循环读出本大对象的数据块,直到本大对象的所有数据读完。
2.7 调用EndBlob函数,
3 大对象导出其他关系
3.1 大对象数据导出的整体逻辑关系
3.1.1 当导出对象链表被导出后,WriteDataChunks被调用,用于导出数据
3.1.2 当数据是大对象的数据时,WriteDataChunks函数中通过函数指针,调用了“_StartBlobs”函数(以“pg_backup_custom.c”文件中被调用的函数为例)
3.1.3 之后,又一次通过函数指针,即传给“ArchiveEntry”的倒数第二个参数的“dumpFn”对应的“dumpBlobs”函数,执行导出所有大对象的数据
3.1.4 在“dumpBlobs”函数中,又一次通过函数指针,调用了“StartBlob->_StartBlob”
3.1.5 这样,所有大对象的数据就被一次导出了
3.2 大对象导出在备份文件中的位置是如何记载的?
如果导出格式是二进制,则使用“pg_backup_custom.c”文件中对应的如下函数执行导出:
static void
_StartBlobs(ArchiveHandle *AH, TocEntry *te)
{
lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
tctx->dataPos = _getFilePos(AH, ctx);
tctx->dataState = K_OFFSET_POS_SET;
_WriteByte(AH, BLK_BLOBS); /* Block type */
WriteInt(AH, te->dumpId); /* For sanity check */
}
注意,此处调用_getFilePos函数给tctx->dataPos赋值,而tctx是te的formatData,所以,te上会记载导出数据的在导出文件中的位置。而我们曾经在本系列的第二篇“(二)体系结构“中提出”问题二:为什么又写一遍?”,根本原因就和此有关。
数据对象在导出时,分为两部分,一是“数据对象被抽象后的元信息”、一是“真正的数据”,而数据在导出文件中的位置,被记载到“数据对象被抽象后的元信息”中,但是本位置信息只有在导出数据时,才能得到具体的值(_StartBlobs中即可得到位置信息),所以,记载位置的链表信息,会被重新写出一遍。