88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.62 2004/12/31 22:01:10 pgsql Exp $
11+ * $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.63 2005/03/22 06:17:03 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -274,13 +274,14 @@ PageRestoreTempPage(Page tempPage, Page oldPage)
274274}
275275
276276/*
277- * sorting support for PageRepairFragmentation
277+ * sorting support for PageRepairFragmentation and PageIndexMultiDelete
278278 */
279279typedef struct itemIdSortData
280280{
281281int offsetindex ;/* linp array index */
282282int itemoff ;/* page offset of item data */
283283Size alignedlen ;/* MAXALIGN(item data len) */
284+ ItemIdData olditemid ;/* used only in PageIndexMultiDelete */
284285}itemIdSortData ;
285286typedef itemIdSortData * itemIdSort ;
286287
@@ -297,7 +298,8 @@ itemoffcompare(const void *itemidp1, const void *itemidp2)
297298 *
298299 * Frees fragmented space on a page.
299300 * It doesn't remove unused line pointers! Please don't change this.
300- * This routine is usable for heap pages only.
301+ *
302+ * This routine is usable for heap pages only, but see PageIndexMultiDelete.
301303 *
302304 * Returns number of unused line pointers on page.If "unused" is not NULL
303305 * then the unused[] array is filled with indexes of unused line pointers.
@@ -543,3 +545,135 @@ PageIndexTupleDelete(Page page, OffsetNumber offnum)
543545}
544546}
545547}
548+
549+
550+ /*
551+ * PageIndexMultiDelete
552+ *
553+ * This routine handles the case of deleting multiple tuples from an
554+ * index page at once. It is considerably faster than a loop around
555+ * PageIndexTupleDelete ... however, the caller *must* supply the array
556+ * of item numbers to be deleted in item number order!
557+ */
558+ void
559+ PageIndexMultiDelete (Page page ,OffsetNumber * itemnos ,int nitems )
560+ {
561+ PageHeader phdr = (PageHeader )page ;
562+ Offset pd_lower = phdr -> pd_lower ;
563+ Offset pd_upper = phdr -> pd_upper ;
564+ Offset pd_special = phdr -> pd_special ;
565+ itemIdSort itemidbase ,
566+ itemidptr ;
567+ ItemId lp ;
568+ int nline ,
569+ nused ;
570+ int i ;
571+ Size totallen ;
572+ Offset upper ;
573+ Size size ;
574+ unsigned offset ;
575+ int nextitm ;
576+ OffsetNumber offnum ;
577+
578+ /*
579+ * If there aren't very many items to delete, then retail
580+ * PageIndexTupleDelete is the best way. Delete the items in reverse
581+ * order so we don't have to think about adjusting item numbers for
582+ * previous deletions.
583+ *
584+ * TODO: tune the magic number here
585+ */
586+ if (nitems <=2 )
587+ {
588+ while (-- nitems >=0 )
589+ PageIndexTupleDelete (page ,itemnos [nitems ]);
590+ return ;
591+ }
592+
593+ /*
594+ * As with PageRepairFragmentation, paranoia seems justified.
595+ */
596+ if (pd_lower < SizeOfPageHeaderData ||
597+ pd_lower > pd_upper ||
598+ pd_upper > pd_special ||
599+ pd_special > BLCKSZ ||
600+ pd_special != MAXALIGN (pd_special ))
601+ ereport (ERROR ,
602+ (errcode (ERRCODE_DATA_CORRUPTED ),
603+ errmsg ("corrupted page pointers: lower = %u, upper = %u, special = %u" ,
604+ pd_lower ,pd_upper ,pd_special )));
605+
606+ /*
607+ * Scan the item pointer array and build a list of just the ones we
608+ * are going to keep. Notice we do not modify the page yet, since
609+ * we are still validity-checking.
610+ */
611+ nline = PageGetMaxOffsetNumber (page );
612+ itemidbase = (itemIdSort )palloc (sizeof (itemIdSortData )* nline );
613+ itemidptr = itemidbase ;
614+ totallen = 0 ;
615+ nused = 0 ;
616+ nextitm = 0 ;
617+ for (offnum = 1 ;offnum <=nline ;offnum ++ )
618+ {
619+ lp = PageGetItemId (page ,offnum );
620+ size = ItemIdGetLength (lp );
621+ offset = ItemIdGetOffset (lp );
622+ if (offset < pd_upper ||
623+ (offset + size )> pd_special ||
624+ offset != MAXALIGN (offset ))
625+ ereport (ERROR ,
626+ (errcode (ERRCODE_DATA_CORRUPTED ),
627+ errmsg ("corrupted item pointer: offset = %u, size = %u" ,
628+ offset , (unsignedint )size )));
629+
630+ if (nextitm < nitems && offnum == itemnos [nextitm ])
631+ {
632+ /* skip item to be deleted */
633+ nextitm ++ ;
634+ }
635+ else
636+ {
637+ itemidptr -> offsetindex = nused ;/* where it will go */
638+ itemidptr -> itemoff = offset ;
639+ itemidptr -> olditemid = * lp ;
640+ itemidptr -> alignedlen = MAXALIGN (size );
641+ totallen += itemidptr -> alignedlen ;
642+ itemidptr ++ ;
643+ nused ++ ;
644+ }
645+ }
646+
647+ /* this will catch invalid or out-of-order itemnos[] */
648+ if (nextitm != nitems )
649+ elog (ERROR ,"incorrect index offsets supplied" );
650+
651+ if (totallen > (Size ) (pd_special - pd_lower ))
652+ ereport (ERROR ,
653+ (errcode (ERRCODE_DATA_CORRUPTED ),
654+ errmsg ("corrupted item lengths: total %u, available space %u" ,
655+ (unsignedint )totallen ,pd_special - pd_lower )));
656+
657+ /* sort itemIdSortData array into decreasing itemoff order */
658+ qsort ((char * )itemidbase ,nused ,sizeof (itemIdSortData ),
659+ itemoffcompare );
660+
661+ /* compactify page and install new itemids */
662+ upper = pd_special ;
663+
664+ for (i = 0 ,itemidptr = itemidbase ;i < nused ;i ++ ,itemidptr ++ )
665+ {
666+ lp = PageGetItemId (page ,itemidptr -> offsetindex + 1 );
667+ upper -= itemidptr -> alignedlen ;
668+ memmove ((char * )page + upper ,
669+ (char * )page + itemidptr -> itemoff ,
670+ itemidptr -> alignedlen );
671+ * lp = itemidptr -> olditemid ;
672+ lp -> lp_off = upper ;
673+ }
674+
675+ phdr -> pd_lower = SizeOfPageHeaderData + nused * sizeof (ItemIdData );
676+ phdr -> pd_upper = upper ;
677+
678+ pfree (itemidbase );
679+ }