📄 inv_api.c
字号:
/*------------------------------------------------------------------------- * * inv_api.c * routines for manipulating inversion fs large objects. This file * contains the user-level large object application interface routines. * * * Note: many of these routines leak memory in CurrentMemoryContext, as indeed * does most of the backend code. We expect that CurrentMemoryContext will * be a short-lived context. Data that must persist across function calls * is kept either in CacheMemoryContext (the Relation structs) or in the * memory context given to inv_open (for LargeObjectDesc structs). * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.113.2.1 2006/04/26 00:35:31 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "access/tuptoaster.h"#include "catalog/catalog.h"#include "catalog/indexing.h"#include "catalog/pg_largeobject.h"#include "commands/comment.h"#include "libpq/libpq-fs.h"#include "storage/large_object.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/resowner.h"/* * All accesses to pg_largeobject and its index make use of a single Relation * reference, so that we only need to open pg_relation once per transaction. * To avoid problems when the first such reference occurs inside a * subtransaction, we execute a slightly klugy maneuver to assign ownership of * the Relation reference to TopTransactionResourceOwner. */static Relation lo_heap_r = NULL;static Relation lo_index_r = NULL;/* * Open pg_largeobject and its index, if not already done in current xact */static voidopen_lo_relation(void){ ResourceOwner currentOwner; if (lo_heap_r && lo_index_r) return; /* already open in current xact */ /* Arrange for the top xact to own these relation references */ currentOwner = CurrentResourceOwner; PG_TRY(); { CurrentResourceOwner = TopTransactionResourceOwner; /* Use RowExclusiveLock since we might either read or write */ if (lo_heap_r == NULL) lo_heap_r = heap_open(LargeObjectRelationId, RowExclusiveLock); if (lo_index_r == NULL) lo_index_r = index_open(LargeObjectLOidPNIndexId); } PG_CATCH(); { /* Ensure CurrentResourceOwner is restored on error */ CurrentResourceOwner = currentOwner; PG_RE_THROW(); } PG_END_TRY(); CurrentResourceOwner = currentOwner;}/* * Clean up at main transaction end */voidclose_lo_relation(bool isCommit){ if (lo_heap_r || lo_index_r) { /* * Only bother to close if committing; else abort cleanup will handle * it */ if (isCommit) { ResourceOwner currentOwner; currentOwner = CurrentResourceOwner; PG_TRY(); { CurrentResourceOwner = TopTransactionResourceOwner; if (lo_index_r) index_close(lo_index_r); if (lo_heap_r) heap_close(lo_heap_r, NoLock); } PG_CATCH(); { /* Ensure CurrentResourceOwner is restored on error */ CurrentResourceOwner = currentOwner; PG_RE_THROW(); } PG_END_TRY(); CurrentResourceOwner = currentOwner; } lo_heap_r = NULL; lo_index_r = NULL; }}/* * Same as pg_largeobject.c's LargeObjectExists(), except snapshot to * read with can be specified. */static boolmyLargeObjectExists(Oid loid, Snapshot snapshot){ bool retval = false; Relation pg_largeobject; ScanKeyData skey[1]; SysScanDesc sd; /* * See if we can find any tuples belonging to the specified LO */ ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(loid)); pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, snapshot, 1, skey); if (systable_getnext(sd) != NULL) retval = true; systable_endscan(sd); heap_close(pg_largeobject, AccessShareLock); return retval;}static int32getbytealen(bytea *data){ Assert(!VARATT_IS_EXTENDED(data)); if (VARSIZE(data) < VARHDRSZ) elog(ERROR, "invalid VARSIZE(data)"); return (VARSIZE(data) - VARHDRSZ);}/* * inv_create -- create a new large object * * Arguments: * lobjId - OID to use for new large object, or InvalidOid to pick one * * Returns: * OID of new object * * If lobjId is not InvalidOid, then an error occurs if the OID is already * in use. */Oidinv_create(Oid lobjId){ /* * Allocate an OID to be the LO's identifier, unless we were told what to * use. We can use the index on pg_largeobject for checking OID * uniqueness, even though it has additional columns besides OID. */ if (!OidIsValid(lobjId)) { open_lo_relation(); lobjId = GetNewOidWithIndex(lo_heap_r, lo_index_r); } /* * Create the LO by writing an empty first page for it in pg_largeobject * (will fail if duplicate) */ LargeObjectCreate(lobjId); /* * Advance command counter to make new tuple visible to later operations. */ CommandCounterIncrement(); return lobjId;}/* * inv_open -- access an existing large object. * * Returns: * Large object descriptor, appropriately filled in. The descriptor * and subsidiary data are allocated in the specified memory context, * which must be suitably long-lived for the caller's purposes. */LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt){ LargeObjectDesc *retval; retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); retval->id = lobjId; retval->subid = GetCurrentSubTransactionId(); retval->offset = 0; if (flags & INV_WRITE) { retval->snapshot = SnapshotNow; retval->flags = IFS_WRLOCK | IFS_RDLOCK; } else if (flags & INV_READ) { /* be sure to copy snap into mcxt */ MemoryContext oldContext = MemoryContextSwitchTo(mcxt); retval->snapshot = CopySnapshot(ActiveSnapshot); retval->flags = IFS_RDLOCK; MemoryContextSwitchTo(oldContext); } else elog(ERROR, "invalid flags: %d", flags); /* Can't use LargeObjectExists here because it always uses SnapshotNow */ if (!myLargeObjectExists(lobjId, retval->snapshot)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId))); return retval;}/* * Closes a large object descriptor previously made by inv_open(), and * releases the long-term memory used by it. */voidinv_close(LargeObjectDesc *obj_desc){ Assert(PointerIsValid(obj_desc)); if (obj_desc->snapshot != SnapshotNow) FreeSnapshot(obj_desc->snapshot); pfree(obj_desc);}/* * Destroys an existing large object (not to be confused with a descriptor!) * * returns -1 if failed */intinv_drop(Oid lobjId){ LargeObjectDrop(lobjId); /* Delete any comments on the large object */ DeleteComments(lobjId, LargeObjectRelationId, 0); /* * Advance command counter so that tuple removal will be seen by later * large-object operations in this transaction. */ CommandCounterIncrement(); return 1;}/* * Determine size of a large object * * NOTE: LOs can contain gaps, just like Unix files. We actually return * the offset of the last byte + 1. */static uint32inv_getsize(LargeObjectDesc *obj_desc){ bool found = false; uint32 lastbyte = 0; ScanKeyData skey[1]; IndexScanDesc sd; HeapTuple tuple; Assert(PointerIsValid(obj_desc)); open_lo_relation(); ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id)); sd = index_beginscan(lo_heap_r, lo_index_r, obj_desc->snapshot, 1, skey); /* * Because the pg_largeobject index is on both loid and pageno, but we * constrain only loid, a backwards scan should visit all pages of the * large object in reverse pageno order. So, it's sufficient to examine * the first valid tuple (== last valid page). */ while ((tuple = index_getnext(sd, BackwardScanDirection)) != NULL) { Form_pg_largeobject data; bytea *datafield; bool pfreeit; found = true; data = (Form_pg_largeobject) GETSTRUCT(tuple); datafield = &(data->data); pfreeit = false; if (VARATT_IS_EXTENDED(datafield)) { datafield = (bytea *) heap_tuple_untoast_attr((varattrib *) datafield); pfreeit = true; } lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -