]> granicus.if.org Git - postgresql/blob - src/backend/access/nbtree/nbtsort.c
b52522fa8de8f8ab939add48d5f630f5ac654df5
[postgresql] / src / backend / access / nbtree / nbtsort.c
1 /*-------------------------------------------------------------------------
2  * nbtsort.c
3  *              Build a btree from sorted input by loading leaf pages sequentially.
4  *
5  * NOTES
6  *
7  * We use tuplesort.c to sort the given index tuples into order.
8  * Then we scan the index tuples in order and build the btree pages
9  * for each level.  We load source tuples into leaf-level pages.
10  * Whenever we fill a page at one level, we add a link to it to its
11  * parent level (starting a new parent level if necessary).  When
12  * done, we write out each final page on each level, adding it to
13  * its parent level.  When we have only one page on a level, it must be
14  * the root -- it can be attached to the btree metapage and we are done.
15  *
16  * This code is moderately slow (~10% slower) compared to the regular
17  * btree (insertion) build code on sorted or well-clustered data.  On
18  * random data, however, the insertion build code is unusable -- the
19  * difference on a 60MB heap is a factor of 15 because the random
20  * probes into the btree thrash the buffer pool.  (NOTE: the above
21  * "10%" estimate is probably obsolete, since it refers to an old and
22  * not very good external sort implementation that used to exist in
23  * this module.  tuplesort.c is almost certainly faster.)
24  *
25  * It is not wise to pack the pages entirely full, since then *any*
26  * insertion would cause a split (and not only of the leaf page; the need
27  * for a split would cascade right up the tree).  The steady-state load
28  * factor for btrees is usually estimated at 70%.  We choose to pack leaf
29  * pages to 90% and upper pages to 70%.  This gives us reasonable density
30  * (there aren't many upper pages if the keys are reasonable-size) without
31  * incurring a lot of cascading splits during early insertions.
32  *
33  *
34  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
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.57 2000/08/10 02:33:20 inoue 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         Tuplesortstate *sortstate;      /* state data for tuplesort.c */
55         Relation        index;
56         bool            isunique;
57 };
58
59 /*
60  * Status record for a btree page being built.  We have one of these
61  * for each active tree level.
62  *
63  * The reason we need to store a copy of the minimum key is that we'll
64  * need to propagate it to the parent node when this page is linked
65  * into its parent.  However, if the page is not a leaf page, the first
66  * entry on the page doesn't need to contain a key, so we will not have
67  * stored the key itself on the page.  (You might think we could skip
68  * copying the minimum key on leaf pages, but actually we must have a
69  * writable copy anyway because we'll poke the page's address into it
70  * before passing it up to the parent...)
71  */
72 typedef struct BTPageState
73 {
74         Buffer          btps_buf;               /* current buffer & page */
75         Page            btps_page;
76         BTItem          btps_minkey;    /* copy of minimum key (first item) on page */
77         OffsetNumber btps_lastoff;      /* last item offset loaded */
78         int                     btps_level;             /* tree level (0 = leaf) */
79         Size            btps_full;              /* "full" if less than this much free space */
80         struct BTPageState *btps_next; /* link to parent level, if any */
81 } BTPageState;
82
83
84 #define BTITEMSZ(btitem) \
85         ((btitem) ? \
86          (IndexTupleDSize((btitem)->bti_itup) + \
87           (sizeof(BTItemData) - sizeof(IndexTupleData))) : \
88          0)
89
90
91 static void _bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags);
92 static BTPageState *_bt_pagestate(Relation index, int flags, int level);
93 static void _bt_slideleft(Relation index, Buffer buf, Page page);
94 static void _bt_sortaddtup(Page page, Size itemsize,
95                                                    BTItem btitem, OffsetNumber itup_off);
96 static void _bt_buildadd(Relation index, BTPageState *state, BTItem bti);
97 static void _bt_uppershutdown(Relation index, BTPageState *state);
98 static void _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2);
99
100
101 /*
102  * Interface routines
103  */
104
105
106 /*
107  * create and initialize a spool structure
108  */
109 BTSpool    *
110 _bt_spoolinit(Relation index, bool isunique)
111 {
112         BTSpool    *btspool = (BTSpool *) palloc(sizeof(BTSpool));
113
114         MemSet((char *) btspool, 0, sizeof(BTSpool));
115
116         btspool->index = index;
117         btspool->isunique = isunique;
118
119         btspool->sortstate = tuplesort_begin_index(index, isunique, false);
120
121         /*
122          * Currently, tuplesort provides sort functions on IndexTuples. If we
123          * kept anything in a BTItem other than a regular IndexTuple, we'd
124          * need to modify tuplesort to understand BTItems as such.
125          */
126         Assert(sizeof(BTItemData) == sizeof(IndexTupleData));
127
128         return btspool;
129 }
130
131 /*
132  * clean up a spool structure and its substructures.
133  */
134 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 void
145 _bt_spool(BTItem btitem, BTSpool *btspool)
146 {
147         /* A BTItem is really just an IndexTuple */
148         tuplesort_puttuple(btspool->sortstate, (void *) btitem);
149 }
150
151 /*
152  * given a spool loaded by successive calls to _bt_spool,
153  * 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                 fprintf(StatFp, "BTREE BUILD (Spool) STATISTICS\n");
162                 ShowUsage();
163                 ResetUsage();
164         }
165 #endif /* BTREE_BUILD_STATS */
166         tuplesort_performsort(btspool->sortstate);
167
168         if (btspool2)
169                 tuplesort_performsort(btspool2->sortstate);
170         _bt_load(btspool->index, btspool, btspool2);
171 }
172
173
174 /*
175  * Internal routines.
176  */
177
178
179 /*
180  * allocate a new, clean btree page, not linked to any siblings.
181  */
182 static void
183 _bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags)
184 {
185         BTPageOpaque opaque;
186
187         *buf = _bt_getbuf(index, P_NEW, BT_WRITE);
188         *page = BufferGetPage(*buf);
189         _bt_pageinit(*page, BufferGetPageSize(*buf));
190         opaque = (BTPageOpaque) PageGetSpecialPointer(*page);
191         opaque->btpo_prev = opaque->btpo_next = P_NONE;
192         opaque->btpo_flags = flags;
193 }
194
195 /*
196  * allocate and initialize a new BTPageState.  the returned structure
197  * is suitable for immediate use by _bt_buildadd.
198  */
199 static BTPageState *
200 _bt_pagestate(Relation index, int flags, int level)
201 {
202         BTPageState *state = (BTPageState *) palloc(sizeof(BTPageState));
203
204         MemSet((char *) state, 0, sizeof(BTPageState));
205
206         /* create initial page */
207         _bt_blnewpage(index, &(state->btps_buf), &(state->btps_page), flags);
208
209         state->btps_minkey = (BTItem) NULL;
210         /* initialize lastoff so first item goes into P_FIRSTKEY */
211         state->btps_lastoff = P_HIKEY;
212         state->btps_level = level;
213         /* set "full" threshold based on level.  See notes at head of file. */
214         if (level > 0)
215                 state->btps_full = (PageGetPageSize(state->btps_page) * 3) / 10;
216         else
217                 state->btps_full = PageGetPageSize(state->btps_page) / 10;
218         /* no parent level, yet */
219         state->btps_next = (BTPageState *) NULL;
220
221         return state;
222 }
223
224 /*
225  * slide an array of ItemIds back one slot (from P_FIRSTKEY to
226  * P_HIKEY, overwriting P_HIKEY).  we need to do this when we discover
227  * that we have built an ItemId array in what has turned out to be a
228  * P_RIGHTMOST page.
229  */
230 static void
231 _bt_slideleft(Relation index, Buffer buf, Page page)
232 {
233         OffsetNumber off;
234         OffsetNumber maxoff;
235         ItemId          previi;
236         ItemId          thisii;
237
238         if (!PageIsEmpty(page))
239         {
240                 maxoff = PageGetMaxOffsetNumber(page);
241                 previi = PageGetItemId(page, P_HIKEY);
242                 for (off = P_FIRSTKEY; off <= maxoff; off = OffsetNumberNext(off))
243                 {
244                         thisii = PageGetItemId(page, off);
245                         *previi = *thisii;
246                         previi = thisii;
247                 }
248                 ((PageHeader) page)->pd_lower -= sizeof(ItemIdData);
249         }
250 }
251
252 /*
253  * Add an item to a page being built.
254  *
255  * The main difference between this routine and a bare PageAddItem call
256  * is that this code knows that the leftmost data item on a non-leaf
257  * btree page doesn't need to have a key.  Therefore, it strips such
258  * items down to just the item header.
259  *
260  * This is almost like nbtinsert.c's _bt_pgaddtup(), but we can't use
261  * that because it assumes that P_RIGHTMOST() will return the correct
262  * answer for the page.  Here, we don't know yet if the page will be
263  * rightmost.  Offset P_FIRSTKEY is always the first data key.
264  */
265 static void
266 _bt_sortaddtup(Page page,
267                            Size itemsize,
268                            BTItem btitem,
269                            OffsetNumber itup_off)
270 {
271         BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
272         BTItemData truncitem;
273
274         if (! P_ISLEAF(opaque) && itup_off == P_FIRSTKEY)
275         {
276                 memcpy(&truncitem, btitem, sizeof(BTItemData));
277                 truncitem.bti_itup.t_info = sizeof(BTItemData);
278                 btitem = &truncitem;
279                 itemsize = sizeof(BTItemData);
280         }
281
282         if (PageAddItem(page, (Item) btitem, itemsize, itup_off,
283                                         LP_USED) == InvalidOffsetNumber)
284                 elog(FATAL, "btree: failed to add item to the page in _bt_sort");
285 }
286
287 /*----------
288  * Add an item to a disk page from the sort output.
289  *
290  * We must be careful to observe the page layout conventions of nbtsearch.c:
291  * - rightmost pages start data items at P_HIKEY instead of at P_FIRSTKEY.
292  * - on non-leaf pages, the key portion of the first item need not be
293  *   stored, we should store only the link.
294  *
295  * A leaf page being built looks like:
296  *
297  * +----------------+---------------------------------+
298  * | PageHeaderData | linp0 linp1 linp2 ...                       |
299  * +-----------+----+---------------------------------+
300  * | ... linpN |                                                                          |
301  * +-----------+--------------------------------------+
302  * |     ^ last                                                                           |
303  * |                                                                                              |
304  * +-------------+------------------------------------+
305  * |                     | itemN ...                                              |
306  * +-------------+------------------+-----------------+
307  * |              ... item3 item2 item1 | "special space" |
308  * +--------------------------------+-----------------+
309  *
310  * Contrast this with the diagram in bufpage.h; note the mismatch
311  * between linps and items.  This is because we reserve linp0 as a
312  * placeholder for the pointer to the "high key" item; when we have
313  * filled up the page, we will set linp0 to point to itemN and clear
314  * linpN.  On the other hand, if we find this is the last (rightmost)
315  * page, we leave the items alone and slide the linp array over.
316  *
317  * 'last' pointer indicates the last offset added to the page.
318  *----------
319  */
320 static void
321 _bt_buildadd(Relation index, BTPageState *state, BTItem bti)
322 {
323         Buffer          nbuf;
324         Page            npage;
325         OffsetNumber last_off;
326         Size            pgspc;
327         Size            btisz;
328
329         nbuf = state->btps_buf;
330         npage = state->btps_page;
331         last_off = state->btps_lastoff;
332
333         pgspc = PageGetFreeSpace(npage);
334         btisz = BTITEMSZ(bti);
335         btisz = MAXALIGN(btisz);
336
337         /*
338          * Check whether the item can fit on a btree page at all. (Eventually,
339          * we ought to try to apply TOAST methods if not.) We actually need to
340          * be able to fit three items on every page, so restrict any one item
341          * to 1/3 the per-page available space. Note that at this point, btisz
342          * doesn't include the ItemId.
343          *
344          * NOTE: similar code appears in _bt_insertonpg() to defend against
345          * oversize items being inserted into an already-existing index. But
346          * during creation of an index, we don't go through there.
347          */
348         if (btisz > (PageGetPageSize(npage) - sizeof(PageHeaderData) - MAXALIGN(sizeof(BTPageOpaqueData))) / 3 - sizeof(ItemIdData))
349                 elog(ERROR, "btree: index item size %d exceeds maximum %ld",
350                          btisz,
351                          (PageGetPageSize(npage) - sizeof(PageHeaderData) - MAXALIGN(sizeof(BTPageOpaqueData))) /3 - sizeof(ItemIdData));
352
353         if (pgspc < btisz || pgspc < state->btps_full)
354         {
355                 /*
356                  * Item won't fit on this page, or we feel the page is full enough
357                  * already.  Finish off the page and write it out.
358                  */
359                 Buffer          obuf = nbuf;
360                 Page            opage = npage;
361                 ItemId          ii;
362                 ItemId          hii;
363                 BTItem          obti;
364
365                 /* Create new page */
366                 _bt_blnewpage(index, &nbuf, &npage,
367                                           (state->btps_level > 0) ? 0 : BTP_LEAF);
368
369                 /*
370                  * We copy the last item on the page into the new page, and then
371                  * rearrange the old page so that the 'last item' becomes its high
372                  * key rather than a true data item.  There had better be at least
373                  * two items on the page already, else the page would be empty of
374                  * useful data.  (Hence, we must allow pages to be packed at least
375                  * 2/3rds full; the 70% figure used above is close to minimum.)
376                  */
377                 Assert(last_off > P_FIRSTKEY);
378                 ii = PageGetItemId(opage, last_off);
379                 obti = (BTItem) PageGetItem(opage, ii);
380                 _bt_sortaddtup(npage, ItemIdGetLength(ii), obti, P_FIRSTKEY);
381
382                 /*
383                  * Move 'last' into the high key position on opage
384                  */
385                 hii = PageGetItemId(opage, P_HIKEY);
386                 *hii = *ii;
387                 ii->lp_flags &= ~LP_USED;
388                 ((PageHeader) opage)->pd_lower -= sizeof(ItemIdData);
389
390                 /*
391                  * Link the old buffer into its parent, using its minimum key.
392                  * If we don't have a parent, we have to create one;
393                  * this adds a new btree level.
394                  */
395                 if (state->btps_next == (BTPageState *) NULL)
396                 {
397                         state->btps_next =
398                                 _bt_pagestate(index, 0, state->btps_level + 1);
399                 }
400                 Assert(state->btps_minkey != NULL);
401                 ItemPointerSet(&(state->btps_minkey->bti_itup.t_tid),
402                                            BufferGetBlockNumber(obuf), P_HIKEY);
403                 _bt_buildadd(index, state->btps_next, state->btps_minkey);
404                 pfree((void *) state->btps_minkey);
405
406                 /*
407                  * Save a copy of the minimum key for the new page.  We have to
408                  * copy it off the old page, not the new one, in case we are
409                  * not at leaf level.
410                  */
411                 state->btps_minkey = _bt_formitem(&(obti->bti_itup));
412
413                 /*
414                  * Set the sibling links for both pages, and parent links too.
415                  *
416                  * It's not necessary to set the parent link at all, because it's
417                  * only used for handling concurrent root splits, but we may as well
418                  * do it as a debugging aid.  Note we set new page's link as well
419                  * as old's, because if the new page turns out to be the last of
420                  * the level, _bt_uppershutdown won't change it.  The links may be
421                  * out of date by the time the build finishes, but that's OK; they
422                  * need only point to a left-sibling of the true parent.  See the
423                  * README file for more info.
424                  */
425                 {
426                         BTPageOpaque oopaque = (BTPageOpaque) PageGetSpecialPointer(opage);
427                         BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(npage);
428
429                         oopaque->btpo_next = BufferGetBlockNumber(nbuf);
430                         nopaque->btpo_prev = BufferGetBlockNumber(obuf);
431                         nopaque->btpo_next = P_NONE;
432                         oopaque->btpo_parent = nopaque->btpo_parent =
433                                 BufferGetBlockNumber(state->btps_next->btps_buf);
434                 }
435
436                 /*
437                  * Write out the old page.  We never want to see it again, so we
438                  * can give up our lock (if we had one; most likely BuildingBtree
439                  * is set, so we aren't locking).
440                  */
441                 _bt_wrtbuf(index, obuf);
442
443                 /*
444                  * Reset last_off to point to new page
445                  */
446                 last_off = P_FIRSTKEY;
447         }
448
449         /*
450          * If the new item is the first for its page, stash a copy for later.
451          * Note this will only happen for the first item on a level; on later
452          * pages, the first item for a page is copied from the prior page
453          * in the code above.
454          */
455         if (last_off == P_HIKEY)
456         {
457                 Assert(state->btps_minkey == NULL);
458                 state->btps_minkey = _bt_formitem(&(bti->bti_itup));
459         }
460
461         /*
462          * Add the new item into the current page.
463          */
464         last_off = OffsetNumberNext(last_off);
465         _bt_sortaddtup(npage, btisz, bti, last_off);
466
467         state->btps_buf = nbuf;
468         state->btps_page = npage;
469         state->btps_lastoff = last_off;
470 }
471
472 /*
473  * Finish writing out the completed btree.
474  */
475 static void
476 _bt_uppershutdown(Relation index, BTPageState *state)
477 {
478         BTPageState *s;
479
480         /*
481          * Each iteration of this loop completes one more level of the tree.
482          */
483         for (s = state; s != (BTPageState *) NULL; s = s->btps_next)
484         {
485                 BlockNumber blkno;
486                 BTPageOpaque opaque;
487
488                 blkno = BufferGetBlockNumber(s->btps_buf);
489                 opaque = (BTPageOpaque) PageGetSpecialPointer(s->btps_page);
490
491                 /*
492                  * We have to link the last page on this level to somewhere.
493                  *
494                  * If we're at the top, it's the root, so attach it to the metapage.
495                  * Otherwise, add an entry for it to its parent using its minimum
496                  * key.  This may cause the last page of the parent level to split,
497                  * but that's not a problem -- we haven't gotten to it yet.
498                  */
499                 if (s->btps_next == (BTPageState *) NULL)
500                 {
501                         opaque->btpo_flags |= BTP_ROOT;
502                         _bt_metaproot(index, blkno, s->btps_level + 1);
503                 }
504                 else
505                 {
506                         Assert(s->btps_minkey != NULL);
507                         ItemPointerSet(&(s->btps_minkey->bti_itup.t_tid),
508                                                    blkno, P_HIKEY);
509                         _bt_buildadd(index, s->btps_next, s->btps_minkey);
510                         pfree((void *) s->btps_minkey);
511                         s->btps_minkey = NULL;
512                 }
513
514                 /*
515                  * This is the rightmost page, so the ItemId array needs to be
516                  * slid back one slot.  Then we can dump out the page.
517                  */
518                 _bt_slideleft(index, s->btps_buf, s->btps_page);
519                 _bt_wrtbuf(index, s->btps_buf);
520         }
521 }
522
523 /*
524  * Read tuples in correct sort order from tuplesort, and load them into
525  * btree leaves.
526  */
527 static void
528 _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
529 {
530         BTPageState *state = NULL;
531         bool            merge = (btspool2 != NULL);
532         BTItem          bti, bti2 = NULL;
533         bool            should_free, should_free2, load1;
534         TupleDesc       tupdes = RelationGetDescr(index);
535         int             i, keysz = RelationGetNumberOfAttributes(index);
536         ScanKey         indexScanKey = NULL;
537
538         if (merge)
539         {
540                 /*
541                  * Another BTSpool for dead tuples exists.
542                  * Now we have to merge btspool and btspool2.
543                  */
544                 ScanKey entry;
545                 Datum   attrDatum1, attrDatum2;
546                 bool    isFirstNull, isSecondNull;
547                 int32   compare;
548
549                 /* the preparation of merge */
550                 bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
551                 bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
552                 indexScanKey = _bt_mkscankey_nodata(index);
553                 for (;;)
554                 {
555                         load1 = true; /* load BTSpool next ? */
556                         if (NULL == bti2)
557                         {
558                                 if (NULL == bti)
559                                         break;
560                         }
561                         else if (NULL != bti)
562                         {
563
564                                 for (i = 1; i <= keysz; i++)
565                                 {
566                                         entry = indexScanKey + i - 1;
567                                         attrDatum1 = index_getattr((IndexTuple)bti, i, tupdes, &isFirstNull);
568                                         attrDatum2 = index_getattr((IndexTuple)bti2, i, tupdes, &isSecondNull);
569                                         if (isFirstNull)
570                                         {
571                                                 if (!isSecondNull)
572                                                 {
573                                                         load1 = false;
574                                                         break;
575                                                 }
576                                         }
577                                         else if (isSecondNull)
578                                                 break;
579                                         else
580                                         {
581                                                 compare = DatumGetInt32(FunctionCall2(&entry->sk_func, attrDatum1, attrDatum2));
582                                                 if (compare > 0)
583                                                 {
584                                                         load1 = false;
585                                                         break;
586                                                 }
587                                                 else if (compare < 0)
588                                                         break;
589                                         }       
590                                 }
591                         }
592                         else
593                                 load1 = false;
594
595                         /* When we see first tuple, create first index page */
596                         if (state == NULL)
597                                 state = _bt_pagestate(index, BTP_LEAF, 0);
598
599                         if (load1)
600                         {
601                                 _bt_buildadd(index, state, bti);
602                                 if (should_free)
603                                         pfree((void *) bti);
604                                 bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
605                         }
606                         else
607                         {
608                                 _bt_buildadd(index, state, bti2);
609                                 if (should_free2)
610                                         pfree((void *) bti2);
611                                 bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
612                         }
613                 }
614                 _bt_freeskey(indexScanKey);
615         }
616         else    /* merge is unnecessary */
617         {
618                 while (bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free), bti != (BTItem) NULL)
619                 {
620                         /* When we see first tuple, create first index page */
621                         if (state == NULL)
622                                 state = _bt_pagestate(index, BTP_LEAF, 0);
623
624                         _bt_buildadd(index, state, bti);
625                         if (should_free)
626                                 pfree((void *) bti);
627                 }
628         }
629
630         /* Close down final pages, if we had any data at all */
631         if (state != NULL)
632                 _bt_uppershutdown(index, state);
633 }