Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit21b446d

Browse files
committed
Fix CLUSTER/VACUUM FULL for toast values owned by recently-updated rows.
In commit7b0d0e9, I made CLUSTER andVACUUM FULL try to preserve toast value OIDs from the original toast tableto the new one. However, if we have to copy both live and recently-deadversions of a row that has a toasted column, those versions may wellreference the same toast value with the same OID. The patch then led toduplicate-key failures as we tried to insert the toast value twice with thesame OID. (The previous behavior was not very desirable either, since itwould have silently inserted the same value twice with different OIDs.That wastes space, but what's worse is that the toast values inserted foralready-dead heap rows would not be reclaimed by subsequent ordinaryVACUUMs, since they go into the new toast table marked live not deleted.)To fix, check if the copied OID already exists in the new toast table, andif so, assume that it stores the desired value. This is reasonably safesince the only case where we will copy an OID from a previous toast pointeris when toast_insert_or_update was given that toast pointer and so we justpulled the data from the old table; if we got two different values that waythen we have big problems anyway. We do have to assume that no otherbackend is inserting items into the new toast table concurrently, butthat's surely safe for CLUSTER and VACUUM FULL.Per bug #6393 from Maxim Boguk. Back-patch to 9.0, same as the previouspatch.
1 parentde5a08c commit21b446d

File tree

3 files changed

+62
-19
lines changed

3 files changed

+62
-19
lines changed

‎src/backend/access/heap/tuptoaster.c

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ do { \
7676
staticvoidtoast_delete_datum(Relationrel,Datumvalue);
7777
staticDatumtoast_save_datum(Relationrel,Datumvalue,
7878
structvarlena*oldexternal,intoptions);
79-
staticbooltoast_valueid_exists(Oidtoastrelid,Oidvalueid);
79+
staticbooltoastrel_valueid_exists(Relationtoastrel,Oidvalueid);
80+
staticbooltoastid_valueid_exists(Oidtoastrelid,Oidvalueid);
8081
staticstructvarlena*toast_fetch_datum(structvarlena*attr);
8182
staticstructvarlena*toast_fetch_datum_slice(structvarlena*attr,
8283
int32sliceoffset,int32length);
@@ -1342,7 +1343,34 @@ toast_save_datum(Relation rel, Datum value,
13421343
/* Must copy to access aligned fields */
13431344
VARATT_EXTERNAL_GET_POINTER(old_toast_pointer,oldexternal);
13441345
if (old_toast_pointer.va_toastrelid==rel->rd_toastoid)
1346+
{
1347+
/* This value came from the old toast table; reuse its OID */
13451348
toast_pointer.va_valueid=old_toast_pointer.va_valueid;
1349+
1350+
/*
1351+
* There is a corner case here: the table rewrite might have
1352+
* to copy both live and recently-dead versions of a row, and
1353+
* those versions could easily reference the same toast value.
1354+
* When we copy the second or later version of such a row,
1355+
* reusing the OID will mean we select an OID that's already
1356+
* in the new toast table. Check for that, and if so, just
1357+
* fall through without writing the data again.
1358+
*
1359+
* While annoying and ugly-looking, this is a good thing
1360+
* because it ensures that we wind up with only one copy of
1361+
* the toast value when there is only one copy in the old
1362+
* toast table. Before we detected this case, we'd have made
1363+
* multiple copies, wasting space; and what's worse, the
1364+
* copies belonging to already-deleted heap tuples would not
1365+
* be reclaimed by VACUUM.
1366+
*/
1367+
if (toastrel_valueid_exists(toastrel,
1368+
toast_pointer.va_valueid))
1369+
{
1370+
/* Match, so short-circuit the data storage loop below */
1371+
data_todo=0;
1372+
}
1373+
}
13461374
}
13471375
if (toast_pointer.va_valueid==InvalidOid)
13481376
{
@@ -1356,8 +1384,8 @@ toast_save_datum(Relation rel, Datum value,
13561384
GetNewOidWithIndex(toastrel,
13571385
RelationGetRelid(toastidx),
13581386
(AttrNumber)1);
1359-
}while (toast_valueid_exists(rel->rd_toastoid,
1360-
toast_pointer.va_valueid));
1387+
}while (toastid_valueid_exists(rel->rd_toastoid,
1388+
toast_pointer.va_valueid));
13611389
}
13621390
}
13631391

