nbtsort.c 18.9 KB
Newer Older
1
/*-------------------------------------------------------------------------
2 3
 * nbtsort.c
 *		Build a btree from sorted input by loading leaf pages sequentially.
4 5 6
 *
 * NOTES
 *
7 8
 * We use tuplesort.c to sort the given index tuples into order.
 * Then we scan the index tuples in order and build the btree pages
B
Bruce Momjian 已提交
9
 * for each level.	We load source tuples into leaf-level pages.
10 11 12 13 14
 * Whenever we fill a page at one level, we add a link to it to its
 * parent level (starting a new parent level if necessary).  When
 * done, we write out each final page on each level, adding it to
 * its parent level.  When we have only one page on a level, it must be
 * the root -- it can be attached to the btree metapage and we are done.
15
 *
16 17
 * This code is moderately slow (~10% slower) compared to the regular
 * btree (insertion) build code on sorted or well-clustered data.  On
18 19
 * random data, however, the insertion build code is unusable -- the
 * difference on a 60MB heap is a factor of 15 because the random
20 21 22 23
 * probes into the btree thrash the buffer pool.  (NOTE: the above
 * "10%" estimate is probably obsolete, since it refers to an old and
 * not very good external sort implementation that used to exist in
 * this module.  tuplesort.c is almost certainly faster.)
24
 *
25 26 27 28 29 30 31
 * It is not wise to pack the pages entirely full, since then *any*
 * insertion would cause a split (and not only of the leaf page; the need
 * for a split would cascade right up the tree).  The steady-state load
 * factor for btrees is usually estimated at 70%.  We choose to pack leaf
 * pages to 90% and upper pages to 70%.  This gives us reasonable density
 * (there aren't many upper pages if the keys are reasonable-size) without
 * incurring a lot of cascading splits during early insertions.
32
 *
33
 *
B
Bruce Momjian 已提交
34
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
35
 * Portions Copyright (c) 1994, Regents of the University of California
36 37
 *
 * IDENTIFICATION
38
 *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsort.c,v 1.69 2002/11/13 00:39:46 momjian Exp $
39
 *
40 41 42
 *-------------------------------------------------------------------------
 */

43
#include "postgres.h"
44

45
#include "access/nbtree.h"
46
#include "utils/tuplesort.h"
47

48

49
/*
50
 * Status record for spooling.
51
 */
52
struct BTSpool
53
{
54 55
	Tuplesortstate *sortstate;	/* state data for tuplesort.c */
	Relation	index;
56
	bool		isunique;
57
};
58

59
/*
B
Bruce Momjian 已提交
60
 * Status record for a btree page being built.	We have one of these
61
 * for each active tree level.
62 63 64 65 66 67 68 69 70
 *
 * The reason we need to store a copy of the minimum key is that we'll
 * need to propagate it to the parent node when this page is linked
 * into its parent.  However, if the page is not a leaf page, the first
 * entry on the page doesn't need to contain a key, so we will not have
 * stored the key itself on the page.  (You might think we could skip
 * copying the minimum key on leaf pages, but actually we must have a
 * writable copy anyway because we'll poke the page's address into it
 * before passing it up to the parent...)
71 72 73 74 75
 */
typedef struct BTPageState
{
	Buffer		btps_buf;		/* current buffer & page */
	Page		btps_page;
B
Bruce Momjian 已提交
76 77
	BTItem		btps_minkey;	/* copy of minimum key (first item) on
								 * page */
78
	OffsetNumber btps_lastoff;	/* last item offset loaded */
79
	int			btps_level;		/* tree level (0 = leaf) */
B
Bruce Momjian 已提交
80 81 82
	Size		btps_full;		/* "full" if less than this much free
								 * space */
	struct BTPageState *btps_next;		/* link to parent level, if any */
83 84 85
} BTPageState;


86 87 88 89 90 91
#define BTITEMSZ(btitem) \
	((btitem) ? \
	 (IndexTupleDSize((btitem)->bti_itup) + \
	  (sizeof(BTItemData) - sizeof(IndexTupleData))) : \
	 0)

92

