* pg_largeobject.c
* routines to support manipulation of the pg_largeobject relation
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_largeobject.c,v 1.6 2001/01/23 04:32:21 tgl Exp $
+ * src/backend/catalog/pg_largeobject.c
*
*-------------------------------------------------------------------------
*/
#include "access/genam.h"
#include "access/heapam.h"
-#include "catalog/catname.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
#include "miscadmin.h"
-#include "utils/builtins.h"
+#include "utils/acl.h"
#include "utils/fmgroids.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
/*
* Create a large object having the given LO identifier.
*
- * We do this by inserting an empty first page, so that the object will
- * appear to exist with size 0. Note that the unique index will reject
- * an attempt to create a duplicate page.
- *
- * Return value is OID assigned to the page tuple (any use in it?)
+ * We create a new large object by inserting an entry into
+ * pg_largeobject_metadata without any data pages, so that the object
+ * will appear to exist with size 0.
*/
Oid
LargeObjectCreate(Oid loid)
{
- Oid retval;
- Relation pg_largeobject;
+ Relation pg_lo_meta;
HeapTuple ntup;
- Relation idescs[Num_pg_largeobject_indices];
- Datum values[Natts_pg_largeobject];
- char nulls[Natts_pg_largeobject];
- int i;
+ Oid loid_new;
+ Datum values[Natts_pg_largeobject_metadata];
+ bool nulls[Natts_pg_largeobject_metadata];
- pg_largeobject = heap_openr(LargeObjectRelationName, RowExclusiveLock);
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
/*
- * Form new tuple
+ * Insert metadata of the largeobject
*/
- for (i = 0; i < Natts_pg_largeobject; i++)
- {
- values[i] = (Datum)NULL;
- nulls[i] = ' ';
- }
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
- i = 0;
- values[i++] = ObjectIdGetDatum(loid);
- values[i++] = Int32GetDatum(0);
- values[i++] = DirectFunctionCall1(byteain,
- CStringGetDatum(""));
-
- ntup = heap_formtuple(pg_largeobject->rd_att, values, nulls);
+ values[Anum_pg_largeobject_metadata_lomowner - 1]
+ = ObjectIdGetDatum(GetUserId());
+ nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true;
- /*
- * Insert it
- */
- retval = heap_insert(pg_largeobject, ntup);
+ ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta),
+ values, nulls);
+ if (OidIsValid(loid))
+ HeapTupleSetOid(ntup, loid);
- /*
- * Update indices
- */
- if (!IsIgnoringSystemIndexes())
- {
- CatalogOpenIndices(Num_pg_largeobject_indices, Name_pg_largeobject_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_largeobject_indices, pg_largeobject, ntup);
- CatalogCloseIndices(Num_pg_largeobject_indices, idescs);
- }
-
- heap_close(pg_largeobject, RowExclusiveLock);
+ loid_new = simple_heap_insert(pg_lo_meta, ntup);
+ Assert(!OidIsValid(loid) || loid == loid_new);
+
+ CatalogUpdateIndexes(pg_lo_meta, ntup);
heap_freetuple(ntup);
- return retval;
+ heap_close(pg_lo_meta, RowExclusiveLock);
+
+ return loid_new;
}
+/*
+ * Drop a large object having the given LO identifier. Both the data pages
+ * and metadata must be dropped.
+ */
void
LargeObjectDrop(Oid loid)
{
- bool found = false;
+ Relation pg_lo_meta;
Relation pg_largeobject;
- Relation pg_lo_idx;
- ScanKeyData skey[1];
- IndexScanDesc sd;
- RetrieveIndexResult indexRes;
- HeapTupleData tuple;
- Buffer buffer;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
- ScanKeyEntryInitialize(&skey[0],
- (bits16) 0x0,
- (AttrNumber) 1,
- (RegProcedure) F_OIDEQ,
- ObjectIdGetDatum(loid));
+ pg_largeobject = heap_open(LargeObjectRelationId,
+ RowExclusiveLock);
- pg_largeobject = heap_openr(LargeObjectRelationName, RowShareLock);
- pg_lo_idx = index_openr(LargeObjectLOidPNIndex);
+ /*
+ * Delete an entry from pg_largeobject_metadata
+ */
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ NULL, 1, skey);
- sd = index_beginscan(pg_lo_idx, false, 1, skey);
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", loid)));
- tuple.t_datamcxt = CurrentMemoryContext;
- tuple.t_data = NULL;
+ simple_heap_delete(pg_lo_meta, &tuple->t_self);
- while ((indexRes = index_getnext(sd, ForwardScanDirection)))
+ systable_endscan(scan);
+
+ /*
+ * Delete all the associated entries from pg_largeobject
+ */
+ ScanKeyInit(&skey[0],
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(pg_largeobject,
+ LargeObjectLOidPNIndexId, true,
+ NULL, 1, skey);
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
- tuple.t_self = indexRes->heap_iptr;
- heap_fetch(pg_largeobject, SnapshotNow, &tuple, &buffer);
- pfree(indexRes);
- if (tuple.t_data != NULL)
- {
- simple_heap_delete(pg_largeobject, &tuple.t_self);
- ReleaseBuffer(buffer);
- found = true;
- }
+ simple_heap_delete(pg_largeobject, &tuple->t_self);
}
- index_endscan(sd);
+ systable_endscan(scan);
- index_close(pg_lo_idx);
- heap_close(pg_largeobject, RowShareLock);
+ heap_close(pg_largeobject, RowExclusiveLock);
- if (!found)
- elog(ERROR, "LargeObjectDrop: large object %u not found", loid);
+ heap_close(pg_lo_meta, RowExclusiveLock);
}
+/*
+ * LargeObjectExists
+ *
+ * We don't use the system cache for large object metadata, for fear of
+ * using too much local memory.
+ *
+ * This function always scans the system catalog using an up-to-date snapshot,
+ * so it should not be used when a large object is opened in read-only mode
+ * (because large objects opened in read only mode are supposed to be viewed
+ * relative to the caller's snapshot, whereas in read-write mode they are
+ * relative to a current snapshot).
+ */
bool
LargeObjectExists(Oid loid)
{
+ Relation pg_lo_meta;
+ ScanKeyData skey[1];
+ SysScanDesc sd;
+ HeapTuple tuple;
bool retval = false;
- Relation pg_largeobject;
- Relation pg_lo_idx;
- ScanKeyData skey[1];
- IndexScanDesc sd;
- RetrieveIndexResult indexRes;
- HeapTupleData tuple;
- Buffer buffer;
-
- /*
- * See if we can find any tuples belonging to the specified LO
- */
- ScanKeyEntryInitialize(&skey[0],
- (bits16) 0x0,
- (AttrNumber) 1,
- (RegProcedure) F_OIDEQ,
- ObjectIdGetDatum(loid));
- pg_largeobject = heap_openr(LargeObjectRelationName, RowShareLock);
- pg_lo_idx = index_openr(LargeObjectLOidPNIndex);
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
- sd = index_beginscan(pg_lo_idx, false, 1, skey);
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
- tuple.t_datamcxt = CurrentMemoryContext;
- tuple.t_data = NULL;
+ sd = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ NULL, 1, skey);
- while ((indexRes = index_getnext(sd, ForwardScanDirection)))
- {
- tuple.t_self = indexRes->heap_iptr;
- heap_fetch(pg_largeobject, SnapshotNow, &tuple, &buffer);
- pfree(indexRes);
- if (tuple.t_data != NULL)
- {
- retval = true;
- ReleaseBuffer(buffer);
- break;
- }
- }
+ tuple = systable_getnext(sd);
+ if (HeapTupleIsValid(tuple))
+ retval = true;
- index_endscan(sd);
+ systable_endscan(sd);
- index_close(pg_lo_idx);
- heap_close(pg_largeobject, RowShareLock);
+ heap_close(pg_lo_meta, AccessShareLock);
return retval;
}