Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1s5XMY-005vfC-28 for pgsql-hackers@arkaria.postgresql.org; Fri, 10 May 2024 21:01:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1s5XMW-001TT5-BS for pgsql-hackers@arkaria.postgresql.org; Fri, 10 May 2024 21:01:04 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1s5XMV-001TSs-T7 for pgsql-hackers@lists.postgresql.org; Fri, 10 May 2024 21:01:04 +0000 Received: from mail-io1-xd2d.google.com ([2607:f8b0:4864:20::d2d]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1s5XMR-000PNe-Op for pgsql-hackers@postgresql.org; Fri, 10 May 2024 21:01:03 +0000 Received: by mail-io1-xd2d.google.com with SMTP id ca18e2360f4ac-7e1803a4290so113460939f.2 for ; Fri, 10 May 2024 14:01:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715374858; x=1715979658; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=UoLeFE/ljaXAAPg4A08h6ghAj7JO+44bv1YNapfSW58=; b=OFurhsB7z8da+aI0VIHwxo/G5ERCLPPI/8ne9gBbnhlQV2zvhLMYSQHeqYqxb4pPOG 40Chvortj2YHUeIkd7E9MssQfXat0Y70ZgU1N/5T3+zsNChB5uUFYcOrl3QBlWv6aYdH iiJ3kSCwwTYSCUkd+mIs+VToB3oWFM6mno+aLes5ib73Aca4c0ZM4xwsqN9PMnEpZOJW WlCmNP736QpGNxfoutBg6h30Md7scA5RIK8Us2Ndt3HjeLtWA0s6T4nr4cAE8Nt+5q41 pO0ZnVlHo/GNv9tNC0u75wdoffnK5T+8gjbWGxaSVkBA+tBwvPMpFoSG4Qrgl4JZM9Y9 QYUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715374858; x=1715979658; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=UoLeFE/ljaXAAPg4A08h6ghAj7JO+44bv1YNapfSW58=; b=UtIyUqZ7ruSxGmrRVRfHByllbmBTcb7SGH2GKbqmomTzGZ4/JvEcgBv3empDxR4LMi dmoxe8naAG8sH18levwnk5sIbp4E0YX5fPHLECDJQCvWg6+cR6xKYUUqM4s+715Wy5wt ilfPR9AiaYQZ50fTiSqWxaiqmjsEWr/KF9p4Y8Igptwpjapad1c8ghvstMalL9Yo1NUC 5YQstAzkrKs5T5SaI8LujGjQUp7ORZ7V2bnS1508bQ2bDEXPpMo9NycDtxu2e5wXC3Ze ZSML8X5TSSP+L7JIjpJX4JzyrVCPBeQjFiEp/xpbQnHih36ofUpE6s5I0r9GSFQpmOSl w/kQ== X-Forwarded-Encrypted: i=1; AJvYcCWzIxCmLcBsSwNy+vvEAybHuq2I3oGVXYDWgZrIjqbjkEZDjsvFaEYU48aC1SZlUH3IPqcEJiuQqvKJ91vL9ak9ZUqowWzWf6R8gRfj X-Gm-Message-State: AOJu0Yw5joiFnF5PvdvvelvOOtiSvbusm52o+2bfEwTaqFetWiF51+ss NXj9RTuVmNuz6A7GTXFJRchA7lBBYEQoY7ZhXhrc1AJmG6H6lVjo X-Google-Smtp-Source: AGHT+IEqSk7qV+jzT7LN4a4sx5eeE7+5peW6M+FX6/B4veora8noH/X9Bt0hCb++9A8AcMYC5F9tbQ== X-Received: by 2002:a6b:5112:0:b0:7e1:a30a:57f with SMTP id ca18e2360f4ac-7e1b521f177mr375397839f.15.1715374857737; Fri, 10 May 2024 14:00:57 -0700 (PDT) Received: from nathanxps13 (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 8926c6da1cb9f-489376de516sm1107702173.156.2024.05.10.14.00.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 May 2024 14:00:57 -0700 (PDT) Date: Fri, 10 May 2024 16:00:55 -0500 From: Nathan Bossart To: Michael Paquier Cc: Tom Lane , pgsql-hackers@postgresql.org Subject: Re: pg_sequence_last_value() for unlogged sequences on standbys Message-ID: <20240510210055.GA428795@nathanxps13> References: <20240503204908.GA1737291@nathanxps13> <2746846.1714771326@sss.pgh.pa.us> <20240507171033.GA2524814@nathanxps13> <4179588.1715103856@sss.pgh.pa.us> <20240507184051.GA2600328@nathanxps13> <86044.1715108521@sss.pgh.pa.us> <20240507193942.GB2600328@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Q68bSM7Ycu6FN28Q" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Wed, May 08, 2024 at 11:01:01AM +0900, Michael Paquier wrote: > On Tue, May 07, 2024 at 02:39:42PM -0500, Nathan Bossart wrote: >> Okay, phew. We can still do something like v3-0002 for v18. I'll give >> Michael a chance to comment on 0001 before committing/back-patching that >> one. > > What you are doing in 0001, and 0002 for v18 sounds fine to me. Great. Rather than commit this on a Friday afternoon, I'll just post what I have staged for commit early next week. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.master" From 19d9a1dd88385664e6991121e4751aba85a45639 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/system-views.sgml | 34 +++++++++++++++++++++++---- src/backend/commands/sequence.c | 31 +++++++++++++++++------- src/test/recovery/t/001_stream_rep.pl | 8 +++++++ 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index a54f4a4743..9842ee276e 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -3091,15 +3091,41 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + The sequence is unlogged and the server is a standby. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 46103561c3..28f8522264 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1777,11 +1777,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1792,12 +1789,28 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + * + * Also, for the benefit of the pg_sequences view, we return NULL for + * unlogged sequences on standbys instead of throwing an error. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel) && + (RelationIsPermanent(seqrel) || !RecoveryInProgress())) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; + + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - is_called = seq->is_called; - result = seq->last_value; + is_called = seq->is_called; + result = seq->last_value; - UnlockReleaseBuffer(buf); + UnlockReleaseBuffer(buf); + } sequence_close(seqrel, NoLock); if (is_called) diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl index 5311ade509..4c698b5ce1 100644 --- a/src/test/recovery/t/001_stream_rep.pl +++ b/src/test/recovery/t/001_stream_rep.pl @@ -95,6 +95,14 @@ $result = $node_standby_2->safe_psql('postgres', "SELECT * FROM seq1"); print "standby 2: $result\n"; is($result, qq(33|0|t), 'check streamed sequence content on standby 2'); +# Check pg_sequence_last_value() returns NULL for unlogged sequence on standby +$node_primary->safe_psql('postgres', + "CREATE UNLOGGED SEQUENCE ulseq; SELECT nextval('ulseq')"); +$node_primary->wait_for_replay_catchup($node_standby_1); +is($node_standby_1->safe_psql('postgres', + "SELECT pg_sequence_last_value('ulseq'::regclass) IS NULL"), + 't', 'pg_sequence_last_value() on unlogged sequence on standby 1'); + # Check that only READ-only queries can run on standbys is($node_standby_1->psql('postgres', 'INSERT INTO tab_int VALUES (1)'), 3, 'read-only queries on standby 1'); -- 2.25.1 --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.v16" From 6f99d2cfcf3572d2815055ff2e3e75a314d9c7e3 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/system-views.sgml | 34 +++++++++++++++++++++++---- src/backend/commands/sequence.c | 31 +++++++++++++++++------- src/test/recovery/t/001_stream_rep.pl | 8 +++++++ 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 39815d5faf..82a56f6af4 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -3009,15 +3009,41 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + The sequence is unlogged and the server is a standby. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index c7e262c0fc..3fa4e78857 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1795,11 +1795,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1810,12 +1807,28 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + * + * Also, for the benefit of the pg_sequences view, we return NULL for + * unlogged sequences on standbys instead of throwing an error. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel) && + (RelationIsPermanent(seqrel) || !RecoveryInProgress())) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; + + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - is_called = seq->is_called; - result = seq->last_value; + is_called = seq->is_called; + result = seq->last_value; - UnlockReleaseBuffer(buf); + UnlockReleaseBuffer(buf); + } relation_close(seqrel, NoLock); if (is_called) diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl index 0c72ba0944..710bdd54da 100644 --- a/src/test/recovery/t/001_stream_rep.pl +++ b/src/test/recovery/t/001_stream_rep.pl @@ -76,6 +76,14 @@ $result = $node_standby_2->safe_psql('postgres', "SELECT * FROM seq1"); print "standby 2: $result\n"; is($result, qq(33|0|t), 'check streamed sequence content on standby 2'); +# Check pg_sequence_last_value() returns NULL for unlogged sequence on standby +$node_primary->safe_psql('postgres', + "CREATE UNLOGGED SEQUENCE ulseq; SELECT nextval('ulseq')"); +$node_primary->wait_for_replay_catchup($node_standby_1); +is($node_standby_1->safe_psql('postgres', + "SELECT pg_sequence_last_value('ulseq'::regclass) IS NULL"), + 't', 'pg_sequence_last_value() on unlogged sequence on standby 1'); + # Check that only READ-only queries can run on standbys is($node_standby_1->psql('postgres', 'INSERT INTO tab_int VALUES (1)'), 3, 'read-only queries on standby 1'); -- 2.25.1 --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.v15" From 23e3ced85cfdcff5760e3ea32b962c009b50aede Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/system-views.sgml | 34 +++++++++++++++++++++++---- src/backend/commands/sequence.c | 31 +++++++++++++++++------- src/test/recovery/t/001_stream_rep.pl | 9 +++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 5f8b99bf69..e7284e2df5 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -2927,15 +2927,41 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + The sequence is unlogged and the server is a standby. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index acaf660c68..1a73a63d61 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1810,11 +1810,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1825,12 +1822,28 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + * + * Also, for the benefit of the pg_sequences view, we return NULL for + * unlogged sequences on standbys instead of throwing an error. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel) && + (RelationIsPermanent(seqrel) || !RecoveryInProgress())) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; + + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - is_called = seq->is_called; - result = seq->last_value; + is_called = seq->is_called; + result = seq->last_value; - UnlockReleaseBuffer(buf); + UnlockReleaseBuffer(buf); + } relation_close(seqrel, NoLock); if (is_called) diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl index 86864098f9..54c3d3bdf5 100644 --- a/src/test/recovery/t/001_stream_rep.pl +++ b/src/test/recovery/t/001_stream_rep.pl @@ -78,6 +78,15 @@ $result = $node_standby_2->safe_psql('postgres', "SELECT * FROM seq1"); print "standby 2: $result\n"; is($result, qq(33|0|t), 'check streamed sequence content on standby 2'); +# Check pg_sequence_last_value() returns NULL for unlogged sequence on standby +$node_primary->safe_psql('postgres', + "CREATE UNLOGGED SEQUENCE ulseq; SELECT nextval('ulseq')"); +$primary_lsn = $node_primary->lsn('write'); +$node_primary->wait_for_catchup($node_standby_1, 'replay', $primary_lsn); +is($node_standby_1->safe_psql('postgres', + "SELECT pg_sequence_last_value('ulseq'::regclass) IS NULL"), + 't', 'pg_sequence_last_value() on unlogged sequence on standby 1'); + # Check that only READ-only queries can run on standbys is($node_standby_1->psql('postgres', 'INSERT INTO tab_int VALUES (1)'), 3, 'read-only queries on standby 1'); -- 2.25.1 --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.v14" From 9d454ea9fd11e42a2408cf35dd40957529c06308 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/catalogs.sgml | 29 +++++++++++++++++++++++++---- src/backend/commands/sequence.c | 27 ++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index dba6479cf5..b444ca776c 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -12108,15 +12108,36 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 98649986e1..ad34aaff6d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1856,11 +1856,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1871,12 +1868,24 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel)) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; - is_called = seq->is_called; - result = seq->last_value; + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - UnlockReleaseBuffer(buf); + is_called = seq->is_called; + result = seq->last_value; + + UnlockReleaseBuffer(buf); + } relation_close(seqrel, NoLock); if (is_called) -- 2.25.1 --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.v13" From 8e614d1d1439d9b75e3c5218ccfb9e4123fb6d14 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/catalogs.sgml | 29 +++++++++++++++++++++++++---- src/backend/commands/sequence.c | 27 ++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 0569093486..23dd3df40a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -11794,15 +11794,36 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index ca0bdb5771..5106ef685b 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1857,11 +1857,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1872,12 +1869,24 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel)) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; - is_called = seq->is_called; - result = seq->last_value; + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - UnlockReleaseBuffer(buf); + is_called = seq->is_called; + result = seq->last_value; + + UnlockReleaseBuffer(buf); + } relation_close(seqrel, NoLock); if (is_called) -- 2.25.1 --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename*0="v4-0001-Fix-pg_sequence_last_value-for-unlogged-sequences.pat"; filename*1="ch.v12" From 8078c01d39e2f66c9d1f6161799e19d4e5ceb8a5 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 10 May 2024 15:55:24 -0500 Subject: [PATCH v4 1/1] Fix pg_sequence_last_value() for unlogged sequences on standbys. Presently, when this function is called for an unlogged sequence on a standby server, it will error out with a message like ERROR: could not open file "base/5/16388": No such file or directory Since the pg_sequences system view uses pg_sequence_last_value(), it can error similarly. To fix, modify the function to return NULL for unlogged sequences on standby servers. Since this bug is present on all versions since v15, this approach is preferable to making the ERROR nicer because we need to repair the pg_sequences view without modifying its definition on released versions. For consistency, this commit also modifies the function to return NULL for other sessions' temporary sequences. The pg_sequences view already appropriately filters out such sequences, so there's no bug there, but we might as well offer some defense in case someone invokes this function directly. Unlogged sequences were first introduced in v15, but temporary sequences are much older, so while the fix for unlogged sequences is only back-patched to v15, the temporary sequence portion is back-patched to all supported versions. We could also remove the privilege check in the pg_sequences view definition in v18 if we modify this function to return NULL for sequences for which the current user lacks privileges, but that is left as a future exercise for when v18 development begins. Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20240501005730.GA594666%40nathanxps13 Backpatch-through: 12 --- doc/src/sgml/catalogs.sgml | 29 +++++++++++++++++++++++++---- src/backend/commands/sequence.c | 27 ++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 118e325464..2337ce8fd2 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -10381,14 +10381,35 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx The last sequence value written to disk. If caching is used, this value can be greater than the last value handed out from the - sequence. Null if the sequence has not been read from yet. Also, if - the current user does not have USAGE - or SELECT privilege on the sequence, the value is - null. + sequence. + + + The last_value column will read as null if any of + the following are true: + + + + The sequence has not been read from yet. + + + + + The current user does not have USAGE or + SELECT privilege on the sequence. + + + + + The sequence is a temporary sequence created by another session. + + + + + diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0577184f82..daaf8ee3d4 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1857,11 +1857,8 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) Oid relid = PG_GETARG_OID(0); SeqTable elm; Relation seqrel; - Buffer buf; - HeapTupleData seqtuple; - Form_pg_sequence_data seq; - bool is_called; - int64 result; + bool is_called = false; + int64 result = 0; /* open and lock sequence */ init_sequence(relid, &elm, &seqrel); @@ -1872,12 +1869,24 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - seq = read_seq_tuple(seqrel, &buf, &seqtuple); + /* + * We return NULL for other sessions' temporary sequences. The + * pg_sequences system view already filters those out, but this offers a + * defense against ERRORs in case someone invokes this function directly. + */ + if (!RELATION_IS_OTHER_TEMP(seqrel)) + { + Buffer buf; + HeapTupleData seqtuple; + Form_pg_sequence_data seq; - is_called = seq->is_called; - result = seq->last_value; + seq = read_seq_tuple(seqrel, &buf, &seqtuple); - UnlockReleaseBuffer(buf); + is_called = seq->is_called; + result = seq->last_value; + + UnlockReleaseBuffer(buf); + } relation_close(seqrel, NoLock); if (is_called) -- 2.25.1 --Q68bSM7Ycu6FN28Q--