93
static void _bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags);
94
static BTPageState *_bt_pagestate(Relation index, int flags, int level);
95 96
static void _bt_slideleft(Relation index, Buffer buf, Page page);
static void _bt_sortaddtup(Page page, Size itemsize,
B
Bruce Momjian 已提交
97
			   BTItem btitem, OffsetNumber itup_off);
98
static void _bt_buildadd(Relation index, BTPageState *state, BTItem bti);
99
static void _bt_uppershutdown(Relation index, BTPageState *state);
100
static void _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2);
101

102 103

/*
104
 * Interface routines
105 106 107 108
 */


/*
109
 * create and initialize a spool structure
110
 */
111
BTSpool *
112
_bt_spoolinit(Relation index, bool isunique)
113
{
114
	BTSpool    *btspool = (BTSpool *) palloc0(sizeof(BTSpool));
115

116 117
	btspool->index = index;
	btspool->isunique = isunique;
118

119
	btspool->sortstate = tuplesort_begin_index(index, isunique, false);
120

121
	/*
122 123 124
	 * Currently, tuplesort provides sort functions on IndexTuples. If we
	 * kept anything in a BTItem other than a regular IndexTuple, we'd
	 * need to modify tuplesort to understand BTItems as such.
125 126
	 */
	Assert(sizeof(BTItemData) == sizeof(IndexTupleData));
127

128
	return btspool;
129 130 131 132 133 134
}

/*
 * clean up a spool structure and its substructures.
 */
void
135
_bt_spooldestroy(BTSpool *btspool)
136
{
137
	tuplesort_end(btspool->sortstate);
138
	pfree((void *) btspool);
139 140 141
}

/*
142
 * spool a btitem into the sort file.
143
 */
144 145
void
_bt_spool(BTItem btitem, BTSpool *btspool)
146
{
147 148
	/* A BTItem is really just an IndexTuple */
	tuplesort_puttuple(btspool->sortstate, (void *) btitem);
149 150 151
}

/*
152 153
 * given a spool loaded by successive calls to _bt_spool,
 * create an entire btree.
154
 */
155
void
156
_bt_leafbuild(BTSpool *btspool, BTSpool *btspool2)
157
{
158
#ifdef BTREE_BUILD_STATS
159
	if (Show_btree_build_stats)
160
	{
161
		ShowUsage("BTREE BUILD (Spool) STATISTICS");
162
		ResetUsage();
163
	}
164
#endif   /* BTREE_BUILD_STATS */
165 166
	tuplesort_performsort(btspool->sortstate);

167 168 169
	if (btspool2)
		tuplesort_performsort(btspool2->sortstate);
	_bt_load(btspool->index, btspool, btspool2);
170 171 172 173
}


/*
174
 * Internal routines.
175
 */
176

177 178 179 180 181

/*
 * allocate a new, clean btree page, not linked to any siblings.
 */
static void
182
_bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags)
183
{
184
	BTPageOpaque opaque;
185

186 187
	*buf = _bt_getbuf(index, P_NEW, BT_WRITE);
	*page = BufferGetPage(*buf);
188 189

	/* Zero the page and set up standard page header info */
190
	_bt_pageinit(*page, BufferGetPageSize(*buf));
191 192

	/* Initialize BT opaque state */
193 194 195
	opaque = (BTPageOpaque) PageGetSpecialPointer(*page);
	opaque->btpo_prev = opaque->btpo_next = P_NONE;
	opaque->btpo_flags = flags;
196 197 198

	/* Make the P_HIKEY line pointer appear allocated */
	((PageHeader) *page)->pd_lower += sizeof(ItemIdData);
199 200
}

201 202 203 204 205 206 207
/*
 * allocate and initialize a new BTPageState.  the returned structure
 * is suitable for immediate use by _bt_buildadd.
 */
