v1-0001-Adding-modify-toast-and-test-pg_amcheck.patch.WIP
application/octet-stream
Filename: v1-0001-Adding-modify-toast-and-test-pg_amcheck.patch.WIP
Type: application/octet-stream
Part: 0
Message:
Re: pg_amcheck contrib application
From 246e5ea1ec8dad8607ff154cf962fa73ea5356ae Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 26 Apr 2021 08:12:05 -0700
Subject: [PATCH v1 1/2] Adding modify toast and test pg_amcheck
This commit allows toast data to be modified, and adds tests of
pg_amcheck under corrupted toast conditions.
---
src/backend/executor/execMain.c | 7 +--
src/backend/executor/nodeModifyTable.c | 6 +-
src/backend/optimizer/util/appendinfo.c | 4 +-
src/bin/pg_amcheck/t/004_verify_heapam.pl | 71 ++++++++++++++++++++++-
4 files changed, 76 insertions(+), 12 deletions(-)
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index df3d7f9a8b..6cda1bfdb6 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -999,6 +999,7 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
+ case RELKIND_TOASTVALUE:
CheckCmdReplicaIdentity(resultRel, operation);
break;
case RELKIND_SEQUENCE:
@@ -1007,12 +1008,6 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
errmsg("cannot change sequence \"%s\"",
RelationGetRelationName(resultRel))));
break;
- case RELKIND_TOASTVALUE:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot change TOAST relation \"%s\"",
- RelationGetRelationName(resultRel))));
- break;
case RELKIND_VIEW:
/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c5a2a9a054..de84364548 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2466,7 +2466,8 @@ ExecModifyTable(PlanState *pstate)
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_MATVIEW ||
- relkind == RELKIND_PARTITIONED_TABLE)
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
{
/* ri_RowIdAttNo refers to a ctid attribute */
Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo));
@@ -2825,7 +2826,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_MATVIEW ||
- relkind == RELKIND_PARTITIONED_TABLE)
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
{
resultRelInfo->ri_RowIdAttNo =
ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index af46f581ac..43c81d9ed7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -866,7 +866,9 @@ add_row_identity_columns(PlannerInfo *root, Index rtindex,
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_MATVIEW ||
- relkind == RELKIND_PARTITIONED_TABLE)
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE
+ )
{
/*
* Emit CTID so that executor can find the row to update or delete.
diff --git a/src/bin/pg_amcheck/t/004_verify_heapam.pl b/src/bin/pg_amcheck/t/004_verify_heapam.pl
index b842f7bc6d..4d66eb64fe 100644
--- a/src/bin/pg_amcheck/t/004_verify_heapam.pl
+++ b/src/bin/pg_amcheck/t/004_verify_heapam.pl
@@ -225,7 +225,7 @@ my $rel = $node->safe_psql('postgres', qq(SELECT pg_relation_filepath('public.te
my $relpath = "$pgdata/$rel";
# Insert data and freeze public.test
-use constant ROWCOUNT => 16;
+use constant ROWCOUNT => 100;
$node->safe_psql('postgres', qq(
INSERT INTO public.test (a, b, c)
VALUES (
@@ -241,6 +241,13 @@ my $relfrozenxid = $node->safe_psql('postgres',
my $datfrozenxid = $node->safe_psql('postgres',
q(select datfrozenxid from pg_database where datname = 'postgres'));
+# Find our toast relation name
+my $toastname = $node->safe_psql('postgres', qq(
+ SELECT c.reltoastrelid::regclass
+ FROM pg_catalog.pg_class c
+ WHERE c.oid = 'public.test'::regclass
+ ));
+
# Sanity check that our 'test' table has a relfrozenxid newer than the
# datfrozenxid for the database, and that the datfrozenxid is greater than the
# first normal xid. We rely on these invariants in some of our tests.
@@ -297,7 +304,7 @@ close($file)
$node->start;
# Ok, Xids and page layout look ok. We can run corruption tests.
-plan tests => 19;
+plan tests => 38;
# Check that pg_amcheck runs against the uncorrupted table without error.
$node->command_ok(['pg_amcheck', '-p', $port, 'postgres'],
@@ -339,11 +346,12 @@ sub header
# performing any remaining checks, so we can't exercise the system properly if
# we focus all our corruption on a single tuple.
#
-my @expected;
+my (@expected, @corruptions);
open($file, '+<', $relpath)
or BAIL_OUT("open failed: $!");
binmode $file;
+my $value_id_to_corrupt;
for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
{
my $offnum = $tupidx + 1; # offnum is 1-based, not zero-based
@@ -512,12 +520,69 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
push @expected,
qr/${header}multitransaction ID 4000000000 precedes relation minimum multitransaction ID threshold 1/;
}
+ elsif ($offnum == 16)
+ {
+ $value_id_to_corrupt = $tup->{c_va_valueid};
+ push (@corruptions, "UPDATE $toastname SET chunk_seq = chunk_seq + 1000 WHERE chunk_id = $value_id_to_corrupt");
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value 16459 chunk 0 has sequence number 1000, but expected sequence number 0/,
+ qr/${header}toast value 16459 chunk 1 has sequence number 1001, but expected sequence number 1/,
+ qr/${header}toast value 16459 chunk 2 has sequence number 1002, but expected sequence number 2/,
+ qr/${header}toast value 16459 chunk 3 has sequence number 1003, but expected sequence number 3/,
+ qr/${header}toast value 16459 chunk 4 has sequence number 1004, but expected sequence number 4/,
+ qr/${header}toast value 16459 chunk 5 has sequence number 1005, but expected sequence number 5/;
+ }
+ elsif ($offnum == 17)
+ {
+ $value_id_to_corrupt = $tup->{c_va_valueid};
+ push (@corruptions, "UPDATE $toastname SET chunk_seq = chunk_seq * 1000 WHERE chunk_id = $value_id_to_corrupt");
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value $value_id_to_corrupt chunk 1 has sequence number 1000, but expected sequence number 1/,
+ qr/${header}toast value $value_id_to_corrupt chunk 2 has sequence number 2000, but expected sequence number 2/,
+ qr/${header}toast value $value_id_to_corrupt chunk 3 has sequence number 3000, but expected sequence number 3/,
+ qr/${header}toast value $value_id_to_corrupt chunk 4 has sequence number 4000, but expected sequence number 4/,
+ qr/${header}toast value $value_id_to_corrupt chunk 5 has sequence number 5000, but expected sequence number 5/;
+
+ }
+ elsif ($offnum == 18)
+ {
+ $value_id_to_corrupt = $tup->{c_va_valueid};
+ push (@corruptions, "UPDATE $toastname SET chunk_id = (chunk_id::integer + 10000000)::oid WHERE chunk_id = $value_id_to_corrupt");
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value $value_id_to_corrupt not found in toast table/;
+ }
+ elsif ($offnum == 19)
+ {
+ $value_id_to_corrupt = $tup->{c_va_valueid};
+ push (@corruptions, qq(
+INSERT INTO $toastname (chunk_id, chunk_seq, chunk_data)
+ (SELECT chunk_id,
+ 10*chunk_seq + 1000,
+ chunk_data
+ FROM $toastname
+ WHERE chunk_id = $value_id_to_corrupt)
+));
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value $value_id_to_corrupt chunk 6 has sequence number 1000, but expected sequence number 6/,
+ qr/${header}toast value $value_id_to_corrupt chunk 7 has sequence number 1010, but expected sequence number 7/,
+ qr/${header}toast value $value_id_to_corrupt chunk 8 has sequence number 1020, but expected sequence number 8/,
+ qr/${header}toast value $value_id_to_corrupt chunk 9 has sequence number 1030, but expected sequence number 9/,
+ qr/${header}toast value $value_id_to_corrupt chunk 10 has sequence number 1040, but expected sequence number 10/,
+ qr/${header}toast value $value_id_to_corrupt chunk 11 has sequence number 1050, but expected sequence number 11/,
+ qr/${header}toast value $value_id_to_corrupt was expected to end at chunk 6, but ended at chunk 12/;
+ }
write_tuple($file, $offset, $tup);
}
close($file)
or BAIL_OUT("close failed: $!");
$node->start;
+$node->safe_psql('postgres', $_) for (@corruptions);
+
# Run pg_amcheck against the corrupt table with epoch=0, comparing actual
# corruption messages against the expected messages
$node->command_checks_all(
--
2.21.1 (Apple Git-122.3)