]> granicus.if.org Git - postgresql/commitdiff
When a relation is moved to another tablespace, we can't assume that we can
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 7 Oct 2008 11:16:01 +0000 (11:16 +0000)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 7 Oct 2008 11:16:01 +0000 (11:16 +0000)
use the old relfilenode in the new tablespace. There might be another relation
in the new tablespace with the same relfilenode, so we must generate a fresh
relfilenode in the new tablespace.

The 8.3 patch to let deleted relation files linger as zero-length files until
the next checkpoint made this more obvious: moving a relation from one table
space another, and then back again, caused a collision with the lingering
file.

Back-patch to 8.1. The issue is present in 8.0 as well, but it doesn't seem
worth fixing there, because we didn't have protection from OID collisions
after OID wraparound before 8.1.

Report by Guillaume Lelarge.

src/backend/commands/tablecmds.c

index 1350b0d0269618e0f06f24e87c70a8c5956f5bb4..f54902f5daa48ae7ce6254753e762b30586a0df2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.174.2.7 2008/05/27 21:13:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.174.2.8 2008/10/07 11:16:01 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5716,6 +5716,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
        Oid                     oldTableSpace;
        Oid                     reltoastrelid;
        Oid                     reltoastidxid;
+       Oid                     newrelfilenode;
        RelFileNode newrnode;
        SMgrRelation dstrel;
        Relation        pg_class;
@@ -5767,9 +5768,18 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
                elog(ERROR, "cache lookup failed for relation %u", tableOid);
        rd_rel = (Form_pg_class) GETSTRUCT(tuple);
 
+       /*
+        * Relfilenodes are not unique across tablespaces, so we need to allocate
+        * a new one in the new tablespace.
+        */
+       newrelfilenode = GetNewRelFileNode(newTableSpace,
+                                                                          rel->rd_rel->relisshared,
+                                                                          NULL);
+
        /* create another storage file. Is it a little ugly ? */
        /* NOTE: any conflict in relfilenode value will be caught here */
        newrnode = rel->rd_node;
+       newrnode.relNode = newrelfilenode;
        newrnode.spcNode = newTableSpace;
 
        dstrel = smgropen(newrnode);
@@ -5790,6 +5800,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
 
        /* update the pg_class row */
        rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
+       rd_rel->relfilenode = newrelfilenode;
        simple_heap_update(pg_class, &tuple->t_self, tuple);
        CatalogUpdateIndexes(pg_class, tuple);