📄 heapam.c
字号:
/*------------------------------------------------------------------------- * * heapam.c * heap access method code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.46.2.1 1999/08/02 05:56:36 scrappy Exp $ * * * INTERFACE ROUTINES * heapgettup - fetch next heap tuple from a scan * heap_open - open a heap relation by relationId * heap_openr - open a heap relation by name * heap_close - close a heap relation * 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 - retrive tuple with tid * heap_insert - insert tuple into a relation * heap_delete - delete a tuple from a relation * heap_replace - 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. * * OLD COMMENTS * struct relscan hints: (struct should be made AM independent?) * * rs_ctid is the tid of the last tuple returned by getnext. * rs_ptid and rs_ntid are the tids of the previous and next tuples * returned by getnext, respectively. NULL indicates an end of * scan (either direction); NON indicates an unknow value. * * possible combinations: * rs_p rs_c rs_n interpretation * NULL NULL NULL empty scan * NULL NULL NON at begining of scan * NULL NULL t1 at begining of scan (with cached tid) * NON NULL NULL at end of scan * t1 NULL NULL at end of scan (with cached tid) * NULL t1 NULL just returned only tuple * NULL t1 NON just returned first tuple * NULL t1 t2 returned first tuple (with cached tid) * NON t1 NULL just returned last tuple * t2 t1 NULL returned last tuple (with cached tid) * t1 t2 NON in the middle of a forward scan * NON t2 t1 in the middle of a reverse scan * ti tj tk in the middle of a scan (w cached tid) * * Here NULL is ...tup == NULL && ...buf == InvalidBuffer, * and NON is ...tup == NULL && ...buf == UnknownBuffer. * * Currently, the NONTID values are not cached with their actual * values by getnext. Values may be cached by markpos since it stores * all three tids. * * NOTE: the calls to elog() must stop. Should decide on an interface * between the general and specific AM calls. * * XXX probably do not need a free tuple routine for heaps. * Huh? Free tuple is not necessary for tuples returned by scans, but * is necessary for tuples which are returned by * RelationGetTupleByItemPointer. -hirohama * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/hio.h"#include "access/valid.h"#include "catalog/catalog.h"#include "miscadmin.h"#include "storage/smgr.h"#include "utils/builtins.h"#include "utils/inval.h"#include "utils/relcache.h"/* ---------------------------------------------------------------- * heap support routines * ---------------------------------------------------------------- *//* ---------------- * initscan - scan code common to heap_beginscan and heap_rescan * ---------------- */static voidinitscan(HeapScanDesc scan, Relation relation, int atend, unsigned nkeys, ScanKey key){ if (!RelationGetNumberOfBlocks(relation)) { /* ---------------- * relation is empty * ---------------- */ scan->rs_ntup.t_data = scan->rs_ctup.t_data = scan->rs_ptup.t_data = NULL; scan->rs_nbuf = scan->rs_cbuf = scan->rs_pbuf = InvalidBuffer; } else if (atend) { /* ---------------- * reverse scan * ---------------- */ scan->rs_ntup.t_data = scan->rs_ctup.t_data = NULL; scan->rs_nbuf = scan->rs_cbuf = InvalidBuffer; scan->rs_ptup.t_data = NULL; scan->rs_pbuf = UnknownBuffer; } else { /* ---------------- * forward scan * ---------------- */ scan->rs_ctup.t_data = scan->rs_ptup.t_data = NULL; scan->rs_cbuf = scan->rs_pbuf = InvalidBuffer; scan->rs_ntup.t_data = NULL; scan->rs_nbuf = UnknownBuffer; } /* invalid too */ /* we don't have a marked position... */ ItemPointerSetInvalid(&(scan->rs_mptid)); ItemPointerSetInvalid(&(scan->rs_mctid)); ItemPointerSetInvalid(&(scan->rs_mntid)); ItemPointerSetInvalid(&(scan->rs_mcd)); /* ---------------- * copy the scan key, if appropriate * ---------------- */ if (key != NULL) memmove(scan->rs_key, key, nkeys * sizeof(ScanKeyData));}/* ---------------- * unpinscan - code common to heap_rescan and heap_endscan * ---------------- */static voidunpinscan(HeapScanDesc scan){ if (BufferIsValid(scan->rs_pbuf)) ReleaseBuffer(scan->rs_pbuf); /* ------------------------------------ * Scan will pin buffer one for each non-NULL tuple pointer * (ptup, ctup, ntup), so they have to be unpinned multiple * times. * ------------------------------------ */ if (BufferIsValid(scan->rs_cbuf)) ReleaseBuffer(scan->rs_cbuf); if (BufferIsValid(scan->rs_nbuf)) ReleaseBuffer(scan->rs_nbuf);}/* ------------------------------------------ * nextpage * * figure out the next page to scan after the current page * taking into account of possible adjustment of degrees of * parallelism * ------------------------------------------ */static intnextpage(int page, int dir){ return (dir < 0) ? page - 1 : page + 1;}/* ---------------- * heapgettup - fetch next heap tuple * * routine used by heap_getnext() which does most of the * real work in scanning tuples. * * The scan routines handle their own buffer lock/unlocking, so * there is no reason to request the buffer number unless * to want to perform some other operation with the result, * like pass it to another function. * ---------------- */static voidheapgettup(Relation relation, HeapTuple tuple, int dir, Buffer *buffer, Snapshot snapshot, int nkeys, ScanKey key){ ItemId lpp; Page dp; int page; int pages; int lines; OffsetNumber lineoff; int linesleft; ItemPointer tid = (tuple->t_data == NULL) ? (ItemPointer) NULL : &(tuple->t_self); /* ---------------- * increment access statistics * ---------------- */ IncrHeapAccessStat(local_heapgettup); IncrHeapAccessStat(global_heapgettup); /* ---------------- * 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(DEBUG, "heapgettup(%s, tid=0x%x[%d,%d], dir=%d, ...)", RelationGetRelationName(relation), tid, tid->ip_blkid, tid->ip_posid, dir); } else { elog(DEBUG, "heapgettup(%s, tid=0x%x, dir=%d, ...)", RelationGetRelationName(relation), tid, dir); } elog(DEBUG, "heapgettup(..., b=0x%x, nkeys=%d, key=0x%x", buffer, nkeys, key); elog(DEBUG, "heapgettup: relation(%c)=`%s', %p", relation->rd_rel->relkind, &relation->rd_rel->relname, snapshot);#endif /* !defined(HEAPDEBUGALL) */ if (!ItemPointerIsValid(tid)) Assert(!PointerIsValid(tid)); /* ---------------- * return null immediately if relation is empty * ---------------- */ if (!(pages = relation->rd_nblocks)) { tuple->t_data = NULL; return; } /* ---------------- * calculate next starting lineoff, given scan direction * ---------------- */ if (!dir) { /* ---------------- * ``no movement'' scan direction * ---------------- */ /* assume it is a valid TID XXX */ if (ItemPointerIsValid(tid) == false) { *buffer = InvalidBuffer; tuple->t_data = NULL; return; } *buffer = RelationGetBufferWithBuffer(relation, ItemPointerGetBlockNumber(tid), *buffer); if (!BufferIsValid(*buffer)) elog(ERROR, "heapgettup: failed ReadBuffer"); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lineoff = ItemPointerGetOffsetNumber(tid); lpp = PageGetItemId(dp, lineoff); 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 (ItemPointerIsValid(tid) == false) tid = NULL; if (tid == NULL) { page = pages - 1; /* final page */ } else { page = ItemPointerGetBlockNumber(tid); /* current page */ } if (page < 0) { *buffer = InvalidBuffer; tuple->t_data = NULL; return; } *buffer = RelationGetBufferWithBuffer(relation, page, *buffer); if (!BufferIsValid(*buffer)) elog(ERROR, "heapgettup: failed ReadBuffer"); 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 (ItemPointerIsValid(tid) == false) { page = 0; /* first page */ lineoff = FirstOffsetNumber; /* first offnum */ } else { page = ItemPointerGetBlockNumber(tid); /* current page */ lineoff = /* next offnum */ OffsetNumberNext(ItemPointerGetOffsetNumber(tid)); } if (page >= pages) { *buffer = InvalidBuffer; tuple->t_data = NULL; return; } /* page and lineoff now reference the physically next tid */ *buffer = RelationGetBufferWithBuffer(relation, page, *buffer); if (!BufferIsValid(*buffer)) elog(ERROR, "heapgettup: failed ReadBuffer"); LockBuffer(*buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(*buffer); lines = PageGetMaxOffsetNumber(dp); } /* '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)) { 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); if (tuple->t_data != NULL) { 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); page = nextpage(page, dir); /* ---------------- * return NULL if we've exhausted all the pages.. * ---------------- */ if (page < 0 || page >= pages) { if (BufferIsValid(*buffer)) ReleaseBuffer(*buffer); *buffer = InvalidBuffer; tuple->t_data = NULL; return; } *buffer = ReleaseAndReadBuffer(*buffer, relation, page); if (!BufferIsValid(*buffer)) elog(ERROR, "heapgettup: failed ReadBuffer"); 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); } }}/* ---------------------------------------------------------------- * heap access method interface * ---------------------------------------------------------------- *//* ---------------- * heap_open - open a heap relation by relationId * * presently the relcache routines do all the work we need * to open/close heap relations. * ---------------- */Relationheap_open(Oid relationId){ Relation r; /* ---------------- * increment access statistics * ---------------- */ IncrHeapAccessStat(local_open); IncrHeapAccessStat(global_open); r = (Relation) RelationIdGetRelation(relationId); if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) elog(ERROR, "%s is an index relation", r->rd_rel->relname.data); return r;}/* ---------------- * heap_openr - open a heap relation by name * * presently the relcache routines do all the work we need * to open/close heap relations. * ---------------- */Relation
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -