📄 heapam.c
字号:
/*------------------------------------------------------------------------- * * heapam.c * heap access method code * * Portions Copyright (c) 1996-2008, 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.249.2.3 2008/09/11 14:01:35 alvherre 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 * heap_sync - sync heap, for when no WAL has been written * * 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/transam.h"#include "access/tuptoaster.h"#include "access/valid.h"#include "access/xact.h"#include "catalog/catalog.h"#include "catalog/namespace.h"#include "miscadmin.h"#include "pgstat.h"#include "storage/procarray.h"#include "storage/smgr.h"#include "utils/datum.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/relcache.h"#include "utils/syscache.h"/* GUC variable */bool synchronize_seqscans = true;static HeapScanDesc heap_beginscan_internal(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, bool allow_strat, bool allow_sync, bool is_bitmapscan);static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from, Buffer newbuf, HeapTuple newtup, bool move);static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs, HeapTuple oldtup, HeapTuple newtup);/* ---------------------------------------------------------------- * heap support routines * ---------------------------------------------------------------- *//* ---------------- * initscan - scan code common to heap_beginscan and heap_rescan * ---------------- */static voidinitscan(HeapScanDesc scan, ScanKey key){ bool allow_strat; bool allow_sync; /* * 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 snapshot anyway. * (That is not true when using a non-MVCC snapshot. However, we couldn't * guarantee to return tuples added after scan start anyway, since they * might go into pages we already scanned. To guarantee consistent * results for a non-MVCC snapshot, the caller must hold some higher-level * lock that ensures the interesting tuple(s) won't change.) */ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd); /* * If the table is large relative to NBuffers, use a bulk-read access * strategy and enable synchronized scanning (see syncscan.c). Although * the thresholds for these features could be different, we make them the * same so that there are only two behaviors to tune rather than four. * (However, some callers need to be able to disable one or both of * these behaviors, independently of the size of the table; also there * is a GUC variable that can disable synchronized scanning.) * * During a rescan, don't make a new strategy object if we don't have to. */ if (!scan->rs_rd->rd_istemp && scan->rs_nblocks > NBuffers / 4) { allow_strat = scan->rs_allow_strat; allow_sync = scan->rs_allow_sync; } else allow_strat = allow_sync = false; if (allow_strat) { if (scan->rs_strategy == NULL) scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD); } else { if (scan->rs_strategy != NULL) FreeAccessStrategy(scan->rs_strategy); scan->rs_strategy = NULL; } if (allow_sync && synchronize_seqscans) { scan->rs_syncscan = true; scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks); } else { scan->rs_syncscan = false; scan->rs_startblock = 0; } scan->rs_inited = false; scan->rs_ctup.t_data = NULL; ItemPointerSetInvalid(&scan->rs_ctup.t_self); scan->rs_cbuf = InvalidBuffer; scan->rs_cblock = InvalidBlockNumber; /* we don't have a marked position... */ ItemPointerSetInvalid(&(scan->rs_mctid)); /* page-at-a-time fields are always invalid when not rs_inited */ /* * copy the scan key, if appropriate */ if (key != NULL) memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData)); /* * Currently, we don't have a stats counter for bitmap heap scans (but the * underlying bitmap index scans will be counted). */ if (!scan->rs_bitmapscan) pgstat_count_heap_scan(scan->rs_rd);}/* * heapgetpage - subroutine for heapgettup() * * This routine reads and pins the specified page of the relation. * In page-at-a-time mode it performs additional work, namely determining * which tuples on the page are visible. */static voidheapgetpage(HeapScanDesc scan, BlockNumber page){ Buffer buffer; Snapshot snapshot; Page dp; int lines; int ntup; OffsetNumber lineoff; ItemId lpp; Assert(page < scan->rs_nblocks); /* release previous scan buffer, if any */ if (BufferIsValid(scan->rs_cbuf)) { ReleaseBuffer(scan->rs_cbuf); scan->rs_cbuf = InvalidBuffer; } /* read page using selected strategy */ scan->rs_cbuf = ReadBufferWithStrategy(scan->rs_rd, page, scan->rs_strategy); scan->rs_cblock = page; if (!scan->rs_pageatatime) return; buffer = scan->rs_cbuf; snapshot = scan->rs_snapshot; /* * Prune and repair fragmentation for the whole page, if possible. */ Assert(TransactionIdIsValid(RecentGlobalXmin)); heap_page_prune_opt(scan->rs_rd, buffer, RecentGlobalXmin); /* * We must hold share lock on the buffer content while examining tuple * visibility. Afterwards, however, the tuples we have found to be * visible are guaranteed good as long as we hold the buffer pin. */ LockBuffer(buffer, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(buffer); lines = PageGetMaxOffsetNumber(dp); ntup = 0; for (lineoff = FirstOffsetNumber, lpp = PageGetItemId(dp, lineoff); lineoff <= lines; lineoff++, lpp++) { if (ItemIdIsNormal(lpp)) { HeapTupleData loctup; bool valid; loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp); loctup.t_len = ItemIdGetLength(lpp); ItemPointerSet(&(loctup.t_self), page, lineoff); valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); if (valid) scan->rs_vistuples[ntup++] = lineoff; } } LockBuffer(buffer, BUFFER_LOCK_UNLOCK); Assert(ntup <= MaxHeapTuplesPerPage); scan->rs_ntuples = ntup;}/* ---------------- * heapgettup - fetch next heap tuple * * Initialize the scan if not already done; then advance to the next * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup, * or set scan->rs_ctup.t_data = NULL if no more tuples. * * dir == NoMovementScanDirection means "re-fetch the tuple indicated * by scan->rs_ctup". * * Note: the reason nkeys/key are passed separately, even though they are * kept in the scan descriptor, is that the caller may not want us to check * the scankeys. * * Note: when we fall off the end of the scan in either direction, we * reset rs_inited. This means that a further request with the same * scan direction will restart the scan, which is a bit odd, but a * request with the opposite scan direction will start a fresh scan * in the proper direction. The latter is required behavior for cursors, * while the former case is generally undefined behavior in Postgres * so we don't care too much. * ---------------- */static voidheapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key){ HeapTuple tuple = &(scan->rs_ctup); Snapshot snapshot = scan->rs_snapshot; bool backward = ScanDirectionIsBackward(dir); BlockNumber page; bool finished; Page dp; int lines; OffsetNumber lineoff; int linesleft; ItemId lpp; /* * calculate next starting lineoff, given scan direction */ if (ScanDirectionIsForward(dir)) { if (!scan->rs_inited) { /* * return null immediately if relation is empty */ if (scan->rs_nblocks == 0) { Assert(!BufferIsValid(scan->rs_cbuf)); tuple->t_data = NULL; return; } page = scan->rs_startblock; /* first page */ heapgetpage(scan, page); lineoff = FirstOffsetNumber; /* first offnum */ scan->rs_inited = true; } else { /* continue from previously returned page/tuple */ page = scan->rs_cblock; /* current page */ lineoff = /* next offnum */ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self))); } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(scan->rs_cbuf); lines = PageGetMaxOffsetNumber(dp); /* page and lineoff now reference the physically next tid */ linesleft = lines - lineoff + 1; } else if (backward) { if (!scan->rs_inited) { /* * return null immediately if relation is empty */ if (scan->rs_nblocks == 0) { Assert(!BufferIsValid(scan->rs_cbuf)); tuple->t_data = NULL; return; } /* * Disable reporting to syncscan logic in a backwards scan; it's * not very likely anyone else is doing the same thing at the same * time, and much more likely that we'll just bollix things for * forward scanners. */ scan->rs_syncscan = false; /* start from last page of the scan */ if (scan->rs_startblock > 0) page = scan->rs_startblock - 1; else page = scan->rs_nblocks - 1; heapgetpage(scan, page); } else { /* continue from previously returned page/tuple */ page = scan->rs_cblock; /* current page */ } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); dp = (Page) BufferGetPage(scan->rs_cbuf); lines = PageGetMaxOffsetNumber(dp); if (!scan->rs_inited) { lineoff = lines; /* final offnum */ scan->rs_inited = true; } else { lineoff = /* previous offnum */ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self))); } /* page and lineoff now reference the physically previous tid */ linesleft = lineoff; } else { /* * ``no movement'' scan direction: refetch prior tuple */ if (!scan->rs_inited) { Assert(!BufferIsValid(scan->rs_cbuf)); tuple->t_data = NULL; return; } page = ItemPointerGetBlockNumber(&(tuple->t_self)); if (page != scan->rs_cblock) heapgetpage(scan, page); /* Since the tuple was previously fetched, needn't lock page here */ dp = (Page) BufferGetPage(scan->rs_cbuf); lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self)); lpp = PageGetItemId(dp, lineoff); Assert(ItemIdIsNormal(lpp)); tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp); tuple->t_len = ItemIdGetLength(lpp); return; } /* * advance the scan until we find a qualifying tuple or run out of stuff * to scan */ lpp = PageGetItemId(dp, lineoff); for (;;) { while (linesleft > 0) { if (ItemIdIsNormal(lpp)) { bool valid; 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. */ valid = HeapTupleSatisfiesVisibility(tuple, snapshot, scan->rs_cbuf); if (valid && key != NULL) HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd), nkeys, key, valid); if (valid) { LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); return; } } /* * otherwise move to the next item on the page */ --linesleft; if (backward) { --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(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -