Skip to content

Commit 16bf24e

Browse files
Remove pg_replication_origin's TOAST table.
A few places that access this catalog don't set up an active snapshot before potentially accessing its TOAST table. However, roname (the replication origin name) is the only varlena column, so this is only a problem if the name requires out-of-line storage. This commit removes its TOAST table to avoid needing to set up a snapshot. It also places a limit on replication origin names so that attempts to set long names will fail with a more user-friendly error. Those chosen limit of 512 bytes should be sufficient to avoid "row is too big" errors independent of BLCKSZ, but it should also be lenient enough for all reasonable use-cases. Bumps catversion. Reviewed-by: Michael Paquier <michael@paquier.xyz> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Reviewed-by: Euler Taveira <euler@eulerto.com> Reviewed-by: Nisha Moond <nisha.moond412@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
1 parent 5f4d98d commit 16bf24e

File tree

10 files changed

+51
-6
lines changed

10 files changed

+51
-6
lines changed

doc/src/sgml/func.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29940,6 +29940,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2994029940
<para>
2994129941
Creates a replication origin with the given external
2994229942
name, and returns the internal ID assigned to it.
29943+
The name must be no longer than 512 bytes.
2994329944
</para></entry>
2994429945
</row>
2994529946

src/backend/catalog/catalog.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,6 @@ IsSharedRelation(Oid relationId)
346346
relationId == PgDbRoleSettingToastIndex ||
347347
relationId == PgParameterAclToastTable ||
348348
relationId == PgParameterAclToastIndex ||
349-
relationId == PgReplicationOriginToastTable ||
350-
relationId == PgReplicationOriginToastIndex ||
351349
relationId == PgShdescriptionToastTable ||
352350
relationId == PgShdescriptionToastIndex ||
353351
relationId == PgShseclabelToastTable ||

src/backend/replication/logical/origin.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ replorigin_create(const char *roname)
264264
SysScanDesc scan;
265265
ScanKeyData key;
266266

267+
/*
268+
* To avoid needing a TOAST table for pg_replication_origin, we limit
269+
* replication origin names to 512 bytes. This should be more than enough
270+
* for all practical use.
271+
*/
272+
if (strlen(roname) > MAX_RONAME_LEN)
273+
ereport(ERROR,
274+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
275+
errmsg("replication origin name is too long"),
276+
errdetail("Replication origin names must be no longer than %d bytes.",
277+
MAX_RONAME_LEN)));
278+
267279
roname_d = CStringGetTextDatum(roname);
268280

269281
Assert(IsTransactionState());
@@ -287,6 +299,17 @@ replorigin_create(const char *roname)
287299

288300
rel = table_open(ReplicationOriginRelationId, ExclusiveLock);
289301

302+
/*
303+
* We want to be able to access pg_replication_origin without setting up a
304+
* snapshot. To make that safe, it needs to not have a TOAST table, since
305+
* TOASTed data cannot be fetched without a snapshot. As of this writing,
306+
* its only varlena column is roname, which we limit to 512 bytes to avoid
307+
* needing out-of-line storage. If you add a TOAST table to this catalog,
308+
* be sure to set up a snapshot everywhere it might be needed. For more
309+
* information, see https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan.
310+
*/
311+
Assert(!OidIsValid(rel->rd_rel->reltoastrelid));
312+
290313
for (roident = InvalidOid + 1; roident < PG_UINT16_MAX; roident++)
291314
{
292315
bool nulls[Natts_pg_replication_origin];

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202504091
60+
#define CATALOG_VERSION_NO 202505071
6161

6262
#endif

src/include/catalog/pg_replication_origin.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ CATALOG(pg_replication_origin,6000,ReplicationOriginRelationId) BKI_SHARED_RELAT
5454

5555
typedef FormData_pg_replication_origin *Form_pg_replication_origin;
5656

57-
DECLARE_TOAST_WITH_MACRO(pg_replication_origin, 4181, 4182, PgReplicationOriginToastTable, PgReplicationOriginToastIndex);
58-
5957
DECLARE_UNIQUE_INDEX_PKEY(pg_replication_origin_roiident_index, 6001, ReplicationOriginIdentIndex, pg_replication_origin, btree(roident oid_ops));
6058
DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, ReplicationOriginNameIndex, pg_replication_origin, btree(roname text_ops));
6159

src/include/replication/origin.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ typedef struct xl_replorigin_drop
3333
#define InvalidRepOriginId 0
3434
#define DoNotReplicateId PG_UINT16_MAX
3535

36+
/*
37+
* To avoid needing a TOAST table for pg_replication_origin, we limit
38+
* replication origin names to 512 bytes. This should be more than enough for
39+
* all practical use.
40+
*/
41+
#define MAX_RONAME_LEN 512
42+
3643
extern PGDLLIMPORT RepOriginId replorigin_session_origin;
3744
extern PGDLLIMPORT XLogRecPtr replorigin_session_origin_lsn;
3845
extern PGDLLIMPORT TimestampTz replorigin_session_origin_timestamp;

src/test/regress/expected/misc_functions.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,3 +914,7 @@ SELECT test_relpath();
914914

915915
(1 row)
916916

917+
-- pg_replication_origin.roname limit
918+
SELECT pg_replication_origin_create('regress_' || repeat('a', 505));
919+
ERROR: replication origin name is too long
920+
DETAIL: Replication origin names must be no longer than 512 bytes.

src/test/regress/expected/misc_sanity.out

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ WHERE refclassid = 0 OR refobjid = 0 OR
4242
-- as user data by pg_upgrade, which would cause failures.
4343
-- 3. pg_authid, since its toast table cannot be accessed when it would be
4444
-- needed, i.e., during authentication before we've selected a database.
45+
-- 4. pg_replication_origin, since we want to be able to access that catalog
46+
-- without setting up a snapshot. To make that safe, it needs to not have a
47+
-- toast table, since toasted data cannot be fetched without a snapshot. As of
48+
-- this writing, its only varlena column is roname, which we limit to 512 bytes
49+
-- to avoid needing out-of-line storage.
4550
SELECT relname, attname, atttypid::regtype
4651
FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid
4752
WHERE c.oid < 16384 AND
@@ -61,7 +66,8 @@ ORDER BY 1, 2;
6166
pg_class | relpartbound | pg_node_tree
6267
pg_largeobject | data | bytea
6368
pg_largeobject_metadata | lomacl | aclitem[]
64-
(10 rows)
69+
pg_replication_origin | roname | text
70+
(11 rows)
6571

6672
-- system catalogs without primary keys
6773
--

src/test/regress/sql/misc_functions.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,6 @@ CREATE FUNCTION test_relpath()
411411
AS :'regresslib'
412412
LANGUAGE C;
413413
SELECT test_relpath();
414+
415+
-- pg_replication_origin.roname limit
416+
SELECT pg_replication_origin_create('regress_' || repeat('a', 505));

src/test/regress/sql/misc_sanity.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ WHERE refclassid = 0 OR refobjid = 0 OR
4545
-- as user data by pg_upgrade, which would cause failures.
4646
-- 3. pg_authid, since its toast table cannot be accessed when it would be
4747
-- needed, i.e., during authentication before we've selected a database.
48+
-- 4. pg_replication_origin, since we want to be able to access that catalog
49+
-- without setting up a snapshot. To make that safe, it needs to not have a
50+
-- toast table, since toasted data cannot be fetched without a snapshot. As of
51+
-- this writing, its only varlena column is roname, which we limit to 512 bytes
52+
-- to avoid needing out-of-line storage.
4853

4954
SELECT relname, attname, atttypid::regtype
5055
FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid

0 commit comments

Comments
 (0)