1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Add contrib/pg_logicalinspect.

This module provides SQL functions that allow to inspect logical
decoding components.

It currently allows to inspect the contents of serialized logical
snapshots of a running database cluster, which is useful for debugging
or educational purposes.

Author: Bertrand Drouvot
Reviewed-by: Amit Kapila, Shveta Malik, Peter Smith, Peter Eisentraut
Reviewed-by: David G. Johnston
Discussion: https://postgr.es/m/ZscuZ92uGh3wm4tW%40ip-10-97-1-34.eu-west-3.compute.internal
This commit is contained in:
Masahiko Sawada
2024-10-14 17:22:02 -07:00
parent e2fd615ecc
commit 7cdfeee320
18 changed files with 598 additions and 39 deletions

View File

@ -1684,34 +1684,31 @@ out:
}
/*
* Restore a snapshot into 'builder' if previously one has been stored at the
* location indicated by 'lsn'. Returns true if successful, false otherwise.
* Restore the logical snapshot file contents to 'ondisk'.
*
* 'context' is the memory context where the catalog modifying/committed xid
* will live.
* If 'missing_ok' is true, will not throw an error if the file is not found.
*/
static bool
SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
bool
SnapBuildRestoreSnapshot(SnapBuildOnDisk *ondisk, const char *path,
MemoryContext context, bool missing_ok)
{
SnapBuildOnDisk ondisk;
int fd;
char path[MAXPGPATH];
Size sz;
pg_crc32c checksum;
/* no point in loading a snapshot if we're already there */
if (builder->state == SNAPBUILD_CONSISTENT)
return false;
sprintf(path, "%s/%X-%X.snap",
PG_LOGICAL_SNAPSHOTS_DIR,
LSN_FORMAT_ARGS(lsn));
Size sz;
fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
if (fd < 0 && errno == ENOENT)
return false;
else if (fd < 0)
if (fd < 0)
{
if (missing_ok && errno == ENOENT)
return false;
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open file \"%s\": %m", path)));
}
/* ----
* Make sure the snapshot had been stored safely to disk, that's normally
@ -1724,47 +1721,46 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
fsync_fname(path, false);
fsync_fname(PG_LOGICAL_SNAPSHOTS_DIR, true);
/* read statically sized portion of snapshot */
SnapBuildRestoreContents(fd, (char *) &ondisk, SnapBuildOnDiskConstantSize, path);
SnapBuildRestoreContents(fd, (char *) ondisk, SnapBuildOnDiskConstantSize, path);
if (ondisk.magic != SNAPBUILD_MAGIC)
if (ondisk->magic != SNAPBUILD_MAGIC)
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("snapbuild state file \"%s\" has wrong magic number: %u instead of %u",
path, ondisk.magic, SNAPBUILD_MAGIC)));
path, ondisk->magic, SNAPBUILD_MAGIC)));
if (ondisk.version != SNAPBUILD_VERSION)
if (ondisk->version != SNAPBUILD_VERSION)
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("snapbuild state file \"%s\" has unsupported version: %u instead of %u",
path, ondisk.version, SNAPBUILD_VERSION)));
path, ondisk->version, SNAPBUILD_VERSION)));
INIT_CRC32C(checksum);
COMP_CRC32C(checksum,
((char *) &ondisk) + SnapBuildOnDiskNotChecksummedSize,
((char *) ondisk) + SnapBuildOnDiskNotChecksummedSize,
SnapBuildOnDiskConstantSize - SnapBuildOnDiskNotChecksummedSize);
/* read SnapBuild */
SnapBuildRestoreContents(fd, (char *) &ondisk.builder, sizeof(SnapBuild), path);
COMP_CRC32C(checksum, &ondisk.builder, sizeof(SnapBuild));
SnapBuildRestoreContents(fd, (char *) &ondisk->builder, sizeof(SnapBuild), path);
COMP_CRC32C(checksum, &ondisk->builder, sizeof(SnapBuild));
/* restore committed xacts information */
if (ondisk.builder.committed.xcnt > 0)
if (ondisk->builder.committed.xcnt > 0)
{
sz = sizeof(TransactionId) * ondisk.builder.committed.xcnt;
ondisk.builder.committed.xip = MemoryContextAllocZero(builder->context, sz);
SnapBuildRestoreContents(fd, (char *) ondisk.builder.committed.xip, sz, path);
COMP_CRC32C(checksum, ondisk.builder.committed.xip, sz);
sz = sizeof(TransactionId) * ondisk->builder.committed.xcnt;
ondisk->builder.committed.xip = MemoryContextAllocZero(context, sz);
SnapBuildRestoreContents(fd, (char *) ondisk->builder.committed.xip, sz, path);
COMP_CRC32C(checksum, ondisk->builder.committed.xip, sz);
}
/* restore catalog modifying xacts information */
if (ondisk.builder.catchange.xcnt > 0)
if (ondisk->builder.catchange.xcnt > 0)
{
sz = sizeof(TransactionId) * ondisk.builder.catchange.xcnt;
ondisk.builder.catchange.xip = MemoryContextAllocZero(builder->context, sz);
SnapBuildRestoreContents(fd, (char *) ondisk.builder.catchange.xip, sz, path);
COMP_CRC32C(checksum, ondisk.builder.catchange.xip, sz);
sz = sizeof(TransactionId) * ondisk->builder.catchange.xcnt;
ondisk->builder.catchange.xip = MemoryContextAllocZero(context, sz);
SnapBuildRestoreContents(fd, (char *) ondisk->builder.catchange.xip, sz, path);
COMP_CRC32C(checksum, ondisk->builder.catchange.xip, sz);
}
if (CloseTransientFile(fd) != 0)
@ -1775,11 +1771,36 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
FIN_CRC32C(checksum);
/* verify checksum of what we've read */
if (!EQ_CRC32C(checksum, ondisk.checksum))
if (!EQ_CRC32C(checksum, ondisk->checksum))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("checksum mismatch for snapbuild state file \"%s\": is %u, should be %u",
path, checksum, ondisk.checksum)));
path, checksum, ondisk->checksum)));
return true;
}
/*
* Restore a snapshot into 'builder' if previously one has been stored at the
* location indicated by 'lsn'. Returns true if successful, false otherwise.
*/
static bool
SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
{
SnapBuildOnDisk ondisk;
char path[MAXPGPATH];
/* no point in loading a snapshot if we're already there */
if (builder->state == SNAPBUILD_CONSISTENT)
return false;
sprintf(path, "%s/%X-%X.snap",
PG_LOGICAL_SNAPSHOTS_DIR,
LSN_FORMAT_ARGS(lsn));
/* validate and restore the snapshot to 'ondisk' */
if (!SnapBuildRestoreSnapshot(&ondisk, path, builder->context, true))
return false;
/*
* ok, we now have a sensible snapshot here, figure out if it has more