Commit 9e85183b authored by Tom Lane's avatar Tom Lane

Major overhaul of btree index code. Eliminate special BTP_CHAIN logic for

duplicate keys by letting search go to the left rather than right when an
equal key is seen at an upper tree level.  Fix poor choice of page split
point (leading to insertion failures) that was forced by chaining logic.
Don't store leftmost key in non-leaf pages, since it's not necessary.
Don't create root page until something is first stored in the index, so an
unused index is now 8K not 16K.  (Doesn't seem to be as easy to get rid of
the metadata page, unfortunately.)  Massive cleanup of unreadable code,
fix poor, obsolete, and just plain wrong documentation and comments.
See src/backend/access/nbtree/README for the gory details.
parent c9537ca8
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.61 2000/07/14 22:17:33 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.62 2000/07/21 06:42:32 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "executor/executor.h" #include "executor/executor.h"
#include "miscadmin.h" #include "miscadmin.h"
bool BuildingBtree = false; /* see comment in btbuild() */ bool BuildingBtree = false; /* see comment in btbuild() */
bool FastBuild = true; /* use sort/build instead of insertion bool FastBuild = true; /* use sort/build instead of insertion
* build */ * build */
...@@ -206,8 +207,8 @@ btbuild(PG_FUNCTION_ARGS) ...@@ -206,8 +207,8 @@ btbuild(PG_FUNCTION_ARGS)
* btree pages - NULLs greater NOT_NULLs and NULL = NULL is TRUE. * btree pages - NULLs greater NOT_NULLs and NULL = NULL is TRUE.
* Sure, it's just rule for placing/finding items and no more - * Sure, it's just rule for placing/finding items and no more -
* keytest'll return FALSE for a = 5 for items having 'a' isNULL. * keytest'll return FALSE for a = 5 for items having 'a' isNULL.
* Look at _bt_skeycmp, _bt_compare and _bt_itemcmp for how it * Look at _bt_compare for how it works.
* works. - vadim 03/23/97 * - vadim 03/23/97
* *
* if (itup->t_info & INDEX_NULL_MASK) { pfree(itup); continue; } * if (itup->t_info & INDEX_NULL_MASK) { pfree(itup); continue; }
*/ */
...@@ -321,14 +322,6 @@ btinsert(PG_FUNCTION_ARGS) ...@@ -321,14 +322,6 @@ btinsert(PG_FUNCTION_ARGS)
/* generate an index tuple */ /* generate an index tuple */
itup = index_formtuple(RelationGetDescr(rel), datum, nulls); itup = index_formtuple(RelationGetDescr(rel), datum, nulls);
itup->t_tid = *ht_ctid; itup->t_tid = *ht_ctid;
/*
* See comments in btbuild.
*
* if (itup->t_info & INDEX_NULL_MASK)
* PG_RETURN_POINTER((InsertIndexResult) NULL);
*/
btitem = _bt_formitem(itup); btitem = _bt_formitem(itup);
res = _bt_doinsert(rel, btitem, rel->rd_uniqueindex, heapRel); res = _bt_doinsert(rel, btitem, rel->rd_uniqueindex, heapRel);
...@@ -357,10 +350,10 @@ btgettuple(PG_FUNCTION_ARGS) ...@@ -357,10 +350,10 @@ btgettuple(PG_FUNCTION_ARGS)
if (ItemPointerIsValid(&(scan->currentItemData))) if (ItemPointerIsValid(&(scan->currentItemData)))
{ {
/* /*
* Restore scan position using heap TID returned by previous call * Restore scan position using heap TID returned by previous call
* to btgettuple(). _bt_restscan() locks buffer. * to btgettuple(). _bt_restscan() re-grabs the read lock on
* the buffer, too.
*/ */
_bt_restscan(scan); _bt_restscan(scan);
res = _bt_next(scan, dir); res = _bt_next(scan, dir);
...@@ -369,8 +362,9 @@ btgettuple(PG_FUNCTION_ARGS) ...@@ -369,8 +362,9 @@ btgettuple(PG_FUNCTION_ARGS)
res = _bt_first(scan, dir); res = _bt_first(scan, dir);
/* /*
* Save heap TID to use it in _bt_restscan. Unlock buffer before * Save heap TID to use it in _bt_restscan. Then release the read
* leaving index ! * lock on the buffer so that we aren't blocking other backends.
* NOTE: we do keep the pin on the buffer!
*/ */
if (res) if (res)
{ {
...@@ -419,7 +413,18 @@ btrescan(PG_FUNCTION_ARGS) ...@@ -419,7 +413,18 @@ btrescan(PG_FUNCTION_ARGS)
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
/* we don't hold a read lock on the current page in the scan */ if (so == NULL) /* if called from btbeginscan */
{
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
so->btso_curbuf = so->btso_mrkbuf = InvalidBuffer;
so->keyData = (ScanKey) NULL;
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
scan->opaque = so;
scan->flags = 0x0;
}
/* we aren't holding any read locks, but gotta drop the pins */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{ {
ReleaseBuffer(so->btso_curbuf); ReleaseBuffer(so->btso_curbuf);
...@@ -427,7 +432,6 @@ btrescan(PG_FUNCTION_ARGS) ...@@ -427,7 +432,6 @@ btrescan(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(iptr); ItemPointerSetInvalid(iptr);
} }
/* and we don't hold a read lock on the last marked item in the scan */
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
{ {
ReleaseBuffer(so->btso_mrkbuf); ReleaseBuffer(so->btso_mrkbuf);
...@@ -435,17 +439,6 @@ btrescan(PG_FUNCTION_ARGS) ...@@ -435,17 +439,6 @@ btrescan(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(iptr); ItemPointerSetInvalid(iptr);
} }
if (so == NULL) /* if called from btbeginscan */
{
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
so->btso_curbuf = so->btso_mrkbuf = InvalidBuffer;
so->keyData = (ScanKey) NULL;
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
scan->opaque = so;
scan->flags = 0x0;
}
/* /*
* Reset the scan keys. Note that keys ordering stuff moved to * Reset the scan keys. Note that keys ordering stuff moved to
* _bt_first. - vadim 05/05/97 * _bt_first. - vadim 05/05/97
...@@ -472,7 +465,7 @@ btmovescan(IndexScanDesc scan, Datum v) ...@@ -472,7 +465,7 @@ btmovescan(IndexScanDesc scan, Datum v)
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
/* we don't hold a read lock on the current page in the scan */ /* we aren't holding any read locks, but gotta drop the pin */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{ {
ReleaseBuffer(so->btso_curbuf); ReleaseBuffer(so->btso_curbuf);
...@@ -480,7 +473,6 @@ btmovescan(IndexScanDesc scan, Datum v) ...@@ -480,7 +473,6 @@ btmovescan(IndexScanDesc scan, Datum v)
ItemPointerSetInvalid(iptr); ItemPointerSetInvalid(iptr);
} }
/* scan->keyData[0].sk_argument = v; */
so->keyData[0].sk_argument = v; so->keyData[0].sk_argument = v;
} }
...@@ -496,7 +488,7 @@ btendscan(PG_FUNCTION_ARGS) ...@@ -496,7 +488,7 @@ btendscan(PG_FUNCTION_ARGS)
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
/* we don't hold any read locks */ /* we aren't holding any read locks, but gotta drop the pins */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{ {
if (BufferIsValid(so->btso_curbuf)) if (BufferIsValid(so->btso_curbuf))
...@@ -534,7 +526,7 @@ btmarkpos(PG_FUNCTION_ARGS) ...@@ -534,7 +526,7 @@ btmarkpos(PG_FUNCTION_ARGS)
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
/* we don't hold any read locks */ /* we aren't holding any read locks, but gotta drop the pin */
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
{ {
ReleaseBuffer(so->btso_mrkbuf); ReleaseBuffer(so->btso_mrkbuf);
...@@ -542,7 +534,7 @@ btmarkpos(PG_FUNCTION_ARGS) ...@@ -542,7 +534,7 @@ btmarkpos(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(iptr); ItemPointerSetInvalid(iptr);
} }
/* bump pin on current buffer */ /* bump pin on current buffer for assignment to mark buffer */
if (ItemPointerIsValid(&(scan->currentItemData))) if (ItemPointerIsValid(&(scan->currentItemData)))
{ {
so->btso_mrkbuf = ReadBuffer(scan->relation, so->btso_mrkbuf = ReadBuffer(scan->relation,
...@@ -566,7 +558,7 @@ btrestrpos(PG_FUNCTION_ARGS) ...@@ -566,7 +558,7 @@ btrestrpos(PG_FUNCTION_ARGS)
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
/* we don't hold any read locks */ /* we aren't holding any read locks, but gotta drop the pin */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{ {
ReleaseBuffer(so->btso_curbuf); ReleaseBuffer(so->btso_curbuf);
...@@ -579,7 +571,6 @@ btrestrpos(PG_FUNCTION_ARGS) ...@@ -579,7 +571,6 @@ btrestrpos(PG_FUNCTION_ARGS)
{ {
so->btso_curbuf = ReadBuffer(scan->relation, so->btso_curbuf = ReadBuffer(scan->relation,
BufferGetBlockNumber(so->btso_mrkbuf)); BufferGetBlockNumber(so->btso_mrkbuf));
scan->currentItemData = scan->currentMarkData; scan->currentItemData = scan->currentMarkData;
so->curHeapIptr = so->mrkHeapIptr; so->curHeapIptr = so->mrkHeapIptr;
} }
...@@ -603,6 +594,9 @@ btdelete(PG_FUNCTION_ARGS) ...@@ -603,6 +594,9 @@ btdelete(PG_FUNCTION_ARGS)
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
/*
* Restore scan position when btgettuple is called to continue a scan.
*/
static void static void
_bt_restscan(IndexScanDesc scan) _bt_restscan(IndexScanDesc scan)
{ {
...@@ -618,7 +612,12 @@ _bt_restscan(IndexScanDesc scan) ...@@ -618,7 +612,12 @@ _bt_restscan(IndexScanDesc scan)
BTItem item; BTItem item;
BlockNumber blkno; BlockNumber blkno;
LockBuffer(buf, BT_READ); /* lock buffer first! */ /*
* Get back the read lock we were holding on the buffer.
* (We still have a reference-count pin on it, though.)
*/
LockBuffer(buf, BT_READ);
page = BufferGetPage(buf); page = BufferGetPage(buf);
maxoff = PageGetMaxOffsetNumber(page); maxoff = PageGetMaxOffsetNumber(page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page);
...@@ -631,43 +630,40 @@ _bt_restscan(IndexScanDesc scan) ...@@ -631,43 +630,40 @@ _bt_restscan(IndexScanDesc scan)
*/ */
if (!ItemPointerIsValid(&target)) if (!ItemPointerIsValid(&target))
{ {
ItemPointerSetOffsetNumber(&(scan->currentItemData), ItemPointerSetOffsetNumber(current,
OffsetNumberPrev(P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY)); OffsetNumberPrev(P_FIRSTDATAKEY(opaque)));
return; return;
} }
if (maxoff >= offnum) /*
* The item we were on may have moved right due to insertions.
* Find it again.
*/
for (;;)
{ {
/* Check for item on this page */
/*
* if the item is where we left it or has just moved right on this
* page, we're done
*/
for (; for (;
offnum <= maxoff; offnum <= maxoff;
offnum = OffsetNumberNext(offnum)) offnum = OffsetNumberNext(offnum))
{ {
item = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); item = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
if (item->bti_itup.t_tid.ip_blkid.bi_hi == \ if (item->bti_itup.t_tid.ip_blkid.bi_hi ==
target.ip_blkid.bi_hi && \ target.ip_blkid.bi_hi &&
item->bti_itup.t_tid.ip_blkid.bi_lo == \ item->bti_itup.t_tid.ip_blkid.bi_lo ==
target.ip_blkid.bi_lo && \ target.ip_blkid.bi_lo &&
item->bti_itup.t_tid.ip_posid == target.ip_posid) item->bti_itup.t_tid.ip_posid == target.ip_posid)
{ {
current->ip_posid = offnum; current->ip_posid = offnum;
return; return;
} }
} }
}
/* /*
* By here, the item we're looking for moved right at least one page * By here, the item we're looking for moved right at least one page
*/ */
for (;;)
{
if (P_RIGHTMOST(opaque)) if (P_RIGHTMOST(opaque))
elog(FATAL, "_bt_restscan: my bits moved right off the end of the world!\ elog(FATAL, "_bt_restscan: my bits moved right off the end of the world!"
\n\tRecreate index %s.", RelationGetRelationName(rel)); "\n\tRecreate index %s.", RelationGetRelationName(rel));
blkno = opaque->btpo_next; blkno = opaque->btpo_next;
_bt_relbuf(rel, buf, BT_READ); _bt_relbuf(rel, buf, BT_READ);
...@@ -675,23 +671,8 @@ _bt_restscan(IndexScanDesc scan) ...@@ -675,23 +671,8 @@ _bt_restscan(IndexScanDesc scan)
page = BufferGetPage(buf); page = BufferGetPage(buf);
maxoff = PageGetMaxOffsetNumber(page); maxoff = PageGetMaxOffsetNumber(page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page);
offnum = P_FIRSTDATAKEY(opaque);
/* see if it's on this page */ ItemPointerSet(current, blkno, offnum);
for (offnum = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; so->btso_curbuf = buf;
offnum <= maxoff;
offnum = OffsetNumberNext(offnum))
{
item = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
if (item->bti_itup.t_tid.ip_blkid.bi_hi == \
target.ip_blkid.bi_hi && \
item->bti_itup.t_tid.ip_blkid.bi_lo == \
target.ip_blkid.bi_lo && \
item->bti_itup.t_tid.ip_posid == target.ip_posid)
{
ItemPointerSet(current, blkno, offnum);
so->btso_curbuf = buf;
return;
}
}
} }
} }
...@@ -8,22 +8,25 @@ ...@@ -8,22 +8,25 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.31 2000/04/12 17:14:49 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.32 2000/07/21 06:42:32 tgl Exp $
* *
* *
* NOTES * NOTES
* Because we can be doing an index scan on a relation while we update * 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. * it, we need to avoid missing data that moves around in the index.
* The routines and global variables in this file guarantee that all * Insertions and page splits are no problem because _bt_restscan()
* scans in the local address space stay correctly positioned. This * can figure out where the current item moved to, but if a deletion
* is all we need to worry about, since write locking guarantees that * happens at or before the current scan position, we'd better do
* no one else will be on the same page at the same time as we are. * something to stay in sync.
*
* The routines in this file handle the problem for deletions issued
* by the current backend. Currently, that's all we need, since
* deletions are only done by VACUUM and it gets an exclusive lock.
* *
* The scheme is to manage a list of active scans in the current backend. * 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 * Whenever we remove a record from an index, we check the list of active
* split a leaf page, we check the list of active scans to see if any * scans to see if any has been affected. A scan is affected only if it
* has been affected. A scan is affected only if it is on the same * is on the same relation, and the same page, as the update.
* relation, and the same page, as the update.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -111,7 +114,7 @@ _bt_dropscan(IndexScanDesc scan) ...@@ -111,7 +114,7 @@ _bt_dropscan(IndexScanDesc scan)
/* /*
* _bt_adjscans() -- adjust all scans in the scan list to compensate * _bt_adjscans() -- adjust all scans in the scan list to compensate
* for a given deletion or insertion * for a given deletion
*/ */
void void
_bt_adjscans(Relation rel, ItemPointer tid) _bt_adjscans(Relation rel, ItemPointer tid)
...@@ -153,7 +156,7 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) ...@@ -153,7 +156,7 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
{ {
page = BufferGetPage(buf); page = BufferGetPage(buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page);
start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; start = P_FIRSTDATAKEY(opaque);
if (ItemPointerGetOffsetNumber(current) == start) if (ItemPointerGetOffsetNumber(current) == start)
ItemPointerSetInvalid(&(so->curHeapIptr)); ItemPointerSetInvalid(&(so->curHeapIptr));
else else
...@@ -165,7 +168,6 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) ...@@ -165,7 +168,6 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
*/ */
LockBuffer(buf, BT_READ); LockBuffer(buf, BT_READ);
_bt_step(scan, &buf, BackwardScanDirection); _bt_step(scan, &buf, BackwardScanDirection);
so->btso_curbuf = buf;
if (ItemPointerIsValid(current)) if (ItemPointerIsValid(current))
{ {
Page pg = BufferGetPage(buf); Page pg = BufferGetPage(buf);
...@@ -183,10 +185,9 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) ...@@ -183,10 +185,9 @@ _bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) && ItemPointerGetOffsetNumber(current) >= offno)
{ {
page = BufferGetPage(so->btso_mrkbuf); page = BufferGetPage(so->btso_mrkbuf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page);
start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; start = P_FIRSTDATAKEY(opaque);
if (ItemPointerGetOffsetNumber(current) == start) if (ItemPointerGetOffsetNumber(current) == start)
ItemPointerSetInvalid(&(so->mrkHeapIptr)); ItemPointerSetInvalid(&(so->mrkHeapIptr));
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.37 2000/05/30 04:24:33 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.38 2000/07/21 06:42:33 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,16 +20,13 @@ ...@@ -20,16 +20,13 @@
#include "access/nbtree.h" #include "access/nbtree.h"
#include "executor/execdebug.h" #include "executor/execdebug.h"
extern int NIndexTupleProcessed;
/* /*
* _bt_mkscankey * _bt_mkscankey
* Build a scan key that contains comparison data from itup * Build a scan key that contains comparison data from itup
* as well as comparator routines appropriate to the key datatypes. * as well as comparator routines appropriate to the key datatypes.
* *
* The result is intended for use with _bt_skeycmp() or _bt_compare(), * The result is intended for use with _bt_compare().
* although it could be used with _bt_itemcmp() or _bt_tuplecompare().
*/ */
ScanKey ScanKey
_bt_mkscankey(Relation rel, IndexTuple itup) _bt_mkscankey(Relation rel, IndexTuple itup)
...@@ -68,8 +65,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup) ...@@ -68,8 +65,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
* Build a scan key that contains comparator routines appropriate to * Build a scan key that contains comparator routines appropriate to
* the key datatypes, but no comparison data. * the key datatypes, but no comparison data.
* *
* The result can be used with _bt_itemcmp() or _bt_tuplecompare(), * The result cannot be used with _bt_compare(). Currently this
* but not with _bt_skeycmp() or _bt_compare(). * routine is only called by utils/sort/tuplesort.c, which has its
* own comparison routine.
*/ */
ScanKey ScanKey
_bt_mkscankey_nodata(Relation rel) _bt_mkscankey_nodata(Relation rel)
...@@ -114,7 +112,6 @@ _bt_freestack(BTStack stack) ...@@ -114,7 +112,6 @@ _bt_freestack(BTStack stack)
{ {
ostack = stack; ostack = stack;
stack = stack->bts_parent; stack = stack->bts_parent;
pfree(ostack->bts_btitem);
pfree(ostack); pfree(ostack);
} }
} }
...@@ -331,55 +328,16 @@ _bt_formitem(IndexTuple itup) ...@@ -331,55 +328,16 @@ _bt_formitem(IndexTuple itup)
Size tuplen; Size tuplen;
extern Oid newoid(); extern Oid newoid();
/*
* see comments in btbuild
*
* if (itup->t_info & INDEX_NULL_MASK) elog(ERROR, "btree indices cannot
* include null keys");
*/
/* make a copy of the index tuple with room for the sequence number */ /* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup); tuplen = IndexTupleSize(itup);
nbytes_btitem = tuplen + (sizeof(BTItemData) - sizeof(IndexTupleData)); nbytes_btitem = tuplen + (sizeof(BTItemData) - sizeof(IndexTupleData));
btitem = (BTItem) palloc(nbytes_btitem); btitem = (BTItem) palloc(nbytes_btitem);
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen); memcpy((char *) &(btitem->bti_itup), (char *) itup, tuplen);
return btitem; return btitem;
} }
#ifdef NOT_USED
bool
_bt_checkqual(IndexScanDesc scan, IndexTuple itup)
{
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetDescr(scan->relation),
so->numberOfKeys, so->keyData));
else
return true;
}
#endif
#ifdef NOT_USED
bool
_bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz)
{
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (keysz > 0 && so->numberOfKeys >= keysz)
return (index_keytest(itup, RelationGetDescr(scan->relation),
keysz, so->keyData));
else
return true;
}
#endif
bool bool
_bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size *keysok) _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size *keysok)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.30 2000/07/03 02:54:16 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.31 2000/07/21 06:42:33 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
#include "storage/bufpage.h" #include "storage/bufpage.h"
static void PageIndexTupleDeleteAdjustLinePointers(PageHeader phdr, static void PageIndexTupleDeleteAdjustLinePointers(PageHeader phdr,
char *location, Size size); char *location, Size size);
static bool PageManagerShuffle = true; /* default is shuffle mode */
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* Page support functions * Page support functions
...@@ -53,21 +53,17 @@ PageInit(Page page, Size pageSize, Size specialSize) ...@@ -53,21 +53,17 @@ PageInit(Page page, Size pageSize, Size specialSize)
/* ---------------- /* ----------------
* PageAddItem * PageAddItem
* *
* add an item to a page. * Add an item to a page. Return value is offset at which it was
* * inserted, or InvalidOffsetNumber if there's not room to insert.
* !!! ELOG(ERROR) IS DISALLOWED HERE !!!
* *
* Notes on interface: * If offsetNumber is valid and <= current max offset in the page,
* If offsetNumber is valid, shuffle ItemId's down to make room * insert item into the array at that position by shuffling ItemId's
* to use it, if PageManagerShuffle is true. If PageManagerShuffle is * down to make room.
* false, then overwrite the specified ItemId. (PageManagerShuffle is
* true by default, and is modified by calling PageManagerModeSet.)
* If offsetNumber is not valid, then assign one by finding the first * If offsetNumber is not valid, then assign one by finding the first
* one that is both unused and deallocated. * one that is both unused and deallocated.
* *
* NOTE: If offsetNumber is valid, and PageManagerShuffle is true, it * !!! ELOG(ERROR) IS DISALLOWED HERE !!!
* is assumed that there is room on the page to shuffle the ItemId's *
* down by one.
* ---------------- * ----------------
*/ */
OffsetNumber OffsetNumber
...@@ -82,11 +78,8 @@ PageAddItem(Page page, ...@@ -82,11 +78,8 @@ PageAddItem(Page page,
Offset lower; Offset lower;
Offset upper; Offset upper;
ItemId itemId; ItemId itemId;
ItemId fromitemId,
toitemId;
OffsetNumber limit; OffsetNumber limit;
bool needshuffle = false;
bool shuffled = false;
/* /*
* Find first unallocated offsetNumber * Find first unallocated offsetNumber
...@@ -96,31 +89,12 @@ PageAddItem(Page page, ...@@ -96,31 +89,12 @@ PageAddItem(Page page,
/* was offsetNumber passed in? */ /* was offsetNumber passed in? */
if (OffsetNumberIsValid(offsetNumber)) if (OffsetNumberIsValid(offsetNumber))
{ {
if (PageManagerShuffle == true) needshuffle = true; /* need to increase "lower" */
{ /* don't actually do the shuffle till we've checked free space! */
/* shuffle ItemId's (Do the PageManager Shuffle...) */
for (i = (limit - 1); i >= offsetNumber; i--)
{
fromitemId = &((PageHeader) page)->pd_linp[i - 1];
toitemId = &((PageHeader) page)->pd_linp[i];
*toitemId = *fromitemId;
}
shuffled = true; /* need to increase "lower" */
}
else
{ /* overwrite mode */
itemId = &((PageHeader) page)->pd_linp[offsetNumber - 1];
if (((*itemId).lp_flags & LP_USED) ||
((*itemId).lp_len != 0))
{
elog(NOTICE, "PageAddItem: tried overwrite of used ItemId");
return InvalidOffsetNumber;
}
}
} }
else else
{ /* offsetNumber was not passed in, so find {
* one */ /* offsetNumber was not passed in, so find one */
/* look for "recyclable" (unused & deallocated) ItemId */ /* look for "recyclable" (unused & deallocated) ItemId */
for (offsetNumber = 1; offsetNumber < limit; offsetNumber++) for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
{ {
...@@ -130,9 +104,13 @@ PageAddItem(Page page, ...@@ -130,9 +104,13 @@ PageAddItem(Page page,
break; break;
} }
} }
/*
* Compute new lower and upper pointers for page, see if it'll fit
*/
if (offsetNumber > limit) if (offsetNumber > limit)
lower = (Offset) (((char *) (&((PageHeader) page)->pd_linp[offsetNumber])) - ((char *) page)); lower = (Offset) (((char *) (&((PageHeader) page)->pd_linp[offsetNumber])) - ((char *) page));
else if (offsetNumber == limit || shuffled == true) else if (offsetNumber == limit || needshuffle)
lower = ((PageHeader) page)->pd_lower + sizeof(ItemIdData); lower = ((PageHeader) page)->pd_lower + sizeof(ItemIdData);
else else
lower = ((PageHeader) page)->pd_lower; lower = ((PageHeader) page)->pd_lower;
...@@ -144,6 +122,23 @@ PageAddItem(Page page, ...@@ -144,6 +122,23 @@ PageAddItem(Page page,
if (lower > upper) if (lower > upper)
return InvalidOffsetNumber; return InvalidOffsetNumber;
/*
* OK to insert the item. First, shuffle the existing pointers if needed.
*/
if (needshuffle)
{
/* shuffle ItemId's (Do the PageManager Shuffle...) */
for (i = (limit - 1); i >= offsetNumber; i--)
{
ItemId fromitemId,
toitemId;
fromitemId = &((PageHeader) page)->pd_linp[i - 1];
toitemId = &((PageHeader) page)->pd_linp[i];
*toitemId = *fromitemId;
}
}
itemId = &((PageHeader) page)->pd_linp[offsetNumber - 1]; itemId = &((PageHeader) page)->pd_linp[offsetNumber - 1];
(*itemId).lp_off = upper; (*itemId).lp_off = upper;
(*itemId).lp_len = size; (*itemId).lp_len = size;
...@@ -168,9 +163,7 @@ PageGetTempPage(Page page, Size specialSize) ...@@ -168,9 +163,7 @@ PageGetTempPage(Page page, Size specialSize)
PageHeader thdr; PageHeader thdr;
pageSize = PageGetPageSize(page); pageSize = PageGetPageSize(page);
temp = (Page) palloc(pageSize);
if ((temp = (Page) palloc(pageSize)) == (Page) NULL)
elog(FATAL, "Cannot allocate %d bytes for temp page.", pageSize);
thdr = (PageHeader) temp; thdr = (PageHeader) temp;
/* copy old page in */ /* copy old page in */
...@@ -327,23 +320,6 @@ PageGetFreeSpace(Page page) ...@@ -327,23 +320,6 @@ PageGetFreeSpace(Page page)
return space; return space;
} }
/*
* PageManagerModeSet
*
* Sets mode to either: ShufflePageManagerMode (the default) or
* OverwritePageManagerMode. For use by access methods code
* for determining semantics of PageAddItem when the offsetNumber
* argument is passed in.
*/
void
PageManagerModeSet(PageManagerMode mode)
{
if (mode == ShufflePageManagerMode)
PageManagerShuffle = true;
else if (mode == OverwritePageManagerMode)
PageManagerShuffle = false;
}
/* /*
*---------------------------------------------------------------- *----------------------------------------------------------------
* PageIndexTupleDelete * PageIndexTupleDelete
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nbtree.h,v 1.38 2000/06/15 03:32:31 momjian Exp $ * $Id: nbtree.h,v 1.39 2000/07/21 06:42:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,14 +24,9 @@ ...@@ -24,14 +24,9 @@
* info. In addition, we need to know what sort of page this is * info. In addition, we need to know what sort of page this is
* (leaf or internal), and whether the page is available for reuse. * (leaf or internal), and whether the page is available for reuse.
* *
* Lehman and Yao's algorithm requires a ``high key'' on every page. * We also store a back-link to the parent page, but this cannot be trusted
* The high key on a page is guaranteed to be greater than or equal * very far since it does not get updated when the parent is split.
* to any key that appears on this page. Our insertion algorithm * See backend/access/nbtree/README for details.
* guarantees that we can use the initial least key on our right
* sibling as the high key. We allocate space for the line pointer
* to the high key in the opaque data at the end of the page.
*
* Rightmost pages in the tree have no high key.
*/ */
typedef struct BTPageOpaqueData typedef struct BTPageOpaqueData
...@@ -41,11 +36,11 @@ typedef struct BTPageOpaqueData ...@@ -41,11 +36,11 @@ typedef struct BTPageOpaqueData
BlockNumber btpo_parent; BlockNumber btpo_parent;
uint16 btpo_flags; uint16 btpo_flags;
#define BTP_LEAF (1 << 0) /* Bits defined in btpo_flags */
#define BTP_ROOT (1 << 1) #define BTP_LEAF (1 << 0) /* It's a leaf page */
#define BTP_FREE (1 << 2) #define BTP_ROOT (1 << 1) /* It's the root page (has no parent) */
#define BTP_META (1 << 3) #define BTP_FREE (1 << 2) /* not currently used... */
#define BTP_CHAIN (1 << 4) #define BTP_META (1 << 3) /* Set in the meta-page only */
} BTPageOpaqueData; } BTPageOpaqueData;
...@@ -84,21 +79,24 @@ typedef struct BTScanOpaqueData ...@@ -84,21 +79,24 @@ typedef struct BTScanOpaqueData
typedef BTScanOpaqueData *BTScanOpaque; typedef BTScanOpaqueData *BTScanOpaque;
/* /*
* BTItems are what we store in the btree. Each item has an index * BTItems are what we store in the btree. Each item is an index tuple,
* tuple, including key and pointer values. In addition, we must * including key and pointer values. (In some cases either the key or the
* guarantee that all tuples in the index are unique, in order to * pointer may go unused, see backend/access/nbtree/README for details.)
* satisfy some assumptions in Lehman and Yao. The way that we do *
* this is by generating a new OID for every insertion that we do in * Old comments:
* the tree. This adds eight bytes to the size of btree index * In addition, we must guarantee that all tuples in the index are unique,
* tuples. Note that we do not use the OID as part of a composite * in order to satisfy some assumptions in Lehman and Yao. The way that we
* key; the OID only serves as a unique identifier for a given index * do this is by generating a new OID for every insertion that we do in the
* tuple (logical position within a page). * tree. This adds eight bytes to the size of btree index tuples. Note
* that we do not use the OID as part of a composite key; the OID only
* serves as a unique identifier for a given index tuple (logical position
* within a page).
* *
* New comments: * New comments:
* actually, we must guarantee that all tuples in A LEVEL * actually, we must guarantee that all tuples in A LEVEL
* are unique, not in ALL INDEX. So, we can use bti_itup->t_tid * are unique, not in ALL INDEX. So, we can use bti_itup->t_tid
* as unique identifier for a given index tuple (logical position * as unique identifier for a given index tuple (logical position
* within a level). - vadim 04/09/97 * within a level). - vadim 04/09/97
*/ */
typedef struct BTItemData typedef struct BTItemData
...@@ -108,12 +106,13 @@ typedef struct BTItemData ...@@ -108,12 +106,13 @@ typedef struct BTItemData
typedef BTItemData *BTItem; typedef BTItemData *BTItem;
#define BTItemSame(i1, i2) ( i1->bti_itup.t_tid.ip_blkid.bi_hi == \ /* Test whether items are the "same" per the above notes */
i2->bti_itup.t_tid.ip_blkid.bi_hi && \ #define BTItemSame(i1, i2) ( (i1)->bti_itup.t_tid.ip_blkid.bi_hi == \
i1->bti_itup.t_tid.ip_blkid.bi_lo == \ (i2)->bti_itup.t_tid.ip_blkid.bi_hi && \
i2->bti_itup.t_tid.ip_blkid.bi_lo && \ (i1)->bti_itup.t_tid.ip_blkid.bi_lo == \
i1->bti_itup.t_tid.ip_posid == \ (i2)->bti_itup.t_tid.ip_blkid.bi_lo && \
i2->bti_itup.t_tid.ip_posid ) (i1)->bti_itup.t_tid.ip_posid == \
(i2)->bti_itup.t_tid.ip_posid )
/* /*
* BTStackData -- As we descend a tree, we push the (key, pointer) * BTStackData -- As we descend a tree, we push the (key, pointer)
...@@ -129,24 +128,12 @@ typedef struct BTStackData ...@@ -129,24 +128,12 @@ typedef struct BTStackData
{ {
BlockNumber bts_blkno; BlockNumber bts_blkno;
OffsetNumber bts_offset; OffsetNumber bts_offset;
BTItem bts_btitem; BTItemData bts_btitem;
struct BTStackData *bts_parent; struct BTStackData *bts_parent;
} BTStackData; } BTStackData;
typedef BTStackData *BTStack; typedef BTStackData *BTStack;
typedef struct BTPageState
{
Buffer btps_buf;
Page btps_page;
BTItem btps_lastbti;
OffsetNumber btps_lastoff;
OffsetNumber btps_firstoff;
int btps_level;
bool btps_doupper;
struct BTPageState *btps_next;
} BTPageState;
/* /*
* We need to be able to tell the difference between read and write * We need to be able to tell the difference between read and write
* requests for pages, in order to do locking correctly. * requests for pages, in order to do locking correctly.
...@@ -155,31 +142,49 @@ typedef struct BTPageState ...@@ -155,31 +142,49 @@ typedef struct BTPageState
#define BT_READ BUFFER_LOCK_SHARE #define BT_READ BUFFER_LOCK_SHARE
#define BT_WRITE BUFFER_LOCK_EXCLUSIVE #define BT_WRITE BUFFER_LOCK_EXCLUSIVE
/*
* Similarly, the difference between insertion and non-insertion binary
* searches on a given page makes a difference when we're descending the
* tree.
*/
#define BT_INSERTION 0
#define BT_DESCENT 1
/* /*
* In general, the btree code tries to localize its knowledge about * In general, the btree code tries to localize its knowledge about
* page layout to a couple of routines. However, we need a special * page layout to a couple of routines. However, we need a special
* value to indicate "no page number" in those places where we expect * value to indicate "no page number" in those places where we expect
* page numbers. * page numbers. We can use zero for this because we never need to
* make a pointer to the metadata page.
*/ */
#define P_NONE 0 #define P_NONE 0
/*
* Macros to test whether a page is leftmost or rightmost on its tree level,
* as well as other state info kept in the opaque data.
*/
#define P_LEFTMOST(opaque) ((opaque)->btpo_prev == P_NONE) #define P_LEFTMOST(opaque) ((opaque)->btpo_prev == P_NONE)
#define P_RIGHTMOST(opaque) ((opaque)->btpo_next == P_NONE) #define P_RIGHTMOST(opaque) ((opaque)->btpo_next == P_NONE)
#define P_ISLEAF(opaque) ((opaque)->btpo_flags & BTP_LEAF)
#define P_ISROOT(opaque) ((opaque)->btpo_flags & BTP_ROOT)
/*
* Lehman and Yao's algorithm requires a ``high key'' on every non-rightmost
* page. The high key is not a data key, but gives info about what range of
* keys is supposed to be on this page. The high key on a page is required
* to be greater than or equal to any data key that appears on the page.
* If we find ourselves trying to insert a key > high key, we know we need
* to move right (this should only happen if the page was split since we
* examined the parent page).
*
* Our insertion algorithm guarantees that we can use the initial least key
* on our right sibling as the high key. Once a page is created, its high
* key changes only if the page is split.
*
* On a non-rightmost page, the high key lives in item 1 and data items
* start in item 2. Rightmost pages have no high key, so we store data
* items beginning in item 1.
*/
#define P_HIKEY ((OffsetNumber) 1) #define P_HIKEY ((OffsetNumber) 1)
#define P_FIRSTKEY ((OffsetNumber) 2) #define P_FIRSTKEY ((OffsetNumber) 2)
#define P_FIRSTDATAKEY(opaque) (P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY)
/* /*
* Strategy numbers -- ordering of these is <, <=, =, >=, > * Operator strategy numbers -- ordering of these is <, <=, =, >=, >
*/ */
#define BTLessStrategyNumber 1 #define BTLessStrategyNumber 1
...@@ -199,13 +204,27 @@ typedef struct BTPageState ...@@ -199,13 +204,27 @@ typedef struct BTPageState
#define BTORDER_PROC 1 #define BTORDER_PROC 1
/*
* prototypes for functions in nbtree.c (external entry points for btree)
*/
extern bool BuildingBtree; /* in nbtree.c */
extern Datum btbuild(PG_FUNCTION_ARGS);
extern Datum btinsert(PG_FUNCTION_ARGS);
extern Datum btgettuple(PG_FUNCTION_ARGS);
extern Datum btbeginscan(PG_FUNCTION_ARGS);
extern Datum btrescan(PG_FUNCTION_ARGS);
extern void btmovescan(IndexScanDesc scan, Datum v);
extern Datum btendscan(PG_FUNCTION_ARGS);
extern Datum btmarkpos(PG_FUNCTION_ARGS);
extern Datum btrestrpos(PG_FUNCTION_ARGS);
extern Datum btdelete(PG_FUNCTION_ARGS);
/* /*
* prototypes for functions in nbtinsert.c * prototypes for functions in nbtinsert.c
*/ */
extern InsertIndexResult _bt_doinsert(Relation rel, BTItem btitem, extern InsertIndexResult _bt_doinsert(Relation rel, BTItem btitem,
bool index_is_unique, Relation heapRel); bool index_is_unique, Relation heapRel);
extern bool _bt_itemcmp(Relation rel, Size keysz, ScanKey scankey,
BTItem item1, BTItem item2, StrategyNumber strat);
/* /*
* prototypes for functions in nbtpage.c * prototypes for functions in nbtpage.c
...@@ -218,25 +237,8 @@ extern void _bt_wrtbuf(Relation rel, Buffer buf); ...@@ -218,25 +237,8 @@ extern void _bt_wrtbuf(Relation rel, Buffer buf);
extern void _bt_wrtnorelbuf(Relation rel, Buffer buf); extern void _bt_wrtnorelbuf(Relation rel, Buffer buf);
extern void _bt_pageinit(Page page, Size size); extern void _bt_pageinit(Page page, Size size);
extern void _bt_metaproot(Relation rel, BlockNumber rootbknum, int level); extern void _bt_metaproot(Relation rel, BlockNumber rootbknum, int level);
extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
extern void _bt_pagedel(Relation rel, ItemPointer tid); extern void _bt_pagedel(Relation rel, ItemPointer tid);
/*
* prototypes for functions in nbtree.c
*/
extern bool BuildingBtree; /* in nbtree.c */
extern Datum btbuild(PG_FUNCTION_ARGS);
extern Datum btinsert(PG_FUNCTION_ARGS);
extern Datum btgettuple(PG_FUNCTION_ARGS);
extern Datum btbeginscan(PG_FUNCTION_ARGS);
extern Datum btrescan(PG_FUNCTION_ARGS);
extern void btmovescan(IndexScanDesc scan, Datum v);
extern Datum btendscan(PG_FUNCTION_ARGS);
extern Datum btmarkpos(PG_FUNCTION_ARGS);
extern Datum btrestrpos(PG_FUNCTION_ARGS);
extern Datum btdelete(PG_FUNCTION_ARGS);
/* /*
* prototypes for functions in nbtscan.c * prototypes for functions in nbtscan.c
*/ */
...@@ -249,13 +251,13 @@ extern void AtEOXact_nbtree(void); ...@@ -249,13 +251,13 @@ extern void AtEOXact_nbtree(void);
* prototypes for functions in nbtsearch.c * prototypes for functions in nbtsearch.c
*/ */
extern BTStack _bt_search(Relation rel, int keysz, ScanKey scankey, extern BTStack _bt_search(Relation rel, int keysz, ScanKey scankey,
Buffer *bufP); Buffer *bufP, int access);
extern Buffer _bt_moveright(Relation rel, Buffer buf, int keysz, extern Buffer _bt_moveright(Relation rel, Buffer buf, int keysz,
ScanKey scankey, int access); ScanKey scankey, int access);
extern bool _bt_skeycmp(Relation rel, Size keysz, ScanKey scankey,
Page page, ItemId itemid, StrategyNumber strat);
extern OffsetNumber _bt_binsrch(Relation rel, Buffer buf, int keysz, extern OffsetNumber _bt_binsrch(Relation rel, Buffer buf, int keysz,
ScanKey scankey, int srchtype); ScanKey scankey);
extern int32 _bt_compare(Relation rel, int keysz, ScanKey scankey,
Page page, OffsetNumber offnum);
extern RetrieveIndexResult _bt_next(IndexScanDesc scan, ScanDirection dir); extern RetrieveIndexResult _bt_next(IndexScanDesc scan, ScanDirection dir);
extern RetrieveIndexResult _bt_first(IndexScanDesc scan, ScanDirection dir); extern RetrieveIndexResult _bt_first(IndexScanDesc scan, ScanDirection dir);
extern bool _bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir); extern bool _bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: bufpage.h,v 1.30 2000/07/03 02:54:21 vadim Exp $ * $Id: bufpage.h,v 1.31 2000/07/21 06:42:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -309,7 +309,6 @@ extern Page PageGetTempPage(Page page, Size specialSize); ...@@ -309,7 +309,6 @@ extern Page PageGetTempPage(Page page, Size specialSize);
extern void PageRestoreTempPage(Page tempPage, Page oldPage); extern void PageRestoreTempPage(Page tempPage, Page oldPage);
extern void PageRepairFragmentation(Page page); extern void PageRepairFragmentation(Page page);
extern Size PageGetFreeSpace(Page page); extern Size PageGetFreeSpace(Page page);
extern void PageManagerModeSet(PageManagerMode mode);
extern void PageIndexTupleDelete(Page page, OffsetNumber offset); extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment