/*------------------------------------------------------------------------- * * btscan.c-- * manage scans on btrees. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.13 1998/02/28 13:53:18 vadim Exp $ * * * NOTES * Because we can be doing an index scan on a relation while we update * it, we need to avoid missing data that moves around in the index. * The routines and global variables in this file guarantee that all * scans in the local address space stay correctly positioned. This * is all we need to worry about, since write locking guarantees that * no one else will be on the same page at the same time as we are. * * The scheme is to manage a list of active scans in the current backend. * Whenever we add or remove records from an index, or whenever we * split a leaf page, we check the list of active scans to see if any * has been affected. A scan is affected only if it is on the same * relation, and the same page, as the update. * *------------------------------------------------------------------------- */ #include #include #include typedef struct BTScanListData { IndexScanDesc btsl_scan; struct BTScanListData *btsl_next; } BTScanListData; typedef BTScanListData *BTScanList; static BTScanList BTScans = (BTScanList) NULL; static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno); static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); /* * _bt_regscan() -- register a new scan. */ void _bt_regscan(IndexScanDesc scan) { BTScanList new_el; new_el = (BTScanList) palloc(sizeof(BTScanListData)); new_el->btsl_scan = scan; new_el->btsl_next = BTScans; BTScans = new_el; } /* * _bt_dropscan() -- drop a scan from the scan list */ void _bt_dropscan(IndexScanDesc scan) { BTScanList chk, last; last = (BTScanList) NULL; for (chk = BTScans; chk != (BTScanList) NULL && chk->btsl_scan != scan; chk = chk->btsl_next) { last = chk; } if (chk == (BTScanList) NULL) elog(ERROR, "btree scan list trashed; can't find 0x%lx", scan); if (last == (BTScanList) NULL) BTScans = chk->btsl_next; else last->btsl_next = chk->btsl_next; pfree(chk); } /* * _bt_adjscans() -- adjust all scans in the scan list to compensate * for a given deletion or insertion */ void _bt_adjscans(Relation rel, ItemPointer tid, int op) { BTScanList l; Oid relid; relid = rel->rd_id; for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) { if (relid == l->btsl_scan->relation->rd_id) _bt_scandel(l->btsl_scan, op, ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); } } /* * _bt_scandel() -- adjust a single scan * * because each index page is always maintained as an ordered array of * index tuples, the index tuples on a given page shift beneath any * given scan. an index modification "behind" a scan position (i.e., * same page, lower or equal offset number) will therefore force us to * adjust the scan in the following ways: * * - on insertion, we shift the scan forward by one item. * - on deletion, we shift the scan backward by one item. * * note that: * * - we need not worry about the actual ScanDirection of the scan * itself, since the problem is that the "current" scan position has * shifted. * - modifications "ahead" of our scan position do not change the * array index of the current scan position and so can be ignored. */ static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno) { ItemPointer current; Buffer buf; BTScanOpaque so; if (!_bt_scantouched(scan, blkno, offno)) return; so = (BTScanOpaque) scan->opaque; buf = so->btso_curbuf; current = &(scan->currentItemData); if (ItemPointerIsValid(current) && ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetOffsetNumber(current) >= offno) { switch (op) { case BT_INSERT: _bt_step(scan, &buf, ForwardScanDirection); break; case BT_DELETE: _bt_step(scan, &buf, BackwardScanDirection); break; default: elog(ERROR, "_bt_scandel: bad operation '%d'", op); /* NOTREACHED */ } so->btso_curbuf = buf; } current = &(scan->currentMarkData); if (ItemPointerIsValid(current) && ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetOffsetNumber(current) >= offno) { ItemPointerData tmp; tmp = *current; *current = scan->currentItemData; scan->currentItemData = tmp; so->btso_curbuf = so->btso_mrkbuf; so->btso_mrkbuf = buf; buf = so->btso_curbuf; switch (op) { case BT_INSERT: _bt_step(scan, &buf, ForwardScanDirection); break; case BT_DELETE: _bt_step(scan, &buf, BackwardScanDirection); break; default: elog(ERROR, "_bt_scandel: bad operation '%d'", op); /* NOTREACHED */ } so->btso_curbuf = so->btso_mrkbuf; so->btso_mrkbuf = buf; tmp = *current; *current = scan->currentItemData; scan->currentItemData = tmp; } } /* * _bt_scantouched() -- check to see if a scan is affected by a given * change to the index */ static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) { ItemPointer current; current = &(scan->currentItemData); if (ItemPointerIsValid(current) && ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetOffsetNumber(current) >= offno) return (true); current = &(scan->currentMarkData); if (ItemPointerIsValid(current) && ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetOffsetNumber(current) >= offno) return (true); return (false); }