📄 heapam.c
字号:
/*------------------------------------------------------------------------- * * heapam.c * heap access method code * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.200.2.2 2005/11/22 18:23:03 momjian Exp $ * * * INTERFACE ROUTINES * relation_open - open any relation by relation OID * relation_openrv - open any relation specified by a RangeVar * relation_close - close any relation * heap_open - open a heap relation by relation OID * heap_openrv - open a heap relation specified by a RangeVar * heap_close - (now just a macro for relation_close) * heap_beginscan - begin relation scan * heap_rescan - restart a relation scan * heap_endscan - end relation scan * heap_getnext - retrieve next tuple in scan * heap_fetch - retrieve tuple with given tid * heap_insert - insert tuple into a relation * heap_delete - delete a tuple from a relation * heap_update - replace a tuple in a relation with another tuple * heap_markpos - mark scan position * heap_restrpos - restore position to marked location * * NOTES * This file contains the heap_ routines which implement * the POSTGRES heap access method used for all POSTGRES * relations. * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/hio.h"#include "access/multixact.h"#include "access/tuptoaster.h"#include "access/valid.h"#include "access/xlogutils.h"#include "catalog/catalog.h"#include "catalog/namespace.h"#include "miscadmin.h"#include "pgstat.h"#include "storage/procarray.h"#include "utils/inval.h"#include "utils/relcache.h"static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from, Buffer newbuf, HeapTuple newtup, bool move);/* ---------------------------------------------------------------- * heap support routines * ---------------------------------------------------------------- *//* ---------------- * initscan - scan code common to heap_beginscan and heap_rescan * ---------------- */static voidinitscan(HeapScanDesc scan, ScanKey key){ /* * Determine the number of blocks we have to scan. * * It is sufficient to do this once at scan start, since any tuples added * while the scan is in progress will be invisible to my transaction * anyway... */ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd); scan->rs_ctup.t_datamcxt = NULL; scan->rs_ctup.t_data = NULL; scan->rs_cbuf = InvalidBuffer; /* we don't have a marked position... */ ItemPointerSetInvalid(&(scan->rs_mctid)); /* * copy the scan key, if appropriate */ if (key != NULL) memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData)); pgstat_count_heap_scan(&scan->rs_pgstat_info);}/* ---------------- * heapgettup - fetch next heap tuple * * routine used by heap_getnext() which does most of the * real work in scanning tuples. * * The passed-in *buffer must be either InvalidBuffer or the pinned * current page of the scan. If we have to move to another page, * we will unpin this buffer (if valid). On return, *buffer is either * InvalidBuffer or the ID of a pinned buffer. * ---------------- */static voidheapgettup(Relation relation, int dir, HeapTuple tuple, Buffer *buffer, Snapshot snapshot, int nkeys, ScanKey key, BlockNumber pages){ ItemId lpp; Page dp; BlockNumber page; int lines; OffsetNumber lineoff; int linesleft; ItemPointer tid; tid = (tuple->t_data == NULL) ? NULL : &(tuple->t_self); /* * debugging stuff * * check validity of arguments, here and for other functions too Note: no * locking manipulations needed--this is a local function */#ifdef HEAPDEBUGALL if (ItemPointerIsValid(tid)) elog(DEBUG2, "heapgettup(%s, tid=0x%x[%d,%d], dir=%d, ...)", RelationGetRelationName(relation), tid, tid->ip_blkid, tid->ip_posid, dir); else elog(DEBUG2, "heapgettup(%s, tid=0x%x, dir=%d, ...)", RelationGetRelationName(relation), tid, dir); elog(DEBUG2, "heapgettup(..., b=0x%x, nkeys=%d, key=0x%x", buffer, nkeys, key); elog(DEBUG2, "heapgettup: relation(%c)=`%s', %p", relation->rd_rel->relkind, RelationGetRelationName(relation), snapshot);#endif /* HEAPDEBUGALL */ if (!ItemPointerIsValid(tid)) { Assert(!PointerIsValid(tid)); tid = NULL; } tuple->t_tableOid = RelationGetRelid(relation); /* * return null immediately if relation is empty */ if (pages == 0) { if (BufferIsValid(*buffer)) ReleaseBuffer(*buffer); *buffer = InvalidBuffer; tuple->t_datamcxt = NULL; tuple->t_data = NULL; return; } /* * calculate next starting lineoff, given scan direction */ if (dir == 0) { /* * ``no movement'' scan direction: refetch same tuple */ if (tid == NULL) { if (BufferIsValid(*buffer)) ReleaseBuffer(*buffer); *buffer = InvalidBuffer; tuple->t_datamcxt = NULL; tuple->t_data = NULL; return; } *buffer = ReleaseAndReadBuffer(*buffer, relation, ItemPointerGetBlockNumber(tid)); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lineoff = ItemPointerGetOffsetNumber(tid); lpp = PageGetItemId(dp, lineoff); tuple->t_datamcxt = NULL; tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp); tuple->t_len = ItemIdGetLength(lpp); LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); return; } else if (dir < 0) { /* * reverse scan direction */ if (tid == NULL) { page = pages - 1; /* final page */ } else { page = ItemPointerGetBlockNumber(tid); /* current page */ } Assert(page < pages); *buffer = ReleaseAndReadBuffer(*buffer, relation, page); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lines = PageGetMaxOffsetNumber(dp); if (tid == NULL) { lineoff = lines; /* final offnum */ } else { lineoff = /* previous offnum */ OffsetNumberPrev(ItemPointerGetOffsetNumber(tid)); } /* page and lineoff now reference the physically previous tid */ } else { /* * forward scan direction */ if (tid == NULL) { page = 0; /* first page */ lineoff = FirstOffsetNumber; /* first offnum */ } else { page = ItemPointerGetBlockNumber(tid); /* current page */ lineoff = /* next offnum */ OffsetNumberNext(ItemPointerGetOffsetNumber(tid)); } Assert(page < pages); *buffer = ReleaseAndReadBuffer(*buffer, relation, page); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lines = PageGetMaxOffsetNumber(dp); /* page and lineoff now reference the physically next tid */ } /* 'dir' is now non-zero */ /* * calculate line pointer and number of remaining items to check on this * page. */ lpp = PageGetItemId(dp, lineoff); if (dir < 0) linesleft = lineoff - 1; else linesleft = lines - lineoff; /* * advance the scan until we find a qualifying tuple or run out of stuff * to scan */ for (;;) { while (linesleft >= 0) { if (ItemIdIsUsed(lpp)) { bool valid; tuple->t_datamcxt = NULL; tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp); tuple->t_len = ItemIdGetLength(lpp); ItemPointerSet(&(tuple->t_self), page, lineoff); /* * if current tuple qualifies, return it. */ HeapTupleSatisfies(tuple, relation, *buffer, (PageHeader) dp, snapshot, nkeys, key, valid); if (valid) { LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); return; } } /* * otherwise move to the next item on the page */ --linesleft; if (dir < 0) { --lpp; /* move back in this page's ItemId array */ --lineoff; } else { ++lpp; /* move forward in this page's ItemId array */ ++lineoff; } } /* * if we get here, it means we've exhausted the items on this page and * it's time to move to the next. */ LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); /* * return NULL if we've exhausted all the pages */ if ((dir < 0) ? (page == 0) : (page + 1 >= pages)) { if (BufferIsValid(*buffer)) ReleaseBuffer(*buffer); *buffer = InvalidBuffer; tuple->t_datamcxt = NULL; tuple->t_data = NULL; return; } page = (dir < 0) ? (page - 1) : (page + 1); Assert(page < pages); *buffer = ReleaseAndReadBuffer(*buffer, relation, page); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lines = PageGetMaxOffsetNumber((Page) dp); linesleft = lines - 1; if (dir < 0) { lineoff = lines; lpp = PageGetItemId(dp, lines); } else { lineoff = FirstOffsetNumber; lpp = PageGetItemId(dp, FirstOffsetNumber); } }}#if defined(DISABLE_COMPLEX_MACRO)/* * This is formatted so oddly so that the correspondence to the macro * definition in access/heapam.h is maintained. */Datumfastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull){ return ( (attnum) > 0 ? ( ((isnull) ? (*(isnull) = false) : (dummyret) NULL), HeapTupleNoNulls(tup) ? ( (tupleDesc)->attrs[(attnum) - 1]->attcacheoff >= 0 ? ( fetchatt((tupleDesc)->attrs[(attnum) - 1], (char *) (tup)->t_data + (tup)->t_data->t_hoff + (tupleDesc)->attrs[(attnum) - 1]->attcacheoff) ) : nocachegetattr((tup), (attnum), (tupleDesc), (isnull)) ) : ( att_isnull((attnum) - 1, (tup)->t_data->t_bits) ? ( ((isnull) ? (*(isnull) = true) : (dummyret) NULL), (Datum) NULL ) : ( nocachegetattr((tup), (attnum), (tupleDesc), (isnull)) ) ) ) : ( (Datum) NULL ) );}#endif /* defined(DISABLE_COMPLEX_MACRO) *//* ---------------------------------------------------------------- * heap access method interface * ---------------------------------------------------------------- *//* ---------------- * relation_open - open any relation by relation OID * * If lockmode is not "NoLock", the specified kind of lock is * obtained on the relation. (Generally, NoLock should only be * used if the caller knows it has some appropriate lock on the * relation already.) * * An error is raised if the relation does not exist. * * NB: a "relation" is anything with a pg_class entry. The caller is * expected to check whether the relkind is something it can handle. * ---------------- */Relationrelation_open(Oid relationId, LOCKMODE lockmode){ 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -