mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
Adjust INCLUDE index truncation comments and code.
Add several assertions that ensure that we're dealing with a pivot tuple without non-key attributes where that's expected. Also, remove the assertion within _bt_isequal(), restoring the v10 function signature. A similar check will be performed for the page highkey within _bt_moveright() in most cases. Also avoid dropping all objects within regression tests, to increase pg_dump test coverage for INCLUDE indexes. Rather than using infrastructure that's generally intended to be used with reference counted heap tuple descriptors during truncation, use the same function that was introduced to store flat TupleDescs in shared memory (we use a temp palloc'd buffer). This isn't strictly necessary, but seems more future-proof than the old approach. It also lets us avoid including rel.h within indextuple.c, which was arguably a modularity violation. Also, we now call index_deform_tuple() with the truncated TupleDesc, not the source TupleDesc, since that's more robust, and saves a few cycles. In passing, fix a memory leak by pfree'ing truncated pivot tuple memory during CREATE INDEX. Also pfree during a page split, just to be consistent. Refactor _bt_check_natts() to be more readable. Author: Peter Geoghegan with some editorization by me Reviewed by: Alexander Korotkov, Teodor Sigaev Discussion: https://www.postgresql.org/message-id/CAH2-Wz%3DkCWuXeMrBCopC-tFs3FbiVxQNjjgNKdG2sHxZ5k2y3w%40mail.gmail.com
This commit is contained in:
@@ -84,7 +84,7 @@ static void _bt_checksplitloc(FindSplitData *state,
|
||||
int dataitemstoleft, Size firstoldonrightsz);
|
||||
static bool _bt_pgaddtup(Page page, Size itemsize, IndexTuple itup,
|
||||
OffsetNumber itup_off);
|
||||
static bool _bt_isequal(Relation idxrel, Page page, OffsetNumber offnum,
|
||||
static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
|
||||
int keysz, ScanKey scankey);
|
||||
static void _bt_vacuum_one_page(Relation rel, Buffer buffer, Relation heapRel);
|
||||
|
||||
@@ -343,6 +343,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
||||
IndexUniqueCheck checkUnique, bool *is_unique,
|
||||
uint32 *speculativeToken)
|
||||
{
|
||||
TupleDesc itupdesc = RelationGetDescr(rel);
|
||||
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
|
||||
SnapshotData SnapshotDirty;
|
||||
OffsetNumber maxoff;
|
||||
@@ -402,7 +403,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
||||
* in real comparison, but only for ordering/finding items on
|
||||
* pages. - vadim 03/24/97
|
||||
*/
|
||||
if (!_bt_isequal(rel, page, offset, indnkeyatts, itup_scankey))
|
||||
if (!_bt_isequal(itupdesc, page, offset, indnkeyatts, itup_scankey))
|
||||
break; /* we're past all the equal tuples */
|
||||
|
||||
/* okay, we gotta fetch the heap tuple ... */
|
||||
@@ -566,7 +567,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
||||
/* If scankey == hikey we gotta check the next page too */
|
||||
if (P_RIGHTMOST(opaque))
|
||||
break;
|
||||
if (!_bt_isequal(rel, page, P_HIKEY,
|
||||
if (!_bt_isequal(itupdesc, page, P_HIKEY,
|
||||
indnkeyatts, itup_scankey))
|
||||
break;
|
||||
/* Advance to next non-dead page --- there must be one */
|
||||
@@ -849,6 +850,13 @@ _bt_insertonpg(Relation rel,
|
||||
|
||||
/* child buffer must be given iff inserting on an internal page */
|
||||
Assert(P_ISLEAF(lpageop) == !BufferIsValid(cbuf));
|
||||
/* tuple must have appropriate number of attributes */
|
||||
Assert(!P_ISLEAF(lpageop) ||
|
||||
BTreeTupleGetNAtts(itup, rel) ==
|
||||
IndexRelationGetNumberOfAttributes(rel));
|
||||
Assert(P_ISLEAF(lpageop) ||
|
||||
BTreeTupleGetNAtts(itup, rel) ==
|
||||
IndexRelationGetNumberOfKeyAttributes(rel));
|
||||
|
||||
/* The caller should've finished any incomplete splits already. */
|
||||
if (P_INCOMPLETE_SPLIT(lpageop))
|
||||
@@ -956,6 +964,18 @@ _bt_insertonpg(Relation rel,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Every internal page should have exactly one negative infinity item
|
||||
* at all times. Only _bt_split() and _bt_newroot() should add items
|
||||
* that become negative infinity items through truncation, since
|
||||
* they're the only routines that allocate new internal pages. Do not
|
||||
* allow a retail insertion of a new item at the negative infinity
|
||||
* offset.
|
||||
*/
|
||||
if (!P_ISLEAF(lpageop) && newitemoff == P_FIRSTDATAKEY(lpageop))
|
||||
elog(ERROR, "cannot insert second negative infinity item in block %u of index \"%s\"",
|
||||
itup_blkno, RelationGetRelationName(rel));
|
||||
|
||||
/* Do the update. No ereport(ERROR) until changes are logged */
|
||||
START_CRIT_SECTION();
|
||||
|
||||
@@ -1002,7 +1022,6 @@ _bt_insertonpg(Relation rel,
|
||||
xl_btree_metadata xlmeta;
|
||||
uint8 xlinfo;
|
||||
XLogRecPtr recptr;
|
||||
IndexTupleData trunctuple;
|
||||
|
||||
xlrec.offnum = itup_off;
|
||||
|
||||
@@ -1038,17 +1057,8 @@ _bt_insertonpg(Relation rel,
|
||||
xlinfo = XLOG_BTREE_INSERT_META;
|
||||
}
|
||||
|
||||
/* Read comments in _bt_pgaddtup */
|
||||
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
|
||||
if (!P_ISLEAF(lpageop) && newitemoff == P_FIRSTDATAKEY(lpageop))
|
||||
{
|
||||
trunctuple = *itup;
|
||||
trunctuple.t_info = sizeof(IndexTupleData);
|
||||
XLogRegisterBufData(0, (char *) &trunctuple,
|
||||
sizeof(IndexTupleData));
|
||||
}
|
||||
else
|
||||
XLogRegisterBufData(0, (char *) itup, IndexTupleSize(itup));
|
||||
XLogRegisterBufData(0, (char *) itup, IndexTupleSize(itup));
|
||||
|
||||
recptr = XLogInsert(RM_BTREE_ID, xlinfo);
|
||||
|
||||
@@ -1203,6 +1213,7 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
|
||||
itemid = PageGetItemId(origpage, P_HIKEY);
|
||||
itemsz = ItemIdGetLength(itemid);
|
||||
item = (IndexTuple) PageGetItem(origpage, itemid);
|
||||
Assert(BTreeTupleGetNAtts(item, rel) == indnkeyatts);
|
||||
if (PageAddItem(rightpage, (Item) item, itemsz, rightoff,
|
||||
false, false) == InvalidOffsetNumber)
|
||||
{
|
||||
@@ -1235,20 +1246,25 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
|
||||
}
|
||||
|
||||
/*
|
||||
* We must truncate included attributes of the "high key" item, before
|
||||
* insert it onto the leaf page. It's the only point in insertion
|
||||
* process, where we perform truncation. All other functions work with
|
||||
* this high key and do not change it.
|
||||
* Truncate non-key (INCLUDE) attributes of the high key item before
|
||||
* inserting it on the left page. This only needs to happen at the leaf
|
||||
* level, since in general all pivot tuple values originate from leaf
|
||||
* level high keys. This isn't just about avoiding unnecessary work,
|
||||
* though; truncating unneeded key attributes (more aggressive suffix
|
||||
* truncation) can only be performed at the leaf level anyway. This is
|
||||
* because a pivot tuple in a grandparent page must guide a search not
|
||||
* only to the correct parent page, but also to the correct leaf page.
|
||||
*/
|
||||
if (indnatts != indnkeyatts && isleaf)
|
||||
{
|
||||
lefthikey = _bt_truncate_tuple(rel, item);
|
||||
lefthikey = _bt_nonkey_truncate(rel, item);
|
||||
itemsz = IndexTupleSize(lefthikey);
|
||||
itemsz = MAXALIGN(itemsz);
|
||||
}
|
||||
else
|
||||
lefthikey = item;
|
||||
|
||||
Assert(BTreeTupleGetNAtts(lefthikey, rel) == indnkeyatts);
|
||||
if (PageAddItem(leftpage, (Item) lefthikey, itemsz, leftoff,
|
||||
false, false) == InvalidOffsetNumber)
|
||||
{
|
||||
@@ -1258,6 +1274,9 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
|
||||
origpagenumber, RelationGetRelationName(rel));
|
||||
}
|
||||
leftoff = OffsetNumberNext(leftoff);
|
||||
/* be tidy */
|
||||
if (lefthikey != item)
|
||||
pfree(lefthikey);
|
||||
|
||||
/*
|
||||
* Now transfer all the data items to the appropriate page.
|
||||
@@ -2143,7 +2162,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
|
||||
left_item = (IndexTuple) palloc(left_item_sz);
|
||||
left_item->t_info = left_item_sz;
|
||||
BTreeInnerTupleSetDownLink(left_item, lbkno);
|
||||
BTreeTupSetNAtts(left_item, 0);
|
||||
BTreeTupleSetNAtts(left_item, 0);
|
||||
|
||||
/*
|
||||
* Create downlink item for right page. The key for it is obtained from
|
||||
@@ -2180,6 +2199,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
|
||||
* Note: we *must* insert the two items in item-number order, for the
|
||||
* benefit of _bt_restore_page().
|
||||
*/
|
||||
Assert(BTreeTupleGetNAtts(left_item, rel) == 0);
|
||||
if (PageAddItem(rootpage, (Item) left_item, left_item_sz, P_HIKEY,
|
||||
false, false) == InvalidOffsetNumber)
|
||||
elog(PANIC, "failed to add leftkey to new root page"
|
||||
@@ -2189,6 +2209,8 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
|
||||
/*
|
||||
* insert the right page pointer into the new root page.
|
||||
*/
|
||||
Assert(BTreeTupleGetNAtts(right_item, rel) ==
|
||||
IndexRelationGetNumberOfKeyAttributes(rel));
|
||||
if (PageAddItem(rootpage, (Item) right_item, right_item_sz, P_FIRSTKEY,
|
||||
false, false) == InvalidOffsetNumber)
|
||||
elog(PANIC, "failed to add rightkey to new root page"
|
||||
@@ -2284,7 +2306,7 @@ _bt_pgaddtup(Page page,
|
||||
{
|
||||
trunctuple = *itup;
|
||||
trunctuple.t_info = sizeof(IndexTupleData);
|
||||
BTreeTupSetNAtts(&trunctuple, 0);
|
||||
BTreeTupleSetNAtts(&trunctuple, 0);
|
||||
itup = &trunctuple;
|
||||
itemsize = sizeof(IndexTupleData);
|
||||
}
|
||||
@@ -2303,10 +2325,9 @@ _bt_pgaddtup(Page page,
|
||||
* Rule is simple: NOT_NULL not equal NULL, NULL not equal NULL too.
|
||||
*/
|
||||
static bool
|
||||
_bt_isequal(Relation idxrel, Page page, OffsetNumber offnum,
|
||||
_bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
|
||||
int keysz, ScanKey scankey)
|
||||
{
|
||||
TupleDesc itupdesc = RelationGetDescr(idxrel);
|
||||
IndexTuple itup;
|
||||
int i;
|
||||
|
||||
@@ -2316,16 +2337,11 @@ _bt_isequal(Relation idxrel, Page page, OffsetNumber offnum,
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
|
||||
/*
|
||||
* Index tuple shouldn't be truncated. Despite we technically could
|
||||
* compare truncated tuple as well, this function should be only called
|
||||
* for regular non-truncated leaf tuples and P_HIKEY tuple on
|
||||
* rightmost leaf page.
|
||||
* It's okay that we might perform a comparison against a truncated page
|
||||
* high key when caller needs to determine if _bt_check_unique scan must
|
||||
* continue on to the next page. Caller never asks us to compare non-key
|
||||
* attributes within an INCLUDE index.
|
||||
*/
|
||||
Assert((P_RIGHTMOST((BTPageOpaque) PageGetSpecialPointer(page)) ||
|
||||
offnum != P_HIKEY)
|
||||
? BTreeTupGetNAtts(itup, idxrel) == itupdesc->natts
|
||||
: true);
|
||||
|
||||
for (i = 1; i <= keysz; i++)
|
||||
{
|
||||
AttrNumber attno;
|
||||
|
Reference in New Issue
Block a user