@@ -1495,24 +1523,18 @@ toast_delete_datum(Relation rel, Datum value)
14951523

14961524

14971525
/* ----------
1498-
*toast_valueid_exists -
1526+
*toastrel_valueid_exists -
14991527
*
15001528
*Test whether a toast value with the given ID exists in the toast relation
15011529
* ----------
15021530
*/
15031531
staticbool
1504-
toast_valueid_exists(Oidtoastrelid,Oidvalueid)
1532+
toastrel_valueid_exists(Relationtoastrel,Oidvalueid)
15051533
{
15061534
boolresult= false;
1507-
Relationtoastrel;
15081535
ScanKeyDatatoastkey;
15091536
SysScanDesctoastscan;
15101537

1511-
/*
1512-
* Open the toast relation
1513-
*/
1514-
toastrel=heap_open(toastrelid,AccessShareLock);
1515-
15161538
/*
15171539
* Setup a scan key to find chunks with matching va_valueid
15181540
*/
@@ -1530,10 +1552,27 @@ toast_valueid_exists(Oid toastrelid, Oid valueid)
15301552
if (systable_getnext(toastscan)!=NULL)
15311553
result= true;
15321554

1533-
/*
1534-
* End scan and close relations
1535-
*/
15361555
systable_endscan(toastscan);
1556+
1557+
returnresult;
1558+
}
1559+
1560+
/* ----------
1561+
* toastid_valueid_exists -
1562+
*
1563+
*As above, but work from toast rel's OID not an open relation
1564+
* ----------
1565+
*/
1566+
staticbool
1567+
toastid_valueid_exists(Oidtoastrelid,Oidvalueid)
1568+
{
1569+
boolresult;
1570+
Relationtoastrel;
1571+
1572+
toastrel=heap_open(toastrelid,AccessShareLock);
1573+
1574+
result=toastrel_valueid_exists(toastrel,valueid);
1575+
15371576
heap_close(toastrel,AccessShareLock);
15381577

15391578
returnresult;

‎src/backend/commands/cluster.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,16 +787,19 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
787787
* When doing swap by content, any toast pointers written into NewHeap
788788
* must use the old toast table's OID, because that's where the toast
789789
* data will eventually be found. Set this up by setting rd_toastoid.
790-
* This also tells tuptoaster.c to preserve the toast value OIDs,
791-
* which we want so as not to invalidate toast pointers in system
792-
* catalog caches.
790+
* This also tells toast_save_datum() to preserve the toast value
791+
* OIDs, which we want so as not to invalidate toast pointers in
792+
* system catalog caches, and to avoid making multiple copies of a
793+
* single toast value.
793794
*
794795
* Note that we must hold NewHeap open until we are done writing data,
795796
* since the relcache will not guarantee to remember this setting once
796797
* the relation is closed.Also, this technique depends on the fact
797798
* that no one will try to read from the NewHeap until after we've
798799
* finished writing it and swapping the rels --- otherwise they could
799-
* follow the toast pointers to the wrong place.
800+
* follow the toast pointers to the wrong place. (It would actually
801+
* work for values copied over from the old toast table, but not for
802+
* any values that we toast which were previously not toasted.)
800803
*/
801804
NewHeap->rd_toastoid=OldHeap->rd_rel->reltoastrelid;
802805
}

‎src/include/utils/rel.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ typedef struct RelationData
159159
* have the existing toast table's OID, not the OID of the transient toast
160160
* table. If rd_toastoid isn't InvalidOid, it is the OID to place in
161161
* toast pointers inserted into this rel. (Note it's set on the new
162-
* version of the main heap, not the toast table itself.)
162+
* version of the main heap, not the toast table itself.) This also
163+
* causes toast_save_datum() to try to preserve toast value OIDs.
163164
*/
164165
Oidrd_toastoid;/* Real TOAST table's OID, or InvalidOid */
165166

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp