diff options
Diffstat (limited to 'src/backend/storage/large_object/inv_api.c')
-rw-r--r-- | src/backend/storage/large_object/inv_api.c | 165 |
1 files changed, 164 insertions, 1 deletions
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index 9e033c436c9..7becf160b49 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.122 2007/02/27 23:48:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.123 2007/03/03 19:52:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -681,3 +681,166 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) return nwritten; } + +void +inv_truncate(LargeObjectDesc *obj_desc, int len) +{ + int32 pageno = (int32) (len / LOBLKSIZE); + int off; + ScanKeyData skey[2]; + IndexScanDesc sd; + HeapTuple oldtuple; + Form_pg_largeobject olddata; + struct + { + bytea hdr; + char data[LOBLKSIZE]; + } workbuf; + char *workb = VARDATA(&workbuf.hdr); + HeapTuple newtup; + Datum values[Natts_pg_largeobject]; + char nulls[Natts_pg_largeobject]; + char replace[Natts_pg_largeobject]; + CatalogIndexState indstate; + + Assert(PointerIsValid(obj_desc)); + + /* enforce writability because snapshot is probably wrong otherwise */ + if ((obj_desc->flags & IFS_WRLOCK) == 0) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("large object %u was not opened for writing", + obj_desc->id))); + + open_lo_relation(); + + indstate = CatalogOpenIndexes(lo_heap_r); + + ScanKeyInit(&skey[0], + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(obj_desc->id)); + + ScanKeyInit(&skey[1], + Anum_pg_largeobject_pageno, + BTGreaterEqualStrategyNumber, F_INT4GE, + Int32GetDatum(pageno)); + + sd = index_beginscan(lo_heap_r, lo_index_r, + obj_desc->snapshot, 2, skey); + + /* + * If possible, get the page the truncation point is in. + * The truncation point may be beyond the end of the LO or + * in a hole. + */ + olddata = NULL; + if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) + { + olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple); + Assert(olddata->pageno >= pageno); + } + + /* + * If we found the page of the truncation point we need to + * truncate the data in it. Otherwise if we're in a hole, + * we need to create a page to mark the end of data. + */ + if (olddata != NULL && olddata->pageno == pageno) + { + /* First, load old data into workbuf */ + bytea *datafield = &(olddata->data); + bool pfreeit = false; + int pagelen; + + if (VARATT_IS_EXTENDED(datafield)) + { + datafield = (bytea *) + heap_tuple_untoast_attr((varattrib *) datafield); + pfreeit = true; + } + pagelen = getbytealen(datafield); + Assert(pagelen <= LOBLKSIZE); + memcpy(workb, VARDATA(datafield), pagelen); + if (pfreeit) + pfree(datafield); + + /* + * Fill any hole + */ + off = len % LOBLKSIZE; + if (off > pagelen) + MemSet(workb + pagelen, 0, off - pagelen); + + /* compute length of new page */ + SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); + + /* + * Form and insert updated tuple + */ + memset(values, 0, sizeof(values)); + memset(nulls, ' ', sizeof(nulls)); + memset(replace, ' ', sizeof(replace)); + values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); + replace[Anum_pg_largeobject_data - 1] = 'r'; + newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r), + values, nulls, replace); + simple_heap_update(lo_heap_r, &newtup->t_self, newtup); + CatalogIndexInsert(indstate, newtup); + heap_freetuple(newtup); + } + else + { + /* + * If the first page we found was after the truncation + * point, we're in a hole that we'll fill, but we need to + * delete the later page. + */ + if (olddata != NULL && olddata->pageno > pageno) + simple_heap_delete(lo_heap_r, &oldtuple->t_self); + + /* + * Write a brand new page. + * + * Fill the hole up to the truncation point + */ + off = len % LOBLKSIZE; + if (off > 0) + MemSet(workb, 0, off); + + /* compute length of new page */ + SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); + + /* + * Form and insert new tuple + */ + memset(values, 0, sizeof(values)); + memset(nulls, ' ', sizeof(nulls)); + values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id); + values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); + values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); + newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls); + simple_heap_insert(lo_heap_r, newtup); + CatalogIndexInsert(indstate, newtup); + heap_freetuple(newtup); + } + + /* + * Delete any pages after the truncation point + */ + while ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) + { + simple_heap_delete(lo_heap_r, &oldtuple->t_self); + } + + index_endscan(sd); + + CatalogCloseIndexes(indstate); + + /* + * Advance command counter so that tuple updates will be seen by later + * large-object operations in this transaction. + */ + CommandCounterIncrement(); +} + |