📄 heapam.c
字号:
if (lockmode != NoLock) LockRelation(r, lockmode); return r;}/* ---------------- * conditional_relation_open - open with option not to wait * * As above, but if nowait is true, then throw an error rather than * waiting when the lock is not immediately obtainable. * ---------------- */Relationconditional_relation_open(Oid relationId, LOCKMODE lockmode, bool nowait){ Relation r; Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); /* The relcache does all the real work... */ r = RelationIdGetRelation(relationId); if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); if (lockmode != NoLock) { if (nowait) { if (!ConditionalLockRelation(r, lockmode)) ereport(ERROR, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("could not obtain lock on relation \"%s\"", RelationGetRelationName(r)))); } else LockRelation(r, lockmode); } return r;}/* ---------------- * relation_openrv - open any relation specified by a RangeVar * * As above, but the relation is specified by a RangeVar. * ---------------- */Relationrelation_openrv(const RangeVar *relation, LOCKMODE lockmode){ Oid relOid; /* * Check for shared-cache-inval messages before trying to open the * relation. This is needed to cover the case where the name identifies a * rel that has been dropped and recreated since the start of our * transaction: if we don't flush the old syscache entry then we'll latch * onto that entry and suffer an error when we do LockRelation. Note that * relation_open does not need to do this, since a relation's OID never * changes. * * We skip this if asked for NoLock, on the assumption that the caller has * already ensured some appropriate lock is held. */ if (lockmode != NoLock) AcceptInvalidationMessages(); /* Look up the appropriate relation using namespace search */ relOid = RangeVarGetRelid(relation, false); /* Let relation_open do the rest */ return relation_open(relOid, lockmode);}/* ---------------- * relation_close - close any relation * * If lockmode is not "NoLock", we first release the specified lock. * * Note that it is often sensible to hold a lock beyond relation_close; * in that case, the lock is released automatically at xact end. * ---------------- */voidrelation_close(Relation relation, LOCKMODE lockmode){ Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); if (lockmode != NoLock) UnlockRelation(relation, lockmode); /* The relcache does the real work... */ RelationClose(relation);}/* ---------------- * heap_open - open a heap relation by relation OID * * This is essentially relation_open plus check that the relation * is not an index or special relation. (The caller should also check * that it's not a view before assuming it has storage.) * ---------------- */Relationheap_open(Oid relationId, LOCKMODE lockmode){ Relation r; r = relation_open(relationId, lockmode); if (r->rd_rel->relkind == RELKIND_INDEX) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", RelationGetRelationName(r)))); else if (r->rd_rel->relkind == RELKIND_SPECIAL) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a special relation", RelationGetRelationName(r)))); else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a composite type", RelationGetRelationName(r)))); pgstat_initstats(&r->pgstat_info, r); return r;}/* ---------------- * heap_openrv - open a heap relation specified * by a RangeVar node * * As above, but relation is specified by a RangeVar. * ---------------- */Relationheap_openrv(const RangeVar *relation, LOCKMODE lockmode){ Relation r; r = relation_openrv(relation, lockmode); if (r->rd_rel->relkind == RELKIND_INDEX) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", RelationGetRelationName(r)))); else if (r->rd_rel->relkind == RELKIND_SPECIAL) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a special relation", RelationGetRelationName(r)))); else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a composite type", RelationGetRelationName(r)))); pgstat_initstats(&r->pgstat_info, r); return r;}/* ---------------- * heap_beginscan - begin relation scan * ---------------- */HeapScanDescheap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key){ HeapScanDesc scan; /* * increment relation ref count while scanning relation * * This is just to make really sure the relcache entry won't go away while * the scan has a pointer to it. Caller should be holding the rel open * anyway, so this is redundant in all normal scenarios... */ RelationIncrementReferenceCount(relation); /* * allocate and initialize scan descriptor */ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData)); scan->rs_rd = relation; scan->rs_snapshot = snapshot; scan->rs_nkeys = nkeys; /* * we do this here instead of in initscan() because heap_rescan also calls * initscan() and we don't want to allocate memory again */ if (nkeys > 0) scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys); else scan->rs_key = NULL; pgstat_initstats(&scan->rs_pgstat_info, relation); initscan(scan, key); return scan;}/* ---------------- * heap_rescan - restart a relation scan * ---------------- */voidheap_rescan(HeapScanDesc scan, ScanKey key){ /* * unpin scan buffers */ if (BufferIsValid(scan->rs_cbuf)) ReleaseBuffer(scan->rs_cbuf); /* * reinitialize scan descriptor */ initscan(scan, key);}/* ---------------- * heap_endscan - end relation scan * * See how to integrate with index scans. * Check handling if reldesc caching. * ---------------- */voidheap_endscan(HeapScanDesc scan){ /* Note: no locking manipulations needed */ /* * unpin scan buffers */ if (BufferIsValid(scan->rs_cbuf)) ReleaseBuffer(scan->rs_cbuf); /* * decrement relation reference count and free scan descriptor storage */ RelationDecrementReferenceCount(scan->rs_rd); if (scan->rs_key) pfree(scan->rs_key); pfree(scan);}/* ---------------- * heap_getnext - retrieve next tuple in scan * * Fix to work with index relations. * We don't return the buffer anymore, but you can get it from the * returned HeapTuple. * ---------------- */#ifdef HEAPDEBUGALL#define HEAPDEBUG_1 \ elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)#define HEAPDEBUG_2 \ elog(DEBUG2, "heap_getnext returning EOS")#define HEAPDEBUG_3 \ elog(DEBUG2, "heap_getnext returning tuple")#else#define HEAPDEBUG_1#define HEAPDEBUG_2#define HEAPDEBUG_3#endif /* !defined(HEAPDEBUGALL) */HeapTupleheap_getnext(HeapScanDesc scan, ScanDirection direction){ /* Note: no locking manipulations needed */ HEAPDEBUG_1; /* heap_getnext( info ) */ /* * Note: we depend here on the -1/0/1 encoding of ScanDirection. */ heapgettup(scan->rs_rd, (int) direction, &(scan->rs_ctup), &(scan->rs_cbuf), scan->rs_snapshot, scan->rs_nkeys, scan->rs_key, scan->rs_nblocks); if (scan->rs_ctup.t_data == NULL && !BufferIsValid(scan->rs_cbuf)) { HEAPDEBUG_2; /* heap_getnext returning EOS */ return NULL; } /* * if we get here it means we have a new current scan tuple, so point to * the proper return buffer and return the tuple. */ HEAPDEBUG_3; /* heap_getnext returning tuple */ if (scan->rs_ctup.t_data != NULL) pgstat_count_heap_getnext(&scan->rs_pgstat_info); return ((scan->rs_ctup.t_data == NULL) ? NULL : &(scan->rs_ctup));}/* * heap_fetch - retrieve tuple with given tid * * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding * the tuple, fill in the remaining fields of *tuple, and check the tuple * against the specified snapshot. * * If successful (tuple found and passes snapshot time qual), then *userbuf * is set to the buffer holding the tuple and TRUE is returned. The caller * must unpin the buffer when done with the tuple. * * If the tuple is not found (ie, item number references a deleted slot), * then tuple->t_data is set to NULL and FALSE is returned. * * If the tuple is found but fails the time qual check, then FALSE is returned * but tuple->t_data is left pointing to the tuple. * * keep_buf determines what is done with the buffer in the FALSE-result cases. * When the caller specifies keep_buf = true, we retain the pin on the buffer * and return it in *userbuf (so the caller must eventually unpin it); when * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer. * * It is somewhat inconsistent that we ereport() on invalid block number but * return false on invalid item number. There are a couple of reasons though. * One is that the caller can relatively easily check the block number for * validity, but cannot check the item number without reading the page * himself. Another is that when we are following a t_ctid link, we can be * reasonably confident that the page number is valid (since VACUUM shouldn't * truncate off the destination page without having killed the referencing * tuple first), but the item number might well not be good. */boolheap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, PgStat_Info *pgstat_info){ /* Assume *userbuf is undefined on entry */ *userbuf = InvalidBuffer; return heap_release_fetch(relation, snapshot, tuple, userbuf, keep_buf, pgstat_info);}/* * heap_release_fetch - retrieve tuple with given tid * * This has the same API as heap_fetch except that if *userbuf is not * InvalidBuffer on entry, that buffer will be released before reading * the new page. This saves a separate ReleaseBuffer step and hence * one entry into the bufmgr when looping through multiple fetches. * Also, if *userbuf is the same buffer that holds the target tuple, * we avoid bufmgr manipulation altogether. */boolheap_release_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, PgStat_Info *pgstat_info){ ItemPointer tid = &(tuple->t_self); ItemId lp; Buffer buffer; PageHeader dp; OffsetNumber offnum; bool valid; /* * get the buffer from the relation descriptor. Note that this does a * buffer pin, and releases the old *userbuf if not InvalidBuffer. */ buffer = ReleaseAndReadBuffer(*userbuf, relation, ItemPointerGetBlockNumber(tid)); /* * Need share lock on buffer to examine tuple commit status. */ LockBuffer(buffer, BUFFER_LOCK_SHARE); dp = (PageHeader) BufferGetPage(buffer); /* * We'd better check for out-of-range offnum in case of VACUUM since the * TID was obtained. */ offnum = ItemPointerGetOffsetNumber(tid); if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); if (keep_buf) *userbuf = buffer; else { ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } tuple->t_datamcxt = NULL; tuple->t_data = NULL; return false; } /* * get the item line pointer corresponding to the requested tid */ lp = PageGetItemId(dp, offnum); /* * Must check for deleted tuple. */ if (!ItemIdIsUsed(lp)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); if (keep_buf) *userbuf = buffer; else { ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } tuple->t_datamcxt = NULL; tuple->t_data = NULL; return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -