aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/large_object/inv_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/large_object/inv_api.c')
-rw-r--r--src/backend/storage/large_object/inv_api.c165
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();
+}
+