Fix the database-wide version of CLUSTER to silently skip temp tables of
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 12 Sep 2007 15:16:23 +0000 (15:16 +0000)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 12 Sep 2007 15:16:23 +0000 (15:16 +0000)
remote sessions, instead of erroring out in the middle of the operation.

This is a backpatch of a previous fix applied to CLUSTER to HEAD and 8.2, all
the way back that it is relevant to.

src/backend/commands/cluster.c

index 4f95dd1b2b70ad3dcc36b7d75dc2ec5c28e65dde..1b724018d7dd98a4babca8bcd2a7a9c7abd6935f 100644 (file)
@@ -291,6 +291,18 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
         */
        OldHeap = heap_open(rvtc->tableOid, AccessExclusiveLock);
 
+       /*
+        * Don't allow cluster on temp tables of other backends ... their
+        * local buffer manager is not going to cope.  In the recheck case,
+        * silently skip it.  Otherwise continue -- there is a hard error
+        * in check_index_is_clusterable.
+        */
+       if (recheck && isOtherTempNamespace(RelationGetNamespace(OldHeap)))
+       {
+               heap_close(OldHeap, AccessExclusiveLock);
+               return;
+       }
+
        /* Check index is valid to cluster on */
        check_index_is_clusterable(OldHeap, rvtc->indexOid);
 
@@ -605,32 +617,80 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
        Relation        NewHeap,
                                OldHeap,
                                OldIndex;
+       TupleDesc       oldTupDesc;
+       TupleDesc       newTupDesc;
+       int                     natts;
+       Datum      *values;
+       char       *nulls;
        IndexScanDesc scan;
        HeapTuple       tuple;
 
        /*
-        * Open the relations I need. Scan through the OldHeap on the OldIndex
-        * and insert each tuple into the NewHeap.
+        * Open the relations we need.
         */
        NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
        OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
        OldIndex = index_open(OIDOldIndex);
 
+       /*
+        * Their tuple descriptors should be exactly alike, but here we only
+        * need assume that they have the same number of columns.
+        */
+       oldTupDesc = RelationGetDescr(OldHeap);
+       newTupDesc = RelationGetDescr(NewHeap);
+       Assert(newTupDesc->natts == oldTupDesc->natts);
+
+       /* Preallocate values/nulls arrays */
+       natts = newTupDesc->natts;
+       values = (Datum *) palloc0(natts * sizeof(Datum));
+       nulls = (char *) palloc(natts * sizeof(char));
+       memset(nulls, 'n', natts * sizeof(char));
+
+       /*
+        * Scan through the OldHeap on the OldIndex and copy each tuple into the
+        * NewHeap.
+        */
        scan = index_beginscan(OldHeap, OldIndex, SnapshotNow, 0, (ScanKey) NULL);
 
        while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
        {
                /*
-                * We must copy the tuple because heap_insert() will overwrite the
-                * commit-status fields of the tuple it's handed, and the
-                * retrieved tuple will actually be in a disk buffer!  Thus, the
-                * source relation would get trashed, which is bad news if we
-                * abort later on.      (This was a bug in releases thru 7.0)
+                * We cannot simply pass the tuple to heap_insert(), for several
+                * reasons:
+                *
+                * 1. heap_insert() will overwrite the commit-status fields of the
+                * tuple it's handed.  This would trash the source relation, which is
+                * bad news if we abort later on.  (This was a bug in releases thru
+                * 7.0)
+                *
+                * 2. We'd like to squeeze out the values of any dropped columns,
+                * both to save space and to ensure we have no corner-case failures.
+                * (It's possible for example that the new table hasn't got a TOAST
+                * table and so is unable to store any large values of dropped cols.)
                 *
-                * Note that the copied tuple will have the original OID, if any, so
-                * this does preserve OIDs.
+                * 3. The tuple might not even be legal for the new table; this is
+                * currently only known to happen as an after-effect of ALTER TABLE
+                * SET WITHOUT OIDS.
+                *
+                * So, we must reconstruct the tuple from component Datums.
                 */
-               HeapTuple       copiedTuple = heap_copytuple(tuple);
+               HeapTuple       copiedTuple;
+               int                     i;
+
+               heap_deformtuple(tuple, oldTupDesc, values, nulls);
+
+               /* Be sure to null out any dropped columns */
+               for (i = 0; i < natts; i++)
+               {
+                       if (newTupDesc->attrs[i]->attisdropped)
+                               nulls[i] = 'n';
+               }
+
+               copiedTuple = heap_formtuple(newTupDesc, values, nulls);
+
+               /* Preserve OID, if any */
+               if (NewHeap->rd_rel->relhasoids)
+                       HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple));
 
                simple_heap_insert(NewHeap, copiedTuple);
 
@@ -641,6 +701,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
 
        index_endscan(scan);
 
+       pfree(values);
+       pfree(nulls);
+
        index_close(OldIndex);
        heap_close(OldHeap, NoLock);
        heap_close(NewHeap, NoLock);