On Thu, Feb 04, 2021 at 01:22:35PM -0300, Alvaro Herrera wrote:
> On 2021-Feb-05, Julien Rouhaud wrote:
> 
> >  - HEAP_KEYS_UPDATED
> >    This bit lives in t_infomask2.  If set, indicates that the XMAX updated
> > -  this tuple and changed the key values, or it deleted the tuple.
> > -  It's set regardless of whether the XMAX is a TransactionId or a 
> > MultiXactId.
> > +  this tuple and changed the key values, or it deleted the tuple.  It can 
> > also
> > +  be set in combination of HEAP_XMAX_LOCK_ONLY.  It's set regardless of 
> > whether
> > +  the XMAX is a TransactionId or a MultiXactId.
> 
> I think we should reword this more completely, to avoid saying one thing
> (that the op is an update or delete) and then contradicting ourselves
> (that it can also be a lock).  I propose this:
> 
>       This bit lives in t_infomask2.  If set, it indicates that the
>       operation(s) done by the XMAX compromise the tuple key, such as
>       a SELECT FOR UPDATE, an UPDATE that modifies the columns of the
>       key, or a DELETE.

Thanks, that's way better, copied in v3.  I'm still a bit worried about that
description though, as that flag isn't consistently set for the FOR UPDATE
case.  Well, to be more precise it's maybe consistently set when the hint bits
are computed, but in some cases the flag is later cleared, so you won't
reliably find it in the tuple.
>From d2eb1a1314a92adc2d0115192d02164aef09bb88 Mon Sep 17 00:00:00 2001
From: Julien Rouhaud <julien.rouh...@free.fr>
Date: Fri, 22 Jan 2021 00:06:34 +0800
Subject: [PATCH v3] Allow HEAP_XMAX_LOCK_ONLY and HEAP_KEYS_UPDATED
 combination.

This combination of hint bits was previously detected as a form of corruption,
but it can be obtained with some mix of SELECT ... FOR UPDATE and UPDATE
queries.

Discussion: https://postgr.es/m/20210124061758.GA11756@nol
---
 contrib/amcheck/t/001_verify_heapam.pl | 14 +++++++++++++-
 contrib/amcheck/verify_heapam.c        |  7 -------
 src/backend/access/heap/README.tuplock |  7 ++++---
 src/backend/access/heap/hio.c          |  8 +++-----
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/contrib/amcheck/t/001_verify_heapam.pl 
b/contrib/amcheck/t/001_verify_heapam.pl
index a2f65b826d..6050feb712 100644
--- a/contrib/amcheck/t/001_verify_heapam.pl
+++ b/contrib/amcheck/t/001_verify_heapam.pl
@@ -4,7 +4,7 @@ use warnings;
 use PostgresNode;
 use TestLib;
 
-use Test::More tests => 79;
+use Test::More tests => 80;
 
 my ($node, $result);
 
@@ -47,6 +47,9 @@ detects_heap_corruption(
 #
 fresh_test_table('test');
 $node->safe_psql('postgres', q(VACUUM (FREEZE, DISABLE_PAGE_SKIPPING) test));
+detects_no_corruption(
+       "verify_heapam('test')",
+       "all-frozen not corrupted table");
 corrupt_first_page('test');
 detects_heap_corruption("verify_heapam('test')",
        "all-frozen corrupted table");
@@ -92,6 +95,15 @@ sub fresh_test_table
                ALTER TABLE $relname ALTER b SET STORAGE external;
                INSERT INTO $relname (a, b)
                        (SELECT gs, repeat('b',gs*10) FROM 
generate_series(1,1000) gs);
+               BEGIN;
+               SAVEPOINT s1;
+               SELECT 1 FROM $relname WHERE a = 42 FOR UPDATE;
+               UPDATE $relname SET b = b WHERE a = 42;
+               RELEASE s1;
+               SAVEPOINT s1;
+               SELECT 1 FROM $relname WHERE a = 42 FOR UPDATE;
+               UPDATE $relname SET b = b WHERE a = 42;
+               COMMIT;
        ));
 }
 
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 88ab32490c..49f5ca0ef2 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -608,13 +608,6 @@ check_tuple_header_and_visibilty(HeapTupleHeader tuphdr, 
HeapCheckContext *ctx)
                                                                   
ctx->tuphdr->t_hoff, ctx->lp_len));
                header_garbled = true;
        }
-       if ((ctx->tuphdr->t_infomask & HEAP_XMAX_LOCK_ONLY) &&
-               (ctx->tuphdr->t_infomask2 & HEAP_KEYS_UPDATED))
-       {
-               report_corruption(ctx,
-                                                 pstrdup("tuple is marked as 
only locked, but also claims key columns were updated"));
-               header_garbled = true;
-       }
 
        if ((ctx->tuphdr->t_infomask & HEAP_XMAX_COMMITTED) &&
                (ctx->tuphdr->t_infomask & HEAP_XMAX_IS_MULTI))
diff --git a/src/backend/access/heap/README.tuplock 
b/src/backend/access/heap/README.tuplock
index d03ddf6cdc..6441e8baf0 100644
--- a/src/backend/access/heap/README.tuplock
+++ b/src/backend/access/heap/README.tuplock
@@ -146,9 +146,10 @@ The following infomask bits are applicable:
   FOR UPDATE; this is implemented by the HEAP_KEYS_UPDATED bit.
 
 - HEAP_KEYS_UPDATED
-  This bit lives in t_infomask2.  If set, indicates that the XMAX updated
-  this tuple and changed the key values, or it deleted the tuple.
-  It's set regardless of whether the XMAX is a TransactionId or a MultiXactId.
+  This bit lives in t_infomask2.  If set, indicates that the operation(s) done
+  by the XMAX compromise the tuple key, such as a SELECT FOR UPDATE, an UPDATE
+  that modifies the columns of the key, or a DELETE.  It's set regardless of
+  whether the XMAX is a TransactionId or a MultiXactId.
 
 We currently never set the HEAP_XMAX_COMMITTED when the HEAP_XMAX_IS_MULTI bit
 is set.
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index fb7ad0bab4..75223c9bea 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -49,12 +49,10 @@ RelationPutHeapTuple(Relation relation,
 
        /*
         * Do not allow tuples with invalid combinations of hint bits to be 
placed
-        * on a page.  These combinations are detected as corruption by the
-        * contrib/amcheck logic, so if you disable one or both of these
-        * assertions, make corresponding changes there.
+        * on a page.  This combination is detected as corruption by the
+        * contrib/amcheck logic, so if you disable this assertion, make
+        * corresponding changes there.
         */
-       Assert(!((tuple->t_data->t_infomask & HEAP_XMAX_LOCK_ONLY) &&
-                        (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)));
        Assert(!((tuple->t_data->t_infomask & HEAP_XMAX_COMMITTED) &&
                         (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)));
 
-- 
2.20.1

Reply via email to