]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Ignore temporary relations in RelidByRelfilenumber() REL_14_STABLE github/REL_14_STABLE
authorMichael Paquier <michael@paquier.xyz>
Fri, 22 Aug 2025 00:06:40 +0000 (09:06 +0900)
committerMichael Paquier <michael@paquier.xyz>
Fri, 22 Aug 2025 00:06:40 +0000 (09:06 +0900)
Temporary relations may share the same RelFileNumber with a permanent
relation, or other temporary relations associated with other sessions.

Being able to uniquely identify a temporary relation would require
RelidByRelfilenumber() to know about the proc number of the temporary
relation it wants to identify, something it is not designed for since
its introduction in f01d1ae3a104.

There are currently three callers of RelidByRelfilenumber():
- autoprewarm.
- Logical decoding, reorder buffer.
- pg_filenode_relation(), that attempts to find a relation OID based on
a tablespace OID and a RelFileNumber.

This makes the situation problematic particularly for the first two
cases, leading to the possibility of random ERRORs due to
inconsistencies that temporary relations can create in the cache
maintained by RelidByRelfilenumber().  The third case should be less of
an issue, as I suspect that there are few direct callers of
pg_filenode_relation().

The window where the ERRORs are happen is very narrow, requiring an OID
wraparound to create a lookup conflict in RelidByRelfilenumber() with a
temporary table reusing the same OID as another relation already cached.
The problem is easier to reach in workloads with a high OID consumption
rate, especially with a higher number of temporary relations created.

We could get pg_filenode_relation() and RelidByRelfilenumber() to work
with temporary relations if provided the means to identify them with an
optional proc number given in input, but the years have also shown that
we do not have a use case for it, yet.  Note that this could not be
backpatched if pg_filenode_relation() needs changes.  It is simpler to
ignore temporary relations.

Reported-by: Shenhao Wang <wangsh.fnst@fujitsu.com>
Author: Vignesh C <vignesh21@gmail.com>
Reviewed-By: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-By: Robert Haas <robertmhaas@gmail.com>
Reviewed-By: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Reviewed-By: Takamichi Osumi <osumi.takamichi@fujitsu.com>
Reviewed-By: Michael Paquier <michael@paquier.xyz>
Reviewed-By: Masahiko Sawada <sawada.mshk@gmail.com>
Reported-By: Shenhao Wang <wangsh.fnst@fujitsu.com>
Discussion: https://postgr.es/m/bbaaf9f9-ebb2-645f-54bb-34d6efc7ac42@fujitsu.com
Backpatch-through: 13

doc/src/sgml/func.sgml
src/backend/utils/adt/dbsize.c
src/backend/utils/cache/relfilenodemap.c
src/test/regress/expected/alter_table.out
src/test/regress/expected/create_table.out
src/test/regress/sql/alter_table.sql
src/test/regress/sql/create_table.sql

index 8fbef434f3687f9a0989e1d55e886cef6c29f437..1c84756639d1b1b0c4e7eafa5b035d277523bc65 100644 (file)
@@ -26634,7 +26634,8 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
         <function>pg_relation_filepath</function>.  For a relation in the
         database's default tablespace, the tablespace can be specified as zero.
         Returns <literal>NULL</literal> if no relation in the current database
-        is associated with the given values.
+        is associated with the given values, or if dealing with a temporary
+        relation.
        </para></entry>
       </row>
      </tbody>
index 9de2ed09d994a2e29750f89dccb7c5167e101d0b..9f42a46f4fc7870ddff19e815f54792462fa6e65 100644 (file)
@@ -900,6 +900,9 @@ pg_relation_filenode(PG_FUNCTION_ARGS)
  *
  * We don't fail but return NULL if we cannot find a mapping.
  *
+ * Temporary relations are not detected, returning NULL (see
+ * RelidByRelfilenumber() for the reasons).
+ *
  * InvalidOid can be passed instead of the current database's default
  * tablespace.
  */
index 56d7c73d3398f90fcb91725f9781514de74fa0d9..6658f9bc473a2f31ad1dfb6c095dcefc95ae12ce 100644 (file)
@@ -132,6 +132,11 @@ InitializeRelfilenodeMap(void)
  * Map a relation's (tablespace, filenode) to a relation's oid and cache the
  * result.
  *
+ * A temporary relation may share its relfilenumber with a permanent relation
+ * or temporary relations created in other backends.  Being able to uniquely
+ * identify a temporary relation would require a backend's proc number, which
+ * we do not know about.  Hence, this function ignores this case.
+ *
  * Returns InvalidOid if no relation matching the criteria could be found.
  */
 Oid
