|
74 | 74 |
|
75 | 75 |
|
76 | 76 | staticvoidtoast_delete_datum(Relationrel,Datumvalue);
|
77 |
| -staticDatumtoast_save_datum(Relationrel,Datumvalue,intoptions); |
| 77 | +staticDatumtoast_save_datum(Relationrel,Datumvalue, |
| 78 | +structvarlena*oldexternal,intoptions); |
| 79 | +staticbooltoast_valueid_exists(Oidtoastrelid,Oidvalueid); |
78 | 80 | staticstructvarlena*toast_fetch_datum(structvarlena*attr);
|
79 | 81 | staticstructvarlena*toast_fetch_datum_slice(structvarlena*attr,
|
80 | 82 | int32sliceoffset,int32length);
|
@@ -431,6 +433,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
431 | 433 | booltoast_oldisnull[MaxHeapAttributeNumber];
|
432 | 434 | Datumtoast_values[MaxHeapAttributeNumber];
|
433 | 435 | Datumtoast_oldvalues[MaxHeapAttributeNumber];
|
| 436 | +structvarlena*toast_oldexternal[MaxHeapAttributeNumber]; |
434 | 437 | int32toast_sizes[MaxHeapAttributeNumber];
|
435 | 438 | booltoast_free[MaxHeapAttributeNumber];
|
436 | 439 | booltoast_delold[MaxHeapAttributeNumber];
|
@@ -466,6 +469,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
466 | 469 | * ----------
|
467 | 470 | */
|
468 | 471 | memset(toast_action,' ',numAttrs*sizeof(char));
|
| 472 | +memset(toast_oldexternal,0,numAttrs*sizeof(structvarlena*)); |
469 | 473 | memset(toast_free,0,numAttrs*sizeof(bool));
|
470 | 474 | memset(toast_delold,0,numAttrs*sizeof(bool));
|
471 | 475 |
|
@@ -550,6 +554,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
550 | 554 | */
|
551 | 555 | if (VARATT_IS_EXTERNAL(new_value))
|
552 | 556 | {
|
| 557 | +toast_oldexternal[i]=new_value; |
553 | 558 | if (att[i]->attstorage=='p')
|
554 | 559 | new_value=heap_tuple_untoast_attr(new_value);
|
555 | 560 | else
|
@@ -676,7 +681,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
676 | 681 | {
|
677 | 682 | old_value=toast_values[i];
|
678 | 683 | toast_action[i]='p';
|
679 |
| -toast_values[i]=toast_save_datum(rel,toast_values[i],options); |
| 684 | +toast_values[i]=toast_save_datum(rel,toast_values[i], |
| 685 | +toast_oldexternal[i],options); |
680 | 686 | if (toast_free[i])
|
681 | 687 | pfree(DatumGetPointer(old_value));
|
682 | 688 | toast_free[i]= true;
|
@@ -726,7 +732,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
726 | 732 | i=biggest_attno;
|
727 | 733 | old_value=toast_values[i];
|
728 | 734 | toast_action[i]='p';
|
729 |
| -toast_values[i]=toast_save_datum(rel,toast_values[i],options); |
| 735 | +toast_values[i]=toast_save_datum(rel,toast_values[i], |
| 736 | +toast_oldexternal[i],options); |
730 | 737 | if (toast_free[i])
|
731 | 738 | pfree(DatumGetPointer(old_value));
|
732 | 739 | toast_free[i]= true;
|
@@ -839,7 +846,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
|
839 | 846 | i=biggest_attno;
|
840 | 847 | old_value=toast_values[i];
|
841 | 848 | toast_action[i]='p';
|
842 |
| -toast_values[i]=toast_save_datum(rel,toast_values[i],options); |
| 849 | +toast_values[i]=toast_save_datum(rel,toast_values[i], |
| 850 | +toast_oldexternal[i],options); |
843 | 851 | if (toast_free[i])
|
844 | 852 | pfree(DatumGetPointer(old_value));
|
845 | 853 | toast_free[i]= true;
|
@@ -1117,10 +1125,16 @@ toast_compress_datum(Datum value)
|
1117 | 1125 | *
|
1118 | 1126 | *Save one single datum into the secondary relation and return
|
1119 | 1127 | *a Datum reference for it.
|
| 1128 | + * |
| 1129 | + * rel: the main relation we're working with (not the toast rel!) |
| 1130 | + * value: datum to be pushed to toast storage |
| 1131 | + * oldexternal: if not NULL, toast pointer previously representing the datum |
| 1132 | + * options: options to be passed to heap_insert() for toast rows |
1120 | 1133 | * ----------
|
1121 | 1134 | */
|
1122 | 1135 | staticDatum
|
1123 |
| -toast_save_datum(Relationrel,Datumvalue,intoptions) |
| 1136 | +toast_save_datum(Relationrel,Datumvalue, |
| 1137 | +structvarlena*oldexternal,intoptions) |
1124 | 1138 | {
|
1125 | 1139 | Relationtoastrel;
|
1126 | 1140 | Relationtoastidx;
|
@@ -1199,11 +1213,55 @@ toast_save_datum(Relation rel, Datum value, int options)
|
1199 | 1213 | toast_pointer.va_toastrelid=RelationGetRelid(toastrel);
|
1200 | 1214 |
|
1201 | 1215 | /*
|
1202 |
| - * Choose an unused OID within the toast table for this toast value. |
| 1216 | + * Choose an OID to use as the value ID for this toast value. |
| 1217 | + * |
| 1218 | + * Normally we just choose an unused OID within the toast table. But |
| 1219 | + * during table-rewriting operations where we are preserving an existing |
| 1220 | + * toast table OID, we want to preserve toast value OIDs too. So, if |
| 1221 | + * rd_toastoid is set and we had a prior external value from that same |
| 1222 | + * toast table, re-use its value ID. If we didn't have a prior external |
| 1223 | + * value (which is a corner case, but possible if the table's attstorage |
| 1224 | + * options have been changed), we have to pick a value ID that doesn't |
| 1225 | + * conflict with either new or existing toast value OIDs. |
1203 | 1226 | */
|
1204 |
| -toast_pointer.va_valueid=GetNewOidWithIndex(toastrel, |
1205 |
| -RelationGetRelid(toastidx), |
1206 |
| - (AttrNumber)1); |
| 1227 | +if (!OidIsValid(rel->rd_toastoid)) |
| 1228 | +{ |
| 1229 | +/* normal case: just choose an unused OID */ |
| 1230 | +toast_pointer.va_valueid= |
| 1231 | +GetNewOidWithIndex(toastrel, |
| 1232 | +RelationGetRelid(toastidx), |
| 1233 | + (AttrNumber)1); |
| 1234 | +} |
| 1235 | +else |
| 1236 | +{ |
| 1237 | +/* rewrite case: check to see if value was in old toast table */ |
| 1238 | +toast_pointer.va_valueid=InvalidOid; |
| 1239 | +if (oldexternal!=NULL) |
| 1240 | +{ |
| 1241 | +structvaratt_externalold_toast_pointer; |
| 1242 | + |
| 1243 | +Assert(VARATT_IS_EXTERNAL(oldexternal)); |
| 1244 | +/* Must copy to access aligned fields */ |
| 1245 | +VARATT_EXTERNAL_GET_POINTER(old_toast_pointer,oldexternal); |
| 1246 | +if (old_toast_pointer.va_toastrelid==rel->rd_toastoid) |
| 1247 | +toast_pointer.va_valueid=old_toast_pointer.va_valueid; |
| 1248 | +} |
| 1249 | +if (toast_pointer.va_valueid==InvalidOid) |
| 1250 | +{ |
| 1251 | +/* |
| 1252 | + * new value; must choose an OID that doesn't conflict in either |
| 1253 | + * old or new toast table |
| 1254 | + */ |
| 1255 | +do |
| 1256 | +{ |
| 1257 | +toast_pointer.va_valueid= |
| 1258 | +GetNewOidWithIndex(toastrel, |
| 1259 | +RelationGetRelid(toastidx), |
| 1260 | + (AttrNumber)1); |
| 1261 | +}while (toast_valueid_exists(rel->rd_toastoid, |
| 1262 | +toast_pointer.va_valueid)); |
| 1263 | +} |
| 1264 | +} |
1207 | 1265 |
|
1208 | 1266 | /*
|
1209 | 1267 | * Initialize constant parts of the tuple data
|
@@ -1338,6 +1396,52 @@ toast_delete_datum(Relation rel, Datum value)
|
1338 | 1396 | }
|
1339 | 1397 |
|
1340 | 1398 |
|
| 1399 | +/* ---------- |
| 1400 | + * toast_valueid_exists - |
| 1401 | + * |
| 1402 | + *Test whether a toast value with the given ID exists in the toast relation |
| 1403 | + * ---------- |
| 1404 | + */ |
| 1405 | +staticbool |
| 1406 | +toast_valueid_exists(Oidtoastrelid,Oidvalueid) |
| 1407 | +{ |
| 1408 | +boolresult= false; |
| 1409 | +Relationtoastrel; |
| 1410 | +ScanKeyDatatoastkey; |
| 1411 | +SysScanDesctoastscan; |
| 1412 | + |
| 1413 | +/* |
| 1414 | + * Open the toast relation |
| 1415 | + */ |
| 1416 | +toastrel=heap_open(toastrelid,AccessShareLock); |
| 1417 | + |
| 1418 | +/* |
| 1419 | + * Setup a scan key to find chunks with matching va_valueid |
| 1420 | + */ |
| 1421 | +ScanKeyInit(&toastkey, |
| 1422 | +(AttrNumber)1, |
| 1423 | +BTEqualStrategyNumber,F_OIDEQ, |
| 1424 | +ObjectIdGetDatum(valueid)); |
| 1425 | + |
| 1426 | +/* |
| 1427 | + * Is there any such chunk? |
| 1428 | + */ |
| 1429 | +toastscan=systable_beginscan(toastrel,toastrel->rd_rel->reltoastidxid, |
| 1430 | + true,SnapshotToast,1,&toastkey); |
| 1431 | + |
| 1432 | +if (systable_getnext(toastscan)!=NULL) |
| 1433 | +result= true; |
| 1434 | + |
| 1435 | +/* |
| 1436 | + * End scan and close relations |
| 1437 | + */ |
| 1438 | +systable_endscan(toastscan); |
| 1439 | +heap_close(toastrel,AccessShareLock); |
| 1440 | + |
| 1441 | +returnresult; |
| 1442 | +} |
| 1443 | + |
| 1444 | + |
1341 | 1445 | /* ----------
|
1342 | 1446 | * toast_fetch_datum -
|
1343 | 1447 | *
|
|