static BTPageState *
_bt_pagestate(Relation index, int flags, int level)
{
208
	BTPageState *state = (BTPageState *) palloc0(sizeof(BTPageState));
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

	/* create initial page */
	_bt_blnewpage(index, &(state->btps_buf), &(state->btps_page), flags);

	state->btps_minkey = (BTItem) NULL;
	/* initialize lastoff so first item goes into P_FIRSTKEY */
	state->btps_lastoff = P_HIKEY;
	state->btps_level = level;
	/* set "full" threshold based on level.  See notes at head of file. */
	if (level > 0)
		state->btps_full = (PageGetPageSize(state->btps_page) * 3) / 10;
	else
		state->btps_full = PageGetPageSize(state->btps_page) / 10;
	/* no parent level, yet */
	state->btps_next = (BTPageState *) NULL;

	return state;
}

228 229
/*
 * slide an array of ItemIds back one slot (from P_FIRSTKEY to
230 231 232
 * P_HIKEY, overwriting P_HIKEY).  we need to do this when we discover
 * that we have built an ItemId array in what has turned out to be a
 * P_RIGHTMOST page.
233 234 235 236
 */
static void
_bt_slideleft(Relation index, Buffer buf, Page page)
{
237 238 239 240
	OffsetNumber off;
	OffsetNumber maxoff;
	ItemId		previi;
	ItemId		thisii;
241 242 243 244 245 246 247 248 249 250 251 252

	if (!PageIsEmpty(page))
	{
		maxoff = PageGetMaxOffsetNumber(page);
		previi = PageGetItemId(page, P_HIKEY);
		for (off = P_FIRSTKEY; off <= maxoff; off = OffsetNumberNext(off))
		{
			thisii = PageGetItemId(page, off);
			*previi = *thisii;
			previi = thisii;
		}
		((PageHeader) page)->pd_lower -= sizeof(ItemIdData);
253
	}
254 255
}

256
/*
257 258 259 260 261 262 263 264 265 266 267
 * Add an item to a page being built.
 *
 * The main difference between this routine and a bare PageAddItem call
 * is that this code knows that the leftmost data item on a non-leaf
 * btree page doesn't need to have a key.  Therefore, it strips such
 * items down to just the item header.
 *
 * This is almost like nbtinsert.c's _bt_pgaddtup(), but we can't use
 * that because it assumes that P_RIGHTMOST() will return the correct
 * answer for the page.  Here, we don't know yet if the page will be
 * rightmost.  Offset P_FIRSTKEY is always the first data key.
268
 */
269 270 271 272 273
static void
_bt_sortaddtup(Page page,
			   Size itemsize,
			   BTItem btitem,
			   OffsetNumber itup_off)
274
{
275
	BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
B
Bruce Momjian 已提交
276
	BTItemData	truncitem;
277

B
Bruce Momjian 已提交
278
	if (!P_ISLEAF(opaque) && itup_off == P_FIRSTKEY)
279 280 281 282 283 284
	{
		memcpy(&truncitem, btitem, sizeof(BTItemData));
		truncitem.bti_itup.t_info = sizeof(BTItemData);
		btitem = &truncitem;
		itemsize = sizeof(BTItemData);
	}
285

286 287 288
	if (PageAddItem(page, (Item) btitem, itemsize, itup_off,
					LP_USED) == InvalidOffsetNumber)
		elog(FATAL, "btree: failed to add item to the page in _bt_sort");
289 290
}

291 292
/*----------
 * Add an item to a disk page from the sort output.
293
 *
294 295 296
 * We must be careful to observe the page layout conventions of nbtsearch.c:
 * - rightmost pages start data items at P_HIKEY instead of at P_FIRSTKEY.
 * - on non-leaf pages, the key portion of the first item need not be
B
Bruce Momjian 已提交
297
 *	 stored, we should store only the link.
298
 *
299
 * A leaf page being built looks like:
300 301
 *
 * +----------------+---------------------------------+
302
 * | PageHeaderData | linp0 linp1 linp2 ...			  |
303
 * +-----------+----+---------------------------------+
304
 * | ... linpN |									  |
305
 * +-----------+--------------------------------------+
306 307
 * |	 ^ last										  |
 * |												  |
308
 * +-------------+------------------------------------+
309
 * |			 | itemN ...						  |
310
 * +-------------+------------------+-----------------+
311
 * |		  ... item3 item2 item1 | "special space" |
312 313
 * +--------------------------------+-----------------+
 *
314 315
 * Contrast this with the diagram in bufpage.h; note the mismatch
 * between linps and items.  This is because we reserve linp0 as a
316 317
 * placeholder for the pointer to the "high key" item; when we have
 * filled up the page, we will set linp0 to point to itemN and clear
318 319
 * linpN.  On the other hand, if we find this is the last (rightmost)
 * page, we leave the items alone and slide the linp array over.
320
 *
321
 * 'last' pointer indicates the last offset added to the page.
322
 *----------
323
 */
324
static void
325
_bt_buildadd(Relation index, BTPageState *state, BTItem bti)
326
{
327 328 329 330 331
	Buffer		nbuf;
	Page		npage;
	OffsetNumber last_off;
	Size		pgspc;
	Size		btisz;
332 333 334 335 336 337 338

	nbuf = state->btps_buf;
	npage = state->btps_page;
	last_off = state->btps_lastoff;

	pgspc = PageGetFreeSpace(npage);
	btisz = BTITEMSZ(bti);
339
	btisz = MAXALIGN(btisz);
340 341

	/*
342 343 344 345 346
	 * Check whether the item can fit on a btree page at all. (Eventually,
	 * we ought to try to apply TOAST methods if not.) We actually need to
	 * be able to fit three items on every page, so restrict any one item
	 * to 1/3 the per-page available space. Note that at this point, btisz
	 * doesn't include the ItemId.
347 348
	 *
	 * NOTE: similar code appears in _bt_insertonpg() to defend against
349 350
	 * oversize items being inserted into an already-existing index. But
	 * during creation of an index, we don't go through there.
351
	 */
352
	if (btisz > BTMaxItemSize(npage))
353
		elog(ERROR, "btree: index item size %lu exceeds maximum %ld",
354
			 (unsigned long) btisz, BTMaxItemSize(npage));
355

356
	if (pgspc < btisz || pgspc < state->btps_full)
357
	{
358
		/*
359 360
		 * Item won't fit on this page, or we feel the page is full enough
		 * already.  Finish off the page and write it out.
361
		 */
362 363 364 365
		Buffer		obuf = nbuf;
		Page		opage = npage;
		ItemId		ii;
		ItemId		hii;
366
		BTItem		obti;
367

368 369 370
		/* Create new page */
		_bt_blnewpage(index, &nbuf, &npage,
					  (state->btps_level > 0) ? 0 : BTP_LEAF);
371 372

		/*
373 374
		 * We copy the last item on the page into the new page, and then
		 * rearrange the old page so that the 'last item' becomes its high
375 376 377 378
		 * key rather than a true data item.  There had better be at least
		 * two items on the page already, else the page would be empty of
		 * useful data.  (Hence, we must allow pages to be packed at least
		 * 2/3rds full; the 70% figure used above is close to minimum.)
379
		 */
380
		Assert(last_off > P_FIRSTKEY);
381
		ii = PageGetItemId(opage, last_off);
382 383
		obti = (BTItem) PageGetItem(opage, ii);
		_bt_sortaddtup(npage, ItemIdGetLength(ii), obti, P_FIRSTKEY);
384

385
		/*
386
		 * Move 'last' into the high key position on opage
387 388 389 390 391
		 */
		hii = PageGetItemId(opage, P_HIKEY);
		*hii = *ii;
		ii->lp_flags &= ~LP_USED;
		((PageHeader) opage)->pd_lower -= sizeof(ItemIdData);
392

393
		/*
B
Bruce Momjian 已提交
394 395 396
		 * Link the old buffer into its parent, using its minimum key. If
		 * we don't have a parent, we have to create one; this adds a new
		 * btree level.
397
		 */
398 399 400 401 402 403 404 405 406 407
		if (state->btps_next == (BTPageState *) NULL)
		{
			state->btps_next =
				_bt_pagestate(index, 0, state->btps_level + 1);
		}
		Assert(state->btps_minkey != NULL);
		ItemPointerSet(&(state->btps_minkey->bti_itup.t_tid),
					   BufferGetBlockNumber(obuf), P_HIKEY);
		_bt_buildadd(index, state->btps_next, state->btps_minkey);
		pfree((void *) state->btps_minkey);
408

409
		/*
410
		 * Save a copy of the minimum key for the new page.  We have to
B
Bruce Momjian 已提交
411 412
		 * copy it off the old page, not the new one, in case we are not
		 * at leaf level.
413 414 415 416 417 418 419
		 */
		state->btps_minkey = _bt_formitem(&(obti->bti_itup));

		/*
		 * Set the sibling links for both pages, and parent links too.
		 *
		 * It's not necessary to set the parent link at all, because it's
B
Bruce Momjian 已提交
420 421 422 423 424 425 426
		 * only used for handling concurrent root splits, but we may as
		 * well do it as a debugging aid.  Note we set new page's link as
		 * well as old's, because if the new page turns out to be the last
		 * of the level, _bt_uppershutdown won't change it.  The links may
		 * be out of date by the time the build finishes, but that's OK;
		 * they need only point to a left-sibling of the true parent.  See
		 * the README file for more info.
427 428
		 */
		{
429 430
			BTPageOpaque oopaque = (BTPageOpaque) PageGetSpecialPointer(opage);
			BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(npage);
431 432 433 434

			oopaque->btpo_next = BufferGetBlockNumber(nbuf);
			nopaque->btpo_prev = BufferGetBlockNumber(obuf);
			nopaque->btpo_next = P_NONE;
435 436
			oopaque->btpo_parent = nopaque->btpo_parent =
				BufferGetBlockNumber(state->btps_next->btps_buf);
437 438 439
		}

		/*
B
Bruce Momjian 已提交
440
		 * Write out the old page.	We never want to see it again, so we
441 442
		 * can give up our lock (if we had one; most likely BuildingBtree
		 * is set, so we aren't locking).
443
		 */
444
		_bt_wrtbuf(index, obuf);
445 446

		/*
447
		 * Reset last_off to point to new page
448
		 */
449
		last_off = P_FIRSTKEY;
450 451
	}

452
	/*
453 454
	 * If the new item is the first for its page, stash a copy for later.
	 * Note this will only happen for the first item on a level; on later
B
Bruce Momjian 已提交
455 456
	 * pages, the first item for a page is copied from the prior page in
	 * the code above.
457
	 */
458
	if (last_off == P_HIKEY)
459
	{
460 461
		Assert(state->btps_minkey == NULL);
		state->btps_minkey = _bt_formitem(&(bti->bti_itup));
462
	}
463 464 465 466 467 468

	/*
	 * Add the new item into the current page.
	 */
	last_off = OffsetNumberNext(last_off);
	_bt_sortaddtup(npage, btisz, bti, last_off);
469 470 471 472

	state->btps_buf = nbuf;
	state->btps_page = npage;
	state->btps_lastoff = last_off;
473 474
}