@@ -211,6 +216,9 @@ RelidByRelfilenode(Oid reltablespace, Oid relfilenode)
                {
                        Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
 
+                       if (classform->relpersistence == RELPERSISTENCE_TEMP)
+                               continue;
+
                        if (found)
                                elog(ERROR,
                                         "unexpected duplicate for tablespace %u, relfilenode %u",
index 3305c6ed24a42102f7cd0b3b8e9f34de6196672f..fcae98a7affa00bbff161482baabc1f0cbc20ea2 100644 (file)
@@ -3460,12 +3460,15 @@ SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment F
 -- filenode function call can return NULL for a relation dropped concurrently
 -- with the call's surrounding query, so ignore a NULL mapped_oid for
 -- relations that no longer exist after all calls finish.
+-- Temporary relations are ignored, as not supported by pg_filenode_relation().
 CREATE TEMP TABLE filenode_mapping AS
 SELECT
     oid, mapped_oid, reltablespace, relfilenode, relname
 FROM pg_class,
     pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid
-WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid;
+WHERE relkind IN ('r', 'i', 'S', 't', 'm')
+  AND relpersistence != 't'
+  AND mapped_oid IS DISTINCT FROM oid;
 SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid
 WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL;
  oid | mapped_oid | reltablespace | relfilenode | relname 
index a958b8497999266491bf7cace2ec6667e53089db..a89a84bbb763f7dbf55736bc4e322ffd459b9bfa 100644 (file)
@@ -297,6 +297,18 @@ ERROR:  tables declared WITH OIDS are not supported
 -- but explicitly not adding oids is still supported
 CREATE TEMP TABLE withoutoid() WITHOUT OIDS; DROP TABLE withoutoid;
 CREATE TEMP TABLE withoutoid() WITH (oids = false); DROP TABLE withoutoid;
+-- temporary tables are ignored by pg_filenode_relation().
+CREATE TEMP TABLE relation_filenode_check(c1 int);
+SELECT relpersistence,
+  pg_filenode_relation (reltablespace, pg_relation_filenode(oid))
+  FROM pg_class
+  WHERE relname = 'relation_filenode_check';
+ relpersistence | pg_filenode_relation 
+----------------+----------------------
+ t              | 
+(1 row)
+
+DROP TABLE relation_filenode_check;
 -- check restriction with default expressions
 -- invalid use of column reference in default expressions
 CREATE TABLE default_expr_column (id int DEFAULT (id));
index b7aa3ee179f65b9cae7d2855e454c13321a0617f..fe59ff42546b0b5eed342662ff1786d42d72123f 100644 (file)
@@ -2180,13 +2180,15 @@ SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment F
 -- filenode function call can return NULL for a relation dropped concurrently
 -- with the call's surrounding query, so ignore a NULL mapped_oid for
 -- relations that no longer exist after all calls finish.
+-- Temporary relations are ignored, as not supported by pg_filenode_relation().
 CREATE TEMP TABLE filenode_mapping AS
 SELECT
     oid, mapped_oid, reltablespace, relfilenode, relname
 FROM pg_class,
     pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid
-WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid;
-
+WHERE relkind IN ('r', 'i', 'S', 't', 'm')
+  AND relpersistence != 't'
+  AND mapped_oid IS DISTINCT FROM oid;
 SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid
 WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL;
 
index cc41f58ba22a73e82c220c3666a4fdb5834cf59a..fa198339ae9cf39b609583eeb7fdd3b3f1462893 100644 (file)
@@ -304,6 +304,14 @@ CREATE TABLE withoid() WITH (oids = true);
 CREATE TEMP TABLE withoutoid() WITHOUT OIDS; DROP TABLE withoutoid;
 CREATE TEMP TABLE withoutoid() WITH (oids = false); DROP TABLE withoutoid;
 
+-- temporary tables are ignored by pg_filenode_relation().
+CREATE TEMP TABLE relation_filenode_check(c1 int);
+SELECT relpersistence,
+  pg_filenode_relation (reltablespace, pg_relation_filenode(oid))
+  FROM pg_class
+  WHERE relname = 'relation_filenode_check';
+DROP TABLE relation_filenode_check;
+
 -- check restriction with default expressions
 -- invalid use of column reference in default expressions
 CREATE TABLE default_expr_column (id int DEFAULT (id));