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 1u9owA-006gbX-Uj for pgsql-hackers@arkaria.postgresql.org; Tue, 29 Apr 2025 17:40:07 +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 1u9ow8-0092Tg-Dt for pgsql-hackers@arkaria.postgresql.org; Tue, 29 Apr 2025 17:40:05 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1u9ow8-0092TY-0E for pgsql-hackers@lists.postgresql.org; Tue, 29 Apr 2025 17:40:05 +0000 Received: from mail-io1-xd33.google.com ([2607:f8b0:4864:20::d33]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1u9ow6-000DI8-16 for pgsql-hackers@lists.postgresql.org; Tue, 29 Apr 2025 17:40:03 +0000 Received: by mail-io1-xd33.google.com with SMTP id ca18e2360f4ac-85b3f92c866so131231139f.3 for ; Tue, 29 Apr 2025 10:40:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1745948401; x=1746553201; darn=lists.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=fEBnczrzjEj5x/y6AtNAt3n1r8SC1W0wf3ug+RcjSOc=; b=a6feJV7OdgbjNscYDq0TmjMnRL84dDLENbD50vC+UcL68H4VsqOZFgHCtyTY6mhb2S 8mF5MekmKZmk7gzLbPh7tGiXeGVBf2fdbH5ST5/jc3NLSCKVelAuoF5jCMxUE2tnWbzl okpWOeUVgOkNUrmXaW1jZRXVaDfXs37SAQpegfYbqNUZ6ixubk7yvncQnnugMiY3DFXI tLWYDF0pvu/5QanD/f8A+sYONRmjRT94YgKr7ud0GL159izIrTqGuxOL5ukO6RUW1/Oz SfumiYZx7FKIZ7Dcsm5TeGbTuuIh+JRA8w6y1SJvCl+wrxdPHk9Qb+VC3HVk1esT+vMN AFDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745948401; x=1746553201; 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=fEBnczrzjEj5x/y6AtNAt3n1r8SC1W0wf3ug+RcjSOc=; b=M4MKn9OVWa02pk7oc2+bbRaVl0hwQSD5DIk1fj+zWt5jZhznQ4CiTnArji26DlxBM1 9sVMGiilbaQV9WBaChHyvsAKvXtBNM4JA9dLP6lfs/HkLbwXROFbi9UJez3bwwitHJnN MkNVRTWWsf0CiOUf8kxwPiojzV0L7Rt2dCVwfDjtt+MBnySmkZ1sBQeD1m7sZJSWhbyj vpDNnLoQmMQyyD1xbRoo5q4KvMAD8oeNTBE40fPTPuiAnJssP9lGpjcIbRNtTHGyNbBN ja2vp/A/iES5/MY+hf9Q2zdfOWp308KL8mZ1PrdF1iXcrZHFK1OVXSLJwJqUPEHmruqH qx3Q== X-Forwarded-Encrypted: i=1; AJvYcCWYn3SzBjLeGi3dscxJ6D8coXwNvnUyfBWSxZxOdnAsaR9genfveciCkHNizecgItLdVnViTdfuRO/8x/jL@lists.postgresql.org X-Gm-Message-State: AOJu0YxEBgBgWxaB6HxtdAtM64LB0k8ryp2mU/kFJBifgjwTUz2sZvKM q+uQ7dJUbvDBVrGdly7MQhlkvsOJd++YoLDEZvG4CzBVgyUFit8B X-Gm-Gg: ASbGncshvcDkjgZFNyv5xitCfeMqgKgTap1yLnVlhAMP965kCdBjHZUG2rNUNRWIH4v IgAZGBTO/n4Hi2TB8a+dRGVsZTczUp0pC9EJApvyWTKXl6DdvqD6i6b9jopJKPwpTVAkvYHoI8h ecNggy0BuuerzJO5S4B8mrQvydhtEQH69/uzOGmajuP0ZdkECzp3L/RKboimazuTceiaAyAM2Hb opIrqEebqcaNnQB/epyfYbrsDQy/mMwVURd2tpVhqpjbnZdsPH/56MeKOH8/cb2SwBt7wnG6Tj1 +lJ0UKk/tE+M0I93vxALQBuXy2YAhgPF2syQxS1drJRIvIaL5UvTV4Vs0vhy1UPI6oy9p5n7fOq WLZsOqcaT18a7zJSV7MqpCbQQi2Goh00= X-Google-Smtp-Source: AGHT+IEnrPhYYh1ev1xOFiM0ze6S19MSJ6I5qNWP9tYu6cqQUycs7QLWqiUCTBqBcJWI5UMelX2YbA== X-Received: by 2002:a05:6602:7288:b0:85e:2e8b:602 with SMTP id ca18e2360f4ac-86495e22466mr28957439f.2.1745948401534; Tue, 29 Apr 2025 10:40:01 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id ca18e2360f4ac-8648c09a155sm36547339f.32.2025.04.29.10.39.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 29 Apr 2025 10:40:00 -0700 (PDT) Date: Tue, 29 Apr 2025 12:39:58 -0500 From: Nathan Bossart To: Tom Lane Cc: Nisha Moond , Euler Taveira , Michael Paquier , Amit Kapila , Alexander Lakhin , "Jonathan S. Katz" , PostgreSQL Hackers Subject: Re: Large expressions in indexes can't be stored (non-TOASTable) Message-ID: References: <1503201.1745444021@sss.pgh.pa.us> <905635.1745876108@sss.pgh.pa.us> <910625.1745878759@sss.pgh.pa.us> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="K9JbhwB5gisUXW7l" Content-Disposition: inline In-Reply-To: <910625.1745878759@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --K9JbhwB5gisUXW7l Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Mon, Apr 28, 2025 at 06:19:19PM -0400, Tom Lane wrote: > LGTM other than that nit. Thanks. I'll stash this away for July unless someone wants to argue that it's fair game for v18. IMHO this isn't nearly urgent enough for that, and the bugs will continue to exist on older major versions regardless. -- nathan --K9JbhwB5gisUXW7l Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v7-0001-Remove-pg_replication_origin-s-TOAST-table.patch" From 4c015dd4ccc09f193eccb5d447d01d6f5c378397 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Tue, 29 Apr 2025 12:19:15 -0500 Subject: [PATCH v7 1/1] Remove pg_replication_origin's TOAST table. A few places that access this catalog do not set up an active snapshot before potentially accessing its TOAST table, which is unsafe. However, roname (the replication origin name) is the only varlena column, so this is only ever a problem if the name requires out-of-line storage, which seems unlikely. This commit removes its TOAST table to avoid needing to set up a snapshot, and it establishes a length restriction of 512 bytes for replication origin names. Even without this restriction, names that would require out-of-line storage would fail with a "row is too big" error; the additional length restriction just provides a more specific error message. Reviewed-by: Michael Paquier Reviewed-by: Amit Kapila Reviewed-by: Euler Taveira Reviewed-by: Nisha Moond Reviewed-by: Tom Lane Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan --- doc/src/sgml/func.sgml | 1 + src/backend/catalog/catalog.c | 2 -- src/backend/replication/logical/origin.c | 24 ++++++++++++++++++++ src/include/catalog/pg_replication_origin.h | 2 -- src/include/replication/origin.h | 7 ++++++ src/test/regress/expected/misc_functions.out | 4 ++++ src/test/regress/expected/misc_sanity.out | 8 ++++++- src/test/regress/sql/misc_functions.sql | 3 +++ src/test/regress/sql/misc_sanity.sql | 5 ++++ 9 files changed, 51 insertions(+), 5 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 574a544d9fa..593e45495be 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -29941,6 +29941,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset Creates a replication origin with the given external name, and returns the internal ID assigned to it. + The name must be no longer than 512 bytes. diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 60000bd0bc7..59caae8f1bc 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -346,8 +346,6 @@ IsSharedRelation(Oid relationId) relationId == PgDbRoleSettingToastIndex || relationId == PgParameterAclToastTable || relationId == PgParameterAclToastIndex || - relationId == PgReplicationOriginToastTable || - relationId == PgReplicationOriginToastIndex || relationId == PgShdescriptionToastTable || relationId == PgShdescriptionToastIndex || relationId == PgShseclabelToastTable || diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c index 6583dd497da..9e5a09e63e1 100644 --- a/src/backend/replication/logical/origin.c +++ b/src/backend/replication/logical/origin.c @@ -287,6 +287,18 @@ replorigin_create(const char *roname) rel = table_open(ReplicationOriginRelationId, ExclusiveLock); + /* + * We want to be able to access pg_replication_origin without setting up a + * snapshot. To make that safe, it needs to not have a TOAST table, since + * TOASTed data cannot be fetched without a snapshot. As of this writing, + * its TOAST table would only be used for replication origin names, which + * we avoid with a reasonable length restriction. If you add a TOAST + * table to this catalog, be sure to set up a snapshot everywhere it might + * be needed. For more information, see + * https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan. + */ + Assert(!OidIsValid(rel->rd_rel->reltoastrelid)); + for (roident = InvalidOid + 1; roident < PG_UINT16_MAX; roident++) { bool nulls[Natts_pg_replication_origin]; @@ -1296,6 +1308,18 @@ pg_replication_origin_create(PG_FUNCTION_ARGS) elog(WARNING, "replication origins created by regression test cases should have names starting with \"regress_\""); #endif + /* + * To avoid needing a TOAST table for pg_replication_origin, we restrict + * replication origin names to 512 bytes. This should be more than enough + * for all practical use. + */ + if (strlen(name) > MAX_RONAME_LEN) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("replication origin name is too long"), + errdetail("Replication origin names must be no longer than %d bytes.", + MAX_RONAME_LEN))); + roident = replorigin_create(name); pfree(name); diff --git a/src/include/catalog/pg_replication_origin.h b/src/include/catalog/pg_replication_origin.h index deb43065fe9..7ade8bbda39 100644 --- a/src/include/catalog/pg_replication_origin.h +++ b/src/include/catalog/pg_replication_origin.h @@ -54,8 +54,6 @@ CATALOG(pg_replication_origin,6000,ReplicationOriginRelationId) BKI_SHARED_RELAT typedef FormData_pg_replication_origin *Form_pg_replication_origin; -DECLARE_TOAST_WITH_MACRO(pg_replication_origin, 4181, 4182, PgReplicationOriginToastTable, PgReplicationOriginToastIndex); - DECLARE_UNIQUE_INDEX_PKEY(pg_replication_origin_roiident_index, 6001, ReplicationOriginIdentIndex, pg_replication_origin, btree(roident oid_ops)); DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, ReplicationOriginNameIndex, pg_replication_origin, btree(roname text_ops)); diff --git a/src/include/replication/origin.h b/src/include/replication/origin.h index 9cb2248fa9f..29724c0dcae 100644 --- a/src/include/replication/origin.h +++ b/src/include/replication/origin.h @@ -33,6 +33,13 @@ typedef struct xl_replorigin_drop #define InvalidRepOriginId 0 #define DoNotReplicateId PG_UINT16_MAX +/* + * To avoid needing a TOAST table for pg_replication_origin, we restrict + * replication origin names to 512 bytes. This should be more than enough for + * all practical use. + */ +#define MAX_RONAME_LEN 512 + extern PGDLLIMPORT RepOriginId replorigin_session_origin; extern PGDLLIMPORT XLogRecPtr replorigin_session_origin_lsn; extern PGDLLIMPORT TimestampTz replorigin_session_origin_timestamp; diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index d3f5d16a672..3de32c3c7ef 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -914,3 +914,7 @@ SELECT test_relpath(); (1 row) +-- pg_replication_origin.roname length restriction +SELECT pg_replication_origin_create('regress_' || repeat('a', 505)); +ERROR: replication origin name is too long +DETAIL: Replication origin names must be no longer than 512 bytes. diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out index b032a3f4761..21297fc6164 100644 --- a/src/test/regress/expected/misc_sanity.out +++ b/src/test/regress/expected/misc_sanity.out @@ -42,6 +42,11 @@ WHERE refclassid = 0 OR refobjid = 0 OR -- as user data by pg_upgrade, which would cause failures. -- 3. pg_authid, since its toast table cannot be accessed when it would be -- needed, i.e., during authentication before we've selected a database. +-- 4. pg_replication_origin, since we want to be able to access that catalog +-- without setting up a snapshot. To make that safe, it needs to not have a +-- toast table, since toasted data cannot be fetched without a snapshot. As of +-- this writing, its toast table would only be used for replication origin +-- names, which we can avoid with a reasonable length restriction. SELECT relname, attname, atttypid::regtype FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid WHERE c.oid < 16384 AND @@ -61,7 +66,8 @@ ORDER BY 1, 2; pg_class | relpartbound | pg_node_tree pg_largeobject | data | bytea pg_largeobject_metadata | lomacl | aclitem[] -(10 rows) + pg_replication_origin | roname | text +(11 rows) -- system catalogs without primary keys -- diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index aaebb298330..ea72a62dab7 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -411,3 +411,6 @@ CREATE FUNCTION test_relpath() AS :'regresslib' LANGUAGE C; SELECT test_relpath(); + +-- pg_replication_origin.roname length restriction +SELECT pg_replication_origin_create('regress_' || repeat('a', 505)); diff --git a/src/test/regress/sql/misc_sanity.sql b/src/test/regress/sql/misc_sanity.sql index 135793871b4..8ff898843e9 100644 --- a/src/test/regress/sql/misc_sanity.sql +++ b/src/test/regress/sql/misc_sanity.sql @@ -45,6 +45,11 @@ WHERE refclassid = 0 OR refobjid = 0 OR -- as user data by pg_upgrade, which would cause failures. -- 3. pg_authid, since its toast table cannot be accessed when it would be -- needed, i.e., during authentication before we've selected a database. +-- 4. pg_replication_origin, since we want to be able to access that catalog +-- without setting up a snapshot. To make that safe, it needs to not have a +-- toast table, since toasted data cannot be fetched without a snapshot. As of +-- this writing, its toast table would only be used for replication origin +-- names, which we can avoid with a reasonable length restriction. SELECT relname, attname, atttypid::regtype FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid -- 2.39.5 (Apple Git-154) --K9JbhwB5gisUXW7l--