475 476 477
/*
 * Finish writing out the completed btree.
 */
478
static void
479
_bt_uppershutdown(Relation index, BTPageState *state)
480
{
481
	BTPageState *s;
482

483 484 485
	/*
	 * Each iteration of this loop completes one more level of the tree.
	 */
486 487
	for (s = state; s != (BTPageState *) NULL; s = s->btps_next)
	{
488 489 490
		BlockNumber blkno;
		BTPageOpaque opaque;

491 492
		blkno = BufferGetBlockNumber(s->btps_buf);
		opaque = (BTPageOpaque) PageGetSpecialPointer(s->btps_page);
493

494
		/*
495 496 497 498
		 * We have to link the last page on this level to somewhere.
		 *
		 * If we're at the top, it's the root, so attach it to the metapage.
		 * Otherwise, add an entry for it to its parent using its minimum
B
Bruce Momjian 已提交
499 500
		 * key.  This may cause the last page of the parent level to
		 * split, but that's not a problem -- we haven't gotten to it yet.
501
		 */
502
		if (s->btps_next == (BTPageState *) NULL)
503
		{
504 505 506 507 508
			opaque->btpo_flags |= BTP_ROOT;
			_bt_metaproot(index, blkno, s->btps_level + 1);
		}
		else
		{
509 510 511 512 513 514
			Assert(s->btps_minkey != NULL);
			ItemPointerSet(&(s->btps_minkey->bti_itup.t_tid),
						   blkno, P_HIKEY);
			_bt_buildadd(index, s->btps_next, s->btps_minkey);
			pfree((void *) s->btps_minkey);
			s->btps_minkey = NULL;
515
		}
516

517
		/*
518
		 * This is the rightmost page, so the ItemId array needs to be
B
Bruce Momjian 已提交
519
		 * slid back one slot.	Then we can dump out the page.
520 521 522 523
		 */
		_bt_slideleft(index, s->btps_buf, s->btps_page);
		_bt_wrtbuf(index, s->btps_buf);
	}
524 525 526
}

/*
527 528
 * Read tuples in correct sort order from tuplesort, and load them into
 * btree leaves.
529
 */
530
static void
531
_bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
532
{
533
	BTPageState *state = NULL;
534
	bool		merge = (btspool2 != NULL);
B
Bruce Momjian 已提交
535 536 537 538 539
	BTItem		bti,
				bti2 = NULL;
	bool		should_free,
				should_free2,
				load1;
540
	TupleDesc	tupdes = RelationGetDescr(index);
B
Bruce Momjian 已提交
541 542
	int			i,
				keysz = RelationGetNumberOfAttributes(index);
543 544 545
	ScanKey		indexScanKey = NULL;

	if (merge)
546
	{
547
		/*
B
Bruce Momjian 已提交
548 549 550 551 552 553 554 555 556
		 * Another BTSpool for dead tuples exists. Now we have to merge
		 * btspool and btspool2.
		 */
		ScanKey		entry;
		Datum		attrDatum1,
					attrDatum2;
		bool		isFirstNull,
					isSecondNull;
		int32		compare;
557 558 559 560 561 562 563

		/* the preparation of merge */
		bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
		bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
		indexScanKey = _bt_mkscankey_nodata(index);
		for (;;)
		{
B
Bruce Momjian 已提交
564
			load1 = true;		/* load BTSpool next ? */
565 566 567 568 569 570 571 572 573 574 575
			if (NULL == bti2)
			{
				if (NULL == bti)
					break;
			}
			else if (NULL != bti)
			{

				for (i = 1; i <= keysz; i++)
				{
					entry = indexScanKey + i - 1;
B
Bruce Momjian 已提交
576 577
					attrDatum1 = index_getattr((IndexTuple) bti, i, tupdes, &isFirstNull);
					attrDatum2 = index_getattr((IndexTuple) bti2, i, tupdes, &isSecondNull);
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
					if (isFirstNull)
					{
						if (!isSecondNull)
						{
							load1 = false;
							break;
						}
					}
					else if (isSecondNull)
						break;
					else
					{
						compare = DatumGetInt32(FunctionCall2(&entry->sk_func, attrDatum1, attrDatum2));
						if (compare > 0)
						{
							load1 = false;
							break;
						}
						else if (compare < 0)
							break;
B
Bruce Momjian 已提交
598
					}
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
				}
			}
			else
				load1 = false;

			/* When we see first tuple, create first index page */
			if (state == NULL)
				state = _bt_pagestate(index, BTP_LEAF, 0);

			if (load1)
			{
				_bt_buildadd(index, state, bti);
				if (should_free)
					pfree((void *) bti);
				bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
			}
			else
			{
				_bt_buildadd(index, state, bti2);
				if (should_free2)
					pfree((void *) bti2);
				bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
			}
		}
		_bt_freeskey(indexScanKey);
	}
B
Bruce Momjian 已提交
625 626
	else
/* merge is unnecessary */
627 628 629 630 631 632
	{
		while (bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free), bti != (BTItem) NULL)
		{
			/* When we see first tuple, create first index page */
			if (state == NULL)
				state = _bt_pagestate(index, BTP_LEAF, 0);
633

634 635 636 637
			_bt_buildadd(index, state, bti);
			if (should_free)
				pfree((void *) bti);
		}
638
	}
639

640
	/* Close down final pages, if we had any data at all */
641 642
	if (state != NULL)
		_bt_uppershutdown(index, state);
643
}