2006-05-02 13:28:56 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* gindatapage.c
|
2014-01-22 17:51:48 +01:00
|
|
|
* routines for handling GIN posting tree pages.
|
2006-05-02 13:28:56 +02:00
|
|
|
*
|
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2006-05-02 13:28:56 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/access/gin/gindatapage.c
|
2006-05-02 13:28:56 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
#include "access/gin_private.h"
|
2017-02-14 21:37:59 +01:00
|
|
|
#include "access/ginxlog.h"
|
2014-11-06 12:52:08 +01:00
|
|
|
#include "access/xloginsert.h"
|
2014-01-22 17:51:48 +01:00
|
|
|
#include "lib/ilist.h"
|
2013-11-06 09:31:38 +01:00
|
|
|
#include "miscadmin.h"
|
2018-03-30 13:23:17 +02:00
|
|
|
#include "storage/predicate.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "utils/rel.h"
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
/*
|
2014-03-31 14:15:19 +02:00
|
|
|
* Min, Max and Target size of posting lists stored on leaf pages, in bytes.
|
|
|
|
*
|
|
|
|
* The code can deal with any size, but random access is more efficient when
|
|
|
|
* a number of smaller lists are stored, rather than one big list. If a
|
|
|
|
* posting list would become larger than Max size as a result of insertions,
|
|
|
|
* it is split into two. If a posting list would be smaller than minimum
|
|
|
|
* size, it is merged with the next posting list.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
2014-03-31 14:15:19 +02:00
|
|
|
#define GinPostingListSegmentMaxSize 384
|
|
|
|
#define GinPostingListSegmentTargetSize 256
|
|
|
|
#define GinPostingListSegmentMinSize 128
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At least this many items fit in a GinPostingListSegmentMaxSize-bytes
|
|
|
|
* long segment. This is used when estimating how much space is required
|
|
|
|
* for N items, at minimum.
|
|
|
|
*/
|
|
|
|
#define MinTuplesPerSegment ((GinPostingListSegmentMaxSize - 2) / 6)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A working struct for manipulating a posting tree leaf page.
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
dlist_head segments; /* a list of leafSegmentInfos */
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* The following fields represent how the segments are split across pages,
|
|
|
|
* if a page split is required. Filled in by leafRepackItems.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
dlist_node *lastleft; /* last segment on left page */
|
|
|
|
int lsize; /* total size on left page */
|
|
|
|
int rsize; /* total size on right page */
|
2014-03-31 14:15:19 +02:00
|
|
|
|
|
|
|
bool oldformat; /* page is in pre-9.4 format on disk */
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we need WAL data representing the reconstructed leaf page, it's
|
|
|
|
* stored here by computeLeafRecompressWALData.
|
|
|
|
*/
|
|
|
|
char *walinfo; /* buffer start */
|
|
|
|
int walinfolen; /* and length */
|
2014-01-22 17:51:48 +01:00
|
|
|
} disassembledLeaf;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
2014-05-06 18:12:18 +02:00
|
|
|
dlist_node node; /* linked list pointers */
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*-------------
|
|
|
|
* 'action' indicates the status of this in-memory segment, compared to
|
|
|
|
* what's on disk. It is one of the GIN_SEGMENT_* action codes:
|
|
|
|
*
|
|
|
|
* UNMODIFIED no changes
|
|
|
|
* DELETE the segment is to be removed. 'seg' and 'items' are
|
|
|
|
* ignored
|
|
|
|
* INSERT this is a completely new segment
|
|
|
|
* REPLACE this replaces an existing segment with new content
|
|
|
|
* ADDITEMS like REPLACE, but no items have been removed, and we track
|
|
|
|
* in detail what items have been added to this segment, in
|
|
|
|
* 'modifieditems'
|
|
|
|
*-------------
|
|
|
|
*/
|
|
|
|
char action;
|
|
|
|
|
|
|
|
ItemPointerData *modifieditems;
|
2016-09-03 19:28:53 +02:00
|
|
|
uint16 nmodifieditems;
|
2014-03-31 14:15:19 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* The following fields represent the items in this segment. If 'items' is
|
|
|
|
* not NULL, it contains a palloc'd array of the itemsin this segment. If
|
|
|
|
* 'seg' is not NULL, it contains the items in an already-compressed
|
2014-03-31 14:15:19 +02:00
|
|
|
* format. It can point to an on-disk page (!modified), or a palloc'd
|
|
|
|
* segment in memory. If both are set, they must represent the same items.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
GinPostingList *seg;
|
|
|
|
ItemPointer items;
|
|
|
|
int nitems; /* # of items in 'items', if items != NULL */
|
|
|
|
} leafSegmentInfo;
|
|
|
|
|
|
|
|
static ItemPointer dataLeafPageGetUncompressed(Page page, int *nitems);
|
|
|
|
static void dataSplitPageInternal(GinBtree btree, Buffer origbuf,
|
2019-05-22 19:04:48 +02:00
|
|
|
GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
|
|
|
Page *newlpage, Page *newrpage);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
static disassembledLeaf *disassembleLeaf(Page page);
|
|
|
|
static bool leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining);
|
2014-03-31 14:15:19 +02:00
|
|
|
static bool addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems,
|
2019-05-22 19:04:48 +02:00
|
|
|
int nNewItems);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
static void computeLeafRecompressWALData(disassembledLeaf *leaf);
|
2014-03-31 14:15:19 +02:00
|
|
|
static void dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf);
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
static void dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
|
2019-05-22 19:04:48 +02:00
|
|
|
ItemPointerData lbound, ItemPointerData rbound,
|
|
|
|
Page lpage, Page rpage);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/*
|
2014-01-29 17:23:17 +01:00
|
|
|
* Read TIDs from leaf data page to single uncompressed array. The TIDs are
|
|
|
|
* returned in ascending order.
|
|
|
|
*
|
|
|
|
* advancePast is a hint, indicating that the caller is only interested in
|
|
|
|
* TIDs > advancePast. To return all items, use ItemPointerSetMin.
|
|
|
|
*
|
|
|
|
* Note: This function can still return items smaller than advancePast that
|
|
|
|
* are in the same posting list as the items of interest, so the caller must
|
|
|
|
* still check all the returned items. But passing it allows this function to
|
|
|
|
* skip whole posting lists.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
ItemPointer
|
2014-01-29 17:23:17 +01:00
|
|
|
GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
|
|
|
ItemPointer result;
|
|
|
|
|
|
|
|
if (GinPageIsCompressed(page))
|
|
|
|
{
|
2014-01-29 17:23:17 +01:00
|
|
|
GinPostingList *seg = GinDataLeafPageGetPostingList(page);
|
2014-01-22 17:51:48 +01:00
|
|
|
Size len = GinDataLeafPageGetPostingListSize(page);
|
2014-01-29 17:23:17 +01:00
|
|
|
Pointer endptr = ((Pointer) seg) + len;
|
|
|
|
GinPostingList *next;
|
|
|
|
|
|
|
|
/* Skip to the segment containing advancePast+1 */
|
|
|
|
if (ItemPointerIsValid(&advancePast))
|
|
|
|
{
|
|
|
|
next = GinNextPostingListSegment(seg);
|
|
|
|
while ((Pointer) next < endptr &&
|
|
|
|
ginCompareItemPointers(&next->first, &advancePast) <= 0)
|
|
|
|
{
|
|
|
|
seg = next;
|
|
|
|
next = GinNextPostingListSegment(seg);
|
|
|
|
}
|
|
|
|
len = endptr - (Pointer) seg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
result = ginPostingListDecodeAllSegments(seg, len, nitems);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = NULL;
|
|
|
|
*nitems = 0;
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ItemPointer tmp = dataLeafPageGetUncompressed(page, nitems);
|
|
|
|
|
|
|
|
result = palloc((*nitems) * sizeof(ItemPointerData));
|
|
|
|
memcpy(result, tmp, (*nitems) * sizeof(ItemPointerData));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Places all TIDs from leaf data page to bitmap.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm)
|
|
|
|
{
|
|
|
|
ItemPointer uncompressed;
|
|
|
|
int nitems;
|
|
|
|
|
|
|
|
if (GinPageIsCompressed(page))
|
|
|
|
{
|
|
|
|
GinPostingList *segment = GinDataLeafPageGetPostingList(page);
|
|
|
|
Size len = GinDataLeafPageGetPostingListSize(page);
|
|
|
|
|
|
|
|
nitems = ginPostingListDecodeAllSegmentsToTbm(segment, len, tbm);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uncompressed = dataLeafPageGetUncompressed(page, &nitems);
|
|
|
|
|
|
|
|
if (nitems > 0)
|
|
|
|
tbm_add_tuples(tbm, uncompressed, nitems, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nitems;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get pointer to the uncompressed array of items on a pre-9.4 format
|
|
|
|
* uncompressed leaf page. The number of items in the array is returned in
|
|
|
|
* *nitems.
|
|
|
|
*/
|
|
|
|
static ItemPointer
|
|
|
|
dataLeafPageGetUncompressed(Page page, int *nitems)
|
|
|
|
{
|
|
|
|
ItemPointer items;
|
|
|
|
|
|
|
|
Assert(!GinPageIsCompressed(page));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the old pre-9.4 page format, the whole page content is used for
|
|
|
|
* uncompressed items, and the number of items is stored in 'maxoff'
|
|
|
|
*/
|
|
|
|
items = (ItemPointer) GinDataPageGetData(page);
|
|
|
|
*nitems = GinPageGetOpaque(page)->maxoff;
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we should follow the right link to find the item we're searching
|
|
|
|
* for.
|
|
|
|
*
|
|
|
|
* Compares inserting item pointer with the right bound of the current page.
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
|
|
|
static bool
|
2006-10-04 02:30:14 +02:00
|
|
|
dataIsMoveRight(GinBtree btree, Page page)
|
|
|
|
{
|
|
|
|
ItemPointer iptr = GinDataPageGetRightBound(page);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (GinPageRightMost(page))
|
2017-08-16 06:22:32 +02:00
|
|
|
return false;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2017-08-16 06:22:32 +02:00
|
|
|
return (ginCompareItemPointers(&btree->itemptr, iptr) > 0) ? true : false;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Find correct PostingItem in non-leaf page. It is assumed that this is
|
|
|
|
* the correct page, and the searched value SHOULD be on the page.
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
|
|
|
static BlockNumber
|
2006-10-04 02:30:14 +02:00
|
|
|
dataLocateItem(GinBtree btree, GinBtreeStack *stack)
|
|
|
|
{
|
|
|
|
OffsetNumber low,
|
|
|
|
high,
|
|
|
|
maxoff;
|
|
|
|
PostingItem *pitem = NULL;
|
|
|
|
int result;
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(stack->buffer);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
|
|
|
|
if (btree->fullScan)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
stack->off = FirstOffsetNumber;
|
|
|
|
stack->predictNumber *= GinPageGetOpaque(page)->maxoff;
|
2013-11-20 15:09:14 +01:00
|
|
|
return btree->getLeftMostChild(btree, page);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
low = FirstOffsetNumber;
|
2006-10-04 02:30:14 +02:00
|
|
|
maxoff = high = GinPageGetOpaque(page)->maxoff;
|
|
|
|
Assert(high >= low);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
high++;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
while (high > low)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
OffsetNumber mid = low + ((high - low) / 2);
|
|
|
|
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, mid);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
|
|
|
if (mid == maxoff)
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
2006-11-12 07:55:54 +01:00
|
|
|
* Right infinity, page already correctly chosen with a help of
|
2006-10-04 02:30:14 +02:00
|
|
|
* dataIsMoveRight
|
|
|
|
*/
|
2006-05-02 13:28:56 +02:00
|
|
|
result = -1;
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
else
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, mid);
|
2013-11-27 14:43:05 +01:00
|
|
|
result = ginCompareItemPointers(&btree->itemptr, &(pitem->key));
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (result == 0)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
stack->off = mid;
|
|
|
|
return PostingItemGetBlockNumber(pitem);
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
|
|
|
else if (result > 0)
|
2006-05-02 13:28:56 +02:00
|
|
|
low = mid + 1;
|
|
|
|
else
|
|
|
|
high = mid;
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Assert(high >= FirstOffsetNumber && high <= maxoff);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
stack->off = high;
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, high);
|
2006-05-02 13:28:56 +02:00
|
|
|
return PostingItemGetBlockNumber(pitem);
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Find link to blkno on non-leaf page, returns offset of PostingItem
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
|
|
|
static OffsetNumber
|
2006-10-04 02:30:14 +02:00
|
|
|
dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
|
|
|
|
{
|
|
|
|
OffsetNumber i,
|
|
|
|
maxoff = GinPageGetOpaque(page)->maxoff;
|
2006-05-02 13:28:56 +02:00
|
|
|
PostingItem *pitem;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
Assert(GinPageIsData(page));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
/* if page isn't changed, we return storedOff */
|
2006-10-04 02:30:14 +02:00
|
|
|
if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, storedOff);
|
2006-10-04 02:30:14 +02:00
|
|
|
if (PostingItemGetBlockNumber(pitem) == blkno)
|
2006-05-02 13:28:56 +02:00
|
|
|
return storedOff;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
|
|
|
* we hope, that needed pointer goes to right. It's true if there
|
|
|
|
* wasn't a deletion
|
|
|
|
*/
|
|
|
|
for (i = storedOff + 1; i <= maxoff; i++)
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, i);
|
2006-10-04 02:30:14 +02:00
|
|
|
if (PostingItemGetBlockNumber(pitem) == blkno)
|
2006-05-02 13:28:56 +02:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
maxoff = storedOff - 1;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* last chance */
|
2006-10-04 02:30:14 +02:00
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, i);
|
2006-10-04 02:30:14 +02:00
|
|
|
if (PostingItemGetBlockNumber(pitem) == blkno)
|
2006-05-02 13:28:56 +02:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return InvalidOffsetNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Return blkno of leftmost child
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
|
|
|
static BlockNumber
|
2006-10-04 02:30:14 +02:00
|
|
|
dataGetLeftMostPage(GinBtree btree, Page page)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
PostingItem *pitem;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
Assert(GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2013-10-03 10:47:17 +02:00
|
|
|
pitem = GinDataPageGetPostingItem(page, FirstOffsetNumber);
|
2006-05-02 13:28:56 +02:00
|
|
|
return PostingItemGetBlockNumber(pitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Add PostingItem to a non-leaf page.
|
2013-10-03 10:47:17 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset)
|
|
|
|
{
|
|
|
|
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
|
|
|
char *ptr;
|
|
|
|
|
2013-11-27 18:21:23 +01:00
|
|
|
Assert(PostingItemGetBlockNumber(data) != InvalidBlockNumber);
|
2013-10-03 10:47:17 +02:00
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (offset == InvalidOffsetNumber)
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
ptr = (char *) GinDataPageGetPostingItem(page, maxoff + 1);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
else
|
|
|
|
{
|
2013-10-03 10:47:17 +02:00
|
|
|
ptr = (char *) GinDataPageGetPostingItem(page, offset);
|
2014-01-22 17:51:48 +01:00
|
|
|
if (offset != maxoff + 1)
|
2013-10-03 10:47:17 +02:00
|
|
|
memmove(ptr + sizeof(PostingItem),
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
ptr,
|
2017-06-21 20:39:04 +02:00
|
|
|
(maxoff - offset + 1) * sizeof(PostingItem));
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
2013-10-03 10:47:17 +02:00
|
|
|
memcpy(ptr, data, sizeof(PostingItem));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-04-14 20:03:01 +02:00
|
|
|
maxoff++;
|
|
|
|
GinPageGetOpaque(page)->maxoff = maxoff;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also set pd_lower to the end of the posting items, to follow the
|
|
|
|
* "standard" page layout, so that we can squeeze out the unused space
|
|
|
|
* from full-page images.
|
|
|
|
*/
|
|
|
|
GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Delete posting item from non-leaf page
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
|
|
|
void
|
2010-10-18 03:43:26 +02:00
|
|
|
GinPageDeletePostingItem(Page page, OffsetNumber offset)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
|
|
|
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Assert(!GinPageIsLeaf(page));
|
|
|
|
Assert(offset >= FirstOffsetNumber && offset <= maxoff);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (offset != maxoff)
|
2013-10-03 10:47:17 +02:00
|
|
|
memmove(GinDataPageGetPostingItem(page, offset),
|
|
|
|
GinDataPageGetPostingItem(page, offset + 1),
|
2006-10-04 02:30:14 +02:00
|
|
|
sizeof(PostingItem) * (maxoff - offset));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-04-14 20:03:01 +02:00
|
|
|
maxoff--;
|
|
|
|
GinPageGetOpaque(page)->maxoff = maxoff;
|
|
|
|
|
|
|
|
GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Prepare to insert data on a leaf data page.
|
|
|
|
*
|
|
|
|
* If it will fit, return GPTP_INSERT after doing whatever setup is needed
|
|
|
|
* before we enter the insertion critical section. *ptp_workspace can be
|
|
|
|
* set to pass information along to the execPlaceToPage function.
|
|
|
|
*
|
|
|
|
* If it won't fit, perform a page split and return two temporary page
|
|
|
|
* images into *newlpage and *newrpage, with result GPTP_SPLIT.
|
|
|
|
*
|
|
|
|
* In neither case should the given page buffer be modified here.
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
2014-01-22 17:51:48 +01:00
|
|
|
static GinPlaceToPageRC
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata,
|
|
|
|
void **ptp_workspace,
|
|
|
|
Page *newlpage, Page *newrpage)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
GinBtreeDataLeafInsertData *items = insertdata;
|
|
|
|
ItemPointer newItems = &items->items[items->curitem];
|
|
|
|
int maxitems = items->nitem - items->curitem;
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(buf);
|
2014-01-22 17:51:48 +01:00
|
|
|
int i;
|
2014-05-06 18:12:18 +02:00
|
|
|
ItemPointerData rbound;
|
|
|
|
ItemPointerData lbound;
|
2014-01-22 17:51:48 +01:00
|
|
|
bool needsplit;
|
|
|
|
bool append;
|
|
|
|
int segsize;
|
|
|
|
Size freespace;
|
|
|
|
disassembledLeaf *leaf;
|
|
|
|
leafSegmentInfo *lastleftinfo;
|
|
|
|
ItemPointerData maxOldItem;
|
|
|
|
ItemPointerData remaining;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2014-05-06 18:12:18 +02:00
|
|
|
rbound = *GinDataPageGetRightBound(page);
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* Count how many of the new items belong to this page.
|
|
|
|
*/
|
|
|
|
if (!GinPageRightMost(page))
|
|
|
|
{
|
|
|
|
for (i = 0; i < maxitems; i++)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
if (ginCompareItemPointers(&newItems[i], &rbound) > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This needs to go to some other location in the tree. (The
|
2014-05-06 18:12:18 +02:00
|
|
|
* caller should've chosen the insert location so that at
|
|
|
|
* least the first item goes here.)
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
Assert(i > 0);
|
|
|
|
break;
|
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
maxitems = i;
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
2006-05-02 13:28:56 +02:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* Disassemble the data on the page */
|
2014-01-22 17:51:48 +01:00
|
|
|
leaf = disassembleLeaf(page);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* Are we appending to the end of the page? IOW, are all the new items
|
|
|
|
* larger than any of the existing items.
|
|
|
|
*/
|
|
|
|
if (!dlist_is_empty(&leaf->segments))
|
|
|
|
{
|
|
|
|
lastleftinfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
dlist_tail_node(&leaf->segments));
|
|
|
|
if (!lastleftinfo->items)
|
|
|
|
lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
|
|
|
|
&lastleftinfo->nitems);
|
|
|
|
maxOldItem = lastleftinfo->items[lastleftinfo->nitems - 1];
|
|
|
|
if (ginCompareItemPointers(&newItems[0], &maxOldItem) >= 0)
|
|
|
|
append = true;
|
|
|
|
else
|
|
|
|
append = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ItemPointerSetMin(&maxOldItem);
|
|
|
|
append = true;
|
|
|
|
}
|
2013-11-06 09:31:38 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* If we're appending to the end of the page, we will append as many items
|
|
|
|
* as we can fit (after splitting), and stop when the pages becomes full.
|
|
|
|
* Otherwise we have to limit the number of new items to insert, because
|
|
|
|
* once we start packing we can't just stop when we run out of space,
|
|
|
|
* because we must make sure that all the old items still fit.
|
|
|
|
*/
|
|
|
|
if (GinPageIsCompressed(page))
|
|
|
|
freespace = GinDataLeafPageGetFreeSpace(page);
|
|
|
|
else
|
|
|
|
freespace = 0;
|
|
|
|
if (append)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Even when appending, trying to append more items than will fit is
|
|
|
|
* not completely free, because we will merge the new items and old
|
|
|
|
* items into an array below. In the best case, every new item fits in
|
|
|
|
* a single byte, and we can use all the free space on the old page as
|
|
|
|
* well as the new page. For simplicity, ignore segment overhead etc.
|
|
|
|
*/
|
2014-04-14 20:03:01 +02:00
|
|
|
maxitems = Min(maxitems, freespace + GinDataPageMaxDataSize);
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Calculate a conservative estimate of how many new items we can fit
|
|
|
|
* on the two pages after splitting.
|
|
|
|
*
|
|
|
|
* We can use any remaining free space on the old page to store full
|
|
|
|
* segments, as well as the new page. Each full-sized segment can hold
|
|
|
|
* at least MinTuplesPerSegment items
|
|
|
|
*/
|
|
|
|
int nnewsegments;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
nnewsegments = freespace / GinPostingListSegmentMaxSize;
|
2014-04-14 20:03:01 +02:00
|
|
|
nnewsegments += GinDataPageMaxDataSize / GinPostingListSegmentMaxSize;
|
2014-01-22 17:51:48 +01:00
|
|
|
maxitems = Min(maxitems, nnewsegments * MinTuplesPerSegment);
|
|
|
|
}
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* Add the new items to the segment list */
|
2014-01-22 17:51:48 +01:00
|
|
|
if (!addItemsToLeaf(leaf, newItems, maxitems))
|
2013-11-27 14:43:05 +01:00
|
|
|
{
|
2014-05-06 18:12:18 +02:00
|
|
|
/* all items were duplicates, we have nothing to do */
|
2014-01-22 17:51:48 +01:00
|
|
|
items->curitem += maxitems;
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
return GPTP_NO_WORK;
|
2013-11-27 14:43:05 +01:00
|
|
|
}
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* Pack the items back to compressed segments, ready for writing to disk.
|
|
|
|
*/
|
|
|
|
needsplit = leafRepackItems(leaf, &remaining);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Did all the new items fit?
|
|
|
|
*
|
|
|
|
* If we're appending, it's OK if they didn't. But as a sanity check,
|
|
|
|
* verify that all the old items fit.
|
|
|
|
*/
|
|
|
|
if (ItemPointerIsValid(&remaining))
|
|
|
|
{
|
|
|
|
if (!append || ItemPointerCompare(&maxOldItem, &remaining) >= 0)
|
|
|
|
elog(ERROR, "could not split GIN page; all old items didn't fit");
|
|
|
|
|
|
|
|
/* Count how many of the new items did fit. */
|
|
|
|
for (i = 0; i < maxitems; i++)
|
|
|
|
{
|
|
|
|
if (ginCompareItemPointers(&newItems[i], &remaining) >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == 0)
|
|
|
|
elog(ERROR, "could not split GIN page; no new items fit");
|
|
|
|
maxitems = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!needsplit)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Great, all the items fit on a single page. If needed, prepare data
|
|
|
|
* for a WAL record describing the changes we'll make.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
Generate less WAL during GiST, GIN and SP-GiST index build.
Instead of WAL-logging every modification during the build separately,
first build the index without any WAL-logging, and make a separate pass
through the index at the end, to write all pages to the WAL. This
significantly reduces the amount of WAL generated, and is usually also
faster, despite the extra I/O needed for the extra scan through the index.
WAL generated this way is also faster to replay.
For GiST, the LSN-NSN interlock makes this a little tricky. All pages must
be marked with a valid (i.e. non-zero) LSN, so that the parent-child
LSN-NSN interlock works correctly. We now use magic value 1 for that during
index build. Change the fake LSN counter to begin from 1000, so that 1 is
safely smaller than any real or fake LSN. 2 would've been enough for our
purposes, but let's reserve a bigger range, in case we need more special
values in the future.
Author: Anastasia Lubennikova, Andrey V. Lepikhov
Reviewed-by: Heikki Linnakangas, Dmitry Dolgov
2019-04-03 16:03:15 +02:00
|
|
|
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
computeLeafRecompressWALData(leaf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're ready to enter the critical section, but
|
|
|
|
* dataExecPlaceToPageLeaf will need access to the "leaf" data.
|
|
|
|
*/
|
|
|
|
*ptp_workspace = leaf;
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
if (append)
|
|
|
|
elog(DEBUG2, "appended %d new items to block %u; %d bytes (%d to go)",
|
|
|
|
maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
|
|
|
|
items->nitem - items->curitem - maxitems);
|
|
|
|
else
|
|
|
|
elog(DEBUG2, "inserted %d new items to block %u; %d bytes (%d to go)",
|
|
|
|
maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
|
|
|
|
items->nitem - items->curitem - maxitems);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Have to split.
|
2014-01-22 17:51:48 +01:00
|
|
|
*
|
2014-09-12 10:23:59 +02:00
|
|
|
* leafRepackItems already divided the segments between the left and
|
|
|
|
* the right page. It filled the left page as full as possible, and
|
|
|
|
* put the rest to the right page. When building a new index, that's
|
2014-01-22 17:51:48 +01:00
|
|
|
* good, because the table is scanned from beginning to end and there
|
|
|
|
* won't be any more insertions to the left page during the build.
|
|
|
|
* This packs the index as tight as possible. But otherwise, split
|
|
|
|
* 50/50, by moving segments from the left page to the right page
|
|
|
|
* until they're balanced.
|
|
|
|
*
|
|
|
|
* As a further heuristic, when appending items to the end of the
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* page, try to make the left page 75% full, on the assumption that
|
2014-09-12 10:23:59 +02:00
|
|
|
* subsequent insertions will probably also go to the end. This packs
|
|
|
|
* the index somewhat tighter when appending to a table, which is very
|
|
|
|
* common.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
if (!btree->isBuild)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
while (dlist_has_prev(&leaf->segments, leaf->lastleft))
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
|
|
|
|
|
2014-08-29 13:19:34 +02:00
|
|
|
/* ignore deleted segments */
|
|
|
|
if (lastleftinfo->action != GIN_SEGMENT_DELETE)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2014-08-29 13:19:34 +02:00
|
|
|
segsize = SizeOfGinPostingList(lastleftinfo->seg);
|
2014-09-12 10:23:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we check that the right page doesn't become
|
|
|
|
* more full than the left page even when appending. It's
|
|
|
|
* possible that we added enough items to make both pages
|
|
|
|
* more than 75% full.
|
|
|
|
*/
|
|
|
|
if ((leaf->lsize - segsize) - (leaf->rsize + segsize) < 0)
|
|
|
|
break;
|
2014-08-29 13:19:34 +02:00
|
|
|
if (append)
|
|
|
|
{
|
2014-09-12 10:23:59 +02:00
|
|
|
if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
|
2014-08-29 13:19:34 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
leaf->lsize -= segsize;
|
|
|
|
leaf->rsize += segsize;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
leaf->lastleft = dlist_prev_node(&leaf->segments, leaf->lastleft);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
2014-04-14 20:03:01 +02:00
|
|
|
Assert(leaf->lsize <= GinDataPageMaxDataSize);
|
|
|
|
Assert(leaf->rsize <= GinDataPageMaxDataSize);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the max item in the left page's last segment; it becomes the
|
|
|
|
* right bound of the page.
|
|
|
|
*/
|
|
|
|
lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
|
|
|
|
if (!lastleftinfo->items)
|
|
|
|
lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
|
|
|
|
&lastleftinfo->nitems);
|
|
|
|
lbound = lastleftinfo->items[lastleftinfo->nitems - 1];
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/*
|
|
|
|
* Now allocate a couple of temporary page images, and fill them.
|
|
|
|
*/
|
|
|
|
*newlpage = palloc(BLCKSZ);
|
|
|
|
*newrpage = palloc(BLCKSZ);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataPlaceToPageLeafSplit(leaf, lbound, rbound,
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
*newlpage, *newrpage);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
Assert(GinPageRightMost(page) ||
|
|
|
|
ginCompareItemPointers(GinDataPageGetRightBound(*newlpage),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
GinDataPageGetRightBound(*newrpage)) < 0);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
if (append)
|
|
|
|
elog(DEBUG2, "appended %d items to block %u; split %d/%d (%d to go)",
|
|
|
|
maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
|
|
|
|
items->nitem - items->curitem - maxitems);
|
|
|
|
else
|
|
|
|
elog(DEBUG2, "inserted %d items to block %u; split %d/%d (%d to go)",
|
|
|
|
maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
|
|
|
|
items->nitem - items->curitem - maxitems);
|
|
|
|
}
|
|
|
|
|
|
|
|
items->curitem += maxitems;
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
return needsplit ? GPTP_SPLIT : GPTP_INSERT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform data insertion after beginPlaceToPage has decided it will fit.
|
|
|
|
*
|
|
|
|
* This is invoked within a critical section, and XLOG record creation (if
|
|
|
|
* needed) is already started. The target buffer is registered in slot 0.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dataExecPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata, void *ptp_workspace)
|
|
|
|
{
|
|
|
|
disassembledLeaf *leaf = (disassembledLeaf *) ptp_workspace;
|
|
|
|
|
|
|
|
/* Apply changes to page */
|
|
|
|
dataPlaceToPageLeafRecompress(buf, leaf);
|
|
|
|
|
|
|
|
/* If needed, register WAL data built by computeLeafRecompressWALData */
|
Generate less WAL during GiST, GIN and SP-GiST index build.
Instead of WAL-logging every modification during the build separately,
first build the index without any WAL-logging, and make a separate pass
through the index at the end, to write all pages to the WAL. This
significantly reduces the amount of WAL generated, and is usually also
faster, despite the extra I/O needed for the extra scan through the index.
WAL generated this way is also faster to replay.
For GiST, the LSN-NSN interlock makes this a little tricky. All pages must
be marked with a valid (i.e. non-zero) LSN, so that the parent-child
LSN-NSN interlock works correctly. We now use magic value 1 for that during
index build. Change the fake LSN counter to begin from 1000, so that 1 is
safely smaller than any real or fake LSN. 2 would've been enough for our
purposes, but let's reserve a bigger range, in case we need more special
values in the future.
Author: Anastasia Lubennikova, Andrey V. Lepikhov
Reviewed-by: Heikki Linnakangas, Dmitry Dolgov
2019-04-03 16:03:15 +02:00
|
|
|
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
{
|
|
|
|
XLogRegisterBufData(0, leaf->walinfo, leaf->walinfolen);
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Vacuum a posting tree leaf page.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
|
|
|
|
{
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(buffer);
|
2014-01-22 17:51:48 +01:00
|
|
|
disassembledLeaf *leaf;
|
|
|
|
bool removedsomething = false;
|
|
|
|
dlist_iter iter;
|
|
|
|
|
|
|
|
leaf = disassembleLeaf(page);
|
|
|
|
|
|
|
|
/* Vacuum each segment. */
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
|
|
|
|
int oldsegsize;
|
|
|
|
ItemPointer cleaned;
|
|
|
|
int ncleaned;
|
|
|
|
|
|
|
|
if (!seginfo->items)
|
|
|
|
seginfo->items = ginPostingListDecode(seginfo->seg,
|
|
|
|
&seginfo->nitems);
|
|
|
|
if (seginfo->seg)
|
|
|
|
oldsegsize = SizeOfGinPostingList(seginfo->seg);
|
2006-10-04 02:30:14 +02:00
|
|
|
else
|
2014-04-14 20:03:01 +02:00
|
|
|
oldsegsize = GinDataPageMaxDataSize;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
cleaned = ginVacuumItemPointers(gvs,
|
|
|
|
seginfo->items,
|
|
|
|
seginfo->nitems,
|
|
|
|
&ncleaned);
|
|
|
|
pfree(seginfo->items);
|
|
|
|
seginfo->items = NULL;
|
|
|
|
seginfo->nitems = 0;
|
|
|
|
if (cleaned)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
if (ncleaned > 0)
|
|
|
|
{
|
|
|
|
int npacked;
|
|
|
|
|
|
|
|
seginfo->seg = ginCompressPostingList(cleaned,
|
|
|
|
ncleaned,
|
|
|
|
oldsegsize,
|
|
|
|
&npacked);
|
|
|
|
/* Removing an item never increases the size of the segment */
|
|
|
|
if (npacked != ncleaned)
|
|
|
|
elog(ERROR, "could not fit vacuumed posting list");
|
2014-03-31 14:15:19 +02:00
|
|
|
seginfo->action = GIN_SEGMENT_REPLACE;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
seginfo->seg = NULL;
|
|
|
|
seginfo->items = NULL;
|
2014-03-31 14:15:19 +02:00
|
|
|
seginfo->action = GIN_SEGMENT_DELETE;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
seginfo->nitems = ncleaned;
|
|
|
|
|
|
|
|
removedsomething = true;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* If we removed any items, reconstruct the page from the pieces.
|
|
|
|
*
|
|
|
|
* We don't try to re-encode the segments here, even though some of them
|
2014-03-31 14:15:19 +02:00
|
|
|
* might be really small now that we've removed some items from them. It
|
|
|
|
* seems like a waste of effort, as there isn't really any benefit from
|
2014-05-06 18:12:18 +02:00
|
|
|
* larger segments per se; larger segments only help to pack more items in
|
|
|
|
* the same space. We might as well delay doing that until the next
|
2014-03-31 14:15:19 +02:00
|
|
|
* insertion, which will need to re-encode at least part of the page
|
2014-01-22 17:51:48 +01:00
|
|
|
* anyway.
|
|
|
|
*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Also note if the page was in uncompressed, pre-9.4 format before, it is
|
|
|
|
* now represented as one huge segment that contains all the items. It
|
|
|
|
* might make sense to split that, to speed up random access, but we don't
|
|
|
|
* bother. You'll have to REINDEX anyway if you want the full gain of the
|
|
|
|
* new tighter index format.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
if (removedsomething)
|
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
bool modified;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we have a palloc'd copy of all segments, after the first
|
|
|
|
* segment that is modified. (dataPlaceToPageLeafRecompress requires
|
|
|
|
* this).
|
|
|
|
*/
|
|
|
|
modified = false;
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
iter.cur);
|
2014-05-06 18:12:18 +02:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
|
|
|
|
modified = true;
|
|
|
|
if (modified && seginfo->action != GIN_SEGMENT_DELETE)
|
|
|
|
{
|
|
|
|
int segsize = SizeOfGinPostingList(seginfo->seg);
|
|
|
|
GinPostingList *tmp = (GinPostingList *) palloc(segsize);
|
2013-11-27 18:21:23 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
memcpy(tmp, seginfo->seg, segsize);
|
|
|
|
seginfo->seg = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RelationNeedsWAL(indexrel))
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
computeLeafRecompressWALData(leaf);
|
|
|
|
|
|
|
|
/* Apply changes to page */
|
2014-01-22 17:51:48 +01:00
|
|
|
START_CRIT_SECTION();
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
dataPlaceToPageLeafRecompress(buffer, leaf);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
|
|
|
|
if (RelationNeedsWAL(indexrel))
|
|
|
|
{
|
|
|
|
XLogRecPtr recptr;
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
XLogBeginInsert();
|
|
|
|
XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
|
|
|
|
XLogRegisterBufData(0, leaf->walinfo, leaf->walinfolen);
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_DATA_LEAF_PAGE);
|
2014-01-22 17:51:48 +01:00
|
|
|
PageSetLSN(page, recptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* Construct a ginxlogRecompressDataLeaf record representing the changes
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* in *leaf. (Because this requires a palloc, we have to do it before
|
|
|
|
* we enter the critical section that actually updates the page.)
|
2014-03-31 14:15:19 +02:00
|
|
|
*/
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
static void
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
computeLeafRecompressWALData(disassembledLeaf *leaf)
|
2014-03-31 14:15:19 +02:00
|
|
|
{
|
|
|
|
int nmodified = 0;
|
|
|
|
char *walbufbegin;
|
|
|
|
char *walbufend;
|
|
|
|
dlist_iter iter;
|
|
|
|
int segno;
|
|
|
|
ginxlogRecompressDataLeaf *recompress_xlog;
|
|
|
|
|
|
|
|
/* Count the modified segments */
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
iter.cur);
|
|
|
|
|
|
|
|
if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
|
|
|
|
nmodified++;
|
|
|
|
}
|
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
walbufbegin =
|
|
|
|
palloc(sizeof(ginxlogRecompressDataLeaf) +
|
|
|
|
BLCKSZ + /* max size needed to hold the segment data */
|
|
|
|
nmodified * 2 /* (segno + action) per action */
|
|
|
|
);
|
2014-03-31 14:15:19 +02:00
|
|
|
walbufend = walbufbegin;
|
|
|
|
|
|
|
|
recompress_xlog = (ginxlogRecompressDataLeaf *) walbufend;
|
|
|
|
walbufend += sizeof(ginxlogRecompressDataLeaf);
|
|
|
|
|
|
|
|
recompress_xlog->nactions = nmodified;
|
|
|
|
|
|
|
|
segno = 0;
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
iter.cur);
|
|
|
|
int segsize = 0;
|
|
|
|
int datalen;
|
|
|
|
uint8 action = seginfo->action;
|
|
|
|
|
|
|
|
if (action == GIN_SEGMENT_UNMODIFIED)
|
|
|
|
{
|
|
|
|
segno++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action != GIN_SEGMENT_DELETE)
|
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If storing the uncompressed list of added item pointers would take
|
|
|
|
* more space than storing the compressed segment as is, do that
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
if (action == GIN_SEGMENT_ADDITEMS &&
|
|
|
|
seginfo->nmodifieditems * sizeof(ItemPointerData) > segsize)
|
|
|
|
{
|
|
|
|
action = GIN_SEGMENT_REPLACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*((uint8 *) (walbufend++)) = segno;
|
|
|
|
*(walbufend++) = action;
|
|
|
|
|
|
|
|
switch (action)
|
|
|
|
{
|
|
|
|
case GIN_SEGMENT_DELETE:
|
|
|
|
datalen = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIN_SEGMENT_ADDITEMS:
|
|
|
|
datalen = seginfo->nmodifieditems * sizeof(ItemPointerData);
|
|
|
|
memcpy(walbufend, &seginfo->nmodifieditems, sizeof(uint16));
|
|
|
|
memcpy(walbufend + sizeof(uint16), seginfo->modifieditems, datalen);
|
|
|
|
datalen += sizeof(uint16);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIN_SEGMENT_INSERT:
|
|
|
|
case GIN_SEGMENT_REPLACE:
|
|
|
|
datalen = SHORTALIGN(segsize);
|
|
|
|
memcpy(walbufend, seginfo->seg, segsize);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unexpected GIN leaf action %d", action);
|
|
|
|
}
|
|
|
|
walbufend += datalen;
|
|
|
|
|
|
|
|
if (action != GIN_SEGMENT_INSERT)
|
|
|
|
segno++;
|
|
|
|
}
|
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* Pass back the constructed info via *leaf */
|
|
|
|
leaf->walinfo = walbufbegin;
|
|
|
|
leaf->walinfolen = walbufend - walbufbegin;
|
2014-03-31 14:15:19 +02:00
|
|
|
}
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* Assemble a disassembled posting tree leaf page back to a buffer.
|
|
|
|
*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* This just updates the target buffer; WAL stuff is caller's responsibility.
|
2014-01-24 09:42:38 +01:00
|
|
|
*
|
2014-03-31 14:15:19 +02:00
|
|
|
* NOTE: The segment pointers must not point directly to the same buffer,
|
|
|
|
* except for segments that have not been modified and whose preceding
|
|
|
|
* segments have not been modified either.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
static void
|
2014-03-31 14:15:19 +02:00
|
|
|
dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(buf);
|
2014-01-22 17:51:48 +01:00
|
|
|
char *ptr;
|
|
|
|
int newsize;
|
|
|
|
bool modified = false;
|
|
|
|
dlist_iter iter;
|
2014-03-31 14:15:19 +02:00
|
|
|
int segsize;
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* If the page was in pre-9.4 format before, convert the header, and force
|
|
|
|
* all segments to be copied to the page whether they were modified or
|
|
|
|
* not.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
2014-03-31 14:15:19 +02:00
|
|
|
if (!GinPageIsCompressed(page))
|
|
|
|
{
|
|
|
|
Assert(leaf->oldformat);
|
|
|
|
GinPageSetCompressed(page);
|
|
|
|
GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
|
|
|
|
modified = true;
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
ptr = (char *) GinDataLeafPageGetPostingList(page);
|
|
|
|
newsize = 0;
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
2013-11-27 14:43:05 +01:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
|
2014-01-22 17:51:48 +01:00
|
|
|
modified = true;
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action != GIN_SEGMENT_DELETE)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (modified)
|
|
|
|
memcpy(ptr, seginfo->seg, segsize);
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
ptr += segsize;
|
|
|
|
newsize += segsize;
|
2014-01-24 09:42:38 +01:00
|
|
|
}
|
2013-11-27 14:43:05 +01:00
|
|
|
}
|
2014-03-31 14:15:19 +02:00
|
|
|
|
2014-04-14 20:03:01 +02:00
|
|
|
Assert(newsize <= GinDataPageMaxDataSize);
|
|
|
|
GinDataPageSetDataSize(page, newsize);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-01-22 17:51:48 +01:00
|
|
|
* Like dataPlaceToPageLeafRecompress, but writes the disassembled leaf
|
|
|
|
* segments to two pages instead of one.
|
|
|
|
*
|
|
|
|
* This is different from the non-split cases in that this does not modify
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* the original page directly, but writes to temporary in-memory copies of
|
|
|
|
* the new left and right pages.
|
2006-05-02 13:28:56 +02:00
|
|
|
*/
|
2014-01-22 17:51:48 +01:00
|
|
|
static void
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
|
2014-01-22 17:51:48 +01:00
|
|
|
ItemPointerData lbound, ItemPointerData rbound,
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
Page lpage, Page rpage)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
|
|
|
char *ptr;
|
2014-01-22 17:51:48 +01:00
|
|
|
int segsize;
|
|
|
|
int lsize;
|
|
|
|
int rsize;
|
|
|
|
dlist_node *node;
|
|
|
|
dlist_node *firstright;
|
|
|
|
leafSegmentInfo *seginfo;
|
2014-05-06 18:12:18 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/* Initialize temporary pages to hold the new left and right pages */
|
|
|
|
GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
|
|
|
|
GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* Copy the segments that go to the left page.
|
|
|
|
*
|
|
|
|
* XXX: We should skip copying the unmodified part of the left page, like
|
|
|
|
* we do when recompressing.
|
|
|
|
*/
|
|
|
|
lsize = 0;
|
|
|
|
ptr = (char *) GinDataLeafPageGetPostingList(lpage);
|
|
|
|
firstright = dlist_next_node(&leaf->segments, leaf->lastleft);
|
|
|
|
for (node = dlist_head_node(&leaf->segments);
|
|
|
|
node != firstright;
|
|
|
|
node = dlist_next_node(&leaf->segments, node))
|
2013-11-27 14:43:05 +01:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
seginfo = dlist_container(leafSegmentInfo, node, node);
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action != GIN_SEGMENT_DELETE)
|
|
|
|
{
|
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
|
|
|
memcpy(ptr, seginfo->seg, segsize);
|
|
|
|
ptr += segsize;
|
|
|
|
lsize += segsize;
|
|
|
|
}
|
2013-11-27 14:43:05 +01:00
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
Assert(lsize == leaf->lsize);
|
2014-04-14 20:03:01 +02:00
|
|
|
GinDataPageSetDataSize(lpage, lsize);
|
2014-01-22 17:51:48 +01:00
|
|
|
*GinDataPageGetRightBound(lpage) = lbound;
|
|
|
|
|
|
|
|
/* Copy the segments that go to the right page */
|
|
|
|
ptr = (char *) GinDataLeafPageGetPostingList(rpage);
|
|
|
|
rsize = 0;
|
|
|
|
for (node = firstright;
|
|
|
|
;
|
|
|
|
node = dlist_next_node(&leaf->segments, node))
|
2013-10-03 10:47:17 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
seginfo = dlist_container(leafSegmentInfo, node, node);
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action != GIN_SEGMENT_DELETE)
|
|
|
|
{
|
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
|
|
|
memcpy(ptr, seginfo->seg, segsize);
|
|
|
|
ptr += segsize;
|
|
|
|
rsize += segsize;
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
if (!dlist_has_next(&leaf->segments, node))
|
|
|
|
break;
|
2013-10-03 10:47:17 +02:00
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
Assert(rsize == leaf->rsize);
|
2014-04-14 20:03:01 +02:00
|
|
|
GinDataPageSetDataSize(rpage, rsize);
|
2014-01-22 17:51:48 +01:00
|
|
|
*GinDataPageGetRightBound(rpage) = rbound;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Prepare to insert data on an internal data page.
|
|
|
|
*
|
|
|
|
* If it will fit, return GPTP_INSERT after doing whatever setup is needed
|
|
|
|
* before we enter the insertion critical section. *ptp_workspace can be
|
|
|
|
* set to pass information along to the execPlaceToPage function.
|
2014-01-22 17:51:48 +01:00
|
|
|
*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* If it won't fit, perform a page split and return two temporary page
|
|
|
|
* images into *newlpage and *newrpage, with result GPTP_SPLIT.
|
2014-01-22 17:51:48 +01:00
|
|
|
*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* In neither case should the given page buffer be modified here.
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Note: on insertion to an internal node, in addition to inserting the given
|
|
|
|
* item, the downlink of the existing item at stack->off will be updated to
|
|
|
|
* point to updateblkno.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
static GinPlaceToPageRC
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataBeginPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
|
|
|
void **ptp_workspace,
|
|
|
|
Page *newlpage, Page *newrpage)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(buf);
|
2014-05-06 18:12:18 +02:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* If it doesn't fit, deal with split case */
|
2014-01-22 17:51:48 +01:00
|
|
|
if (GinNonLeafDataPageGetFreeSpace(page) < sizeof(PostingItem))
|
2013-10-03 10:47:17 +02:00
|
|
|
{
|
2014-01-22 17:51:48 +01:00
|
|
|
dataSplitPageInternal(btree, buf, stack, insertdata, updateblkno,
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
newlpage, newrpage);
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
return GPTP_SPLIT;
|
2013-10-03 10:47:17 +02:00
|
|
|
}
|
2006-05-02 13:28:56 +02:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* Else, we're ready to proceed with insertion */
|
|
|
|
return GPTP_INSERT;
|
|
|
|
}
|
2013-11-27 14:43:05 +01:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/*
|
|
|
|
* Perform data insertion after beginPlaceToPage has decided it will fit.
|
|
|
|
*
|
|
|
|
* This is invoked within a critical section, and XLOG record creation (if
|
|
|
|
* needed) is already started. The target buffer is registered in slot 0.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dataExecPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
|
|
|
void *ptp_workspace)
|
|
|
|
{
|
|
|
|
Page page = BufferGetPage(buf);
|
|
|
|
OffsetNumber off = stack->off;
|
|
|
|
PostingItem *pitem;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/* Update existing downlink to point to next page (on internal page) */
|
|
|
|
pitem = GinDataPageGetPostingItem(page, off);
|
|
|
|
PostingItemSetBlockNumber(pitem, updateblkno);
|
|
|
|
|
|
|
|
/* Add new item */
|
|
|
|
pitem = (PostingItem *) insertdata;
|
|
|
|
GinDataPageAddPostingItem(page, pitem, off);
|
|
|
|
|
Generate less WAL during GiST, GIN and SP-GiST index build.
Instead of WAL-logging every modification during the build separately,
first build the index without any WAL-logging, and make a separate pass
through the index at the end, to write all pages to the WAL. This
significantly reduces the amount of WAL generated, and is usually also
faster, despite the extra I/O needed for the extra scan through the index.
WAL generated this way is also faster to replay.
For GiST, the LSN-NSN interlock makes this a little tricky. All pages must
be marked with a valid (i.e. non-zero) LSN, so that the parent-child
LSN-NSN interlock works correctly. We now use magic value 1 for that during
index build. Change the fake LSN counter to begin from 1000, so that 1 is
safely smaller than any real or fake LSN. 2 would've been enough for our
purposes, but let's reserve a bigger range, in case we need more special
values in the future.
Author: Anastasia Lubennikova, Andrey V. Lepikhov
Reviewed-by: Heikki Linnakangas, Dmitry Dolgov
2019-04-03 16:03:15 +02:00
|
|
|
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
{
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/*
|
|
|
|
* This must be static, because it has to survive until XLogInsert,
|
|
|
|
* and we can't palloc here. Ugly, but the XLogInsert infrastructure
|
|
|
|
* isn't reentrant anyway.
|
|
|
|
*/
|
|
|
|
static ginxlogInsertDataInternal data;
|
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
data.offset = off;
|
|
|
|
data.newitem = *pitem;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
XLogRegisterBufData(0, (char *) &data,
|
|
|
|
sizeof(ginxlogInsertDataInternal));
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Prepare to insert data on a posting-tree data page.
|
|
|
|
*
|
|
|
|
* If it will fit, return GPTP_INSERT after doing whatever setup is needed
|
|
|
|
* before we enter the insertion critical section. *ptp_workspace can be
|
|
|
|
* set to pass information along to the execPlaceToPage function.
|
|
|
|
*
|
|
|
|
* If it won't fit, perform a page split and return two temporary page
|
|
|
|
* images into *newlpage and *newrpage, with result GPTP_SPLIT.
|
|
|
|
*
|
|
|
|
* In neither case should the given page buffer be modified here.
|
|
|
|
*
|
|
|
|
* Note: on insertion to an internal node, in addition to inserting the given
|
|
|
|
* item, the downlink of the existing item at stack->off will be updated to
|
|
|
|
* point to updateblkno.
|
|
|
|
*
|
|
|
|
* Calls relevant function for internal or leaf page because they are handled
|
|
|
|
* very differently.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
static GinPlaceToPageRC
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
|
|
|
void **ptp_workspace,
|
|
|
|
Page *newlpage, Page *newrpage)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2016-04-20 15:31:19 +02:00
|
|
|
Page page = BufferGetPage(buf);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
Assert(GinPageIsData(page));
|
|
|
|
|
|
|
|
if (GinPageIsLeaf(page))
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
return dataBeginPlaceToPageLeaf(btree, buf, stack, insertdata,
|
|
|
|
ptp_workspace,
|
|
|
|
newlpage, newrpage);
|
|
|
|
else
|
|
|
|
return dataBeginPlaceToPageInternal(btree, buf, stack,
|
|
|
|
insertdata, updateblkno,
|
|
|
|
ptp_workspace,
|
|
|
|
newlpage, newrpage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform data insertion after beginPlaceToPage has decided it will fit.
|
|
|
|
*
|
|
|
|
* This is invoked within a critical section, and XLOG record creation (if
|
|
|
|
* needed) is already started. The target buffer is registered in slot 0.
|
|
|
|
*
|
|
|
|
* Calls relevant function for internal or leaf page because they are handled
|
|
|
|
* very differently.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dataExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
|
|
|
void *ptp_workspace)
|
|
|
|
{
|
|
|
|
Page page = BufferGetPage(buf);
|
|
|
|
|
|
|
|
if (GinPageIsLeaf(page))
|
|
|
|
dataExecPlaceToPageLeaf(btree, buf, stack, insertdata,
|
|
|
|
ptp_workspace);
|
2006-10-04 02:30:14 +02:00
|
|
|
else
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
dataExecPlaceToPageInternal(btree, buf, stack, insertdata,
|
|
|
|
updateblkno, ptp_workspace);
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
2013-11-27 14:43:05 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
* Split internal page and insert new data.
|
|
|
|
*
|
|
|
|
* Returns new temp pages to *newlpage and *newrpage.
|
|
|
|
* The original buffer is left untouched.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dataSplitPageInternal(GinBtree btree, Buffer origbuf,
|
|
|
|
GinBtreeStack *stack,
|
|
|
|
void *insertdata, BlockNumber updateblkno,
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
Page *newlpage, Page *newrpage)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2016-04-20 15:31:19 +02:00
|
|
|
Page oldpage = BufferGetPage(origbuf);
|
2014-01-22 17:51:48 +01:00
|
|
|
OffsetNumber off = stack->off;
|
|
|
|
int nitems = GinPageGetOpaque(oldpage)->maxoff;
|
2014-04-14 20:03:01 +02:00
|
|
|
int nleftitems;
|
|
|
|
int nrightitems;
|
2014-01-22 17:51:48 +01:00
|
|
|
Size pageSize = PageGetPageSize(oldpage);
|
|
|
|
ItemPointerData oldbound = *GinDataPageGetRightBound(oldpage);
|
2014-05-06 18:12:18 +02:00
|
|
|
ItemPointer bound;
|
2014-01-22 17:51:48 +01:00
|
|
|
Page lpage;
|
|
|
|
Page rpage;
|
|
|
|
OffsetNumber separator;
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
lpage = PageGetTempPage(oldpage);
|
|
|
|
rpage = PageGetTempPage(oldpage);
|
|
|
|
GinInitPage(lpage, GinPageGetOpaque(oldpage)->flags, pageSize);
|
|
|
|
GinInitPage(rpage, GinPageGetOpaque(oldpage)->flags, pageSize);
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* First construct a new list of PostingItems, which includes all the old
|
|
|
|
* items, and the new item.
|
2006-10-04 02:30:14 +02:00
|
|
|
*/
|
2014-01-22 17:51:48 +01:00
|
|
|
memcpy(allitems, GinDataPageGetPostingItem(oldpage, FirstOffsetNumber),
|
|
|
|
(off - 1) * sizeof(PostingItem));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
allitems[off - 1] = *((PostingItem *) insertdata);
|
|
|
|
memcpy(&allitems[off], GinDataPageGetPostingItem(oldpage, off),
|
|
|
|
(nitems - (off - 1)) * sizeof(PostingItem));
|
|
|
|
nitems++;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/* Update existing downlink to point to next page */
|
|
|
|
PostingItemSetBlockNumber(&allitems[off], updateblkno);
|
2013-10-03 10:47:17 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
|
|
|
* When creating a new index, fit as many tuples as possible on the left
|
|
|
|
* page, on the assumption that the table is scanned from beginning to
|
|
|
|
* end. This packs the index as tight as possible.
|
|
|
|
*/
|
|
|
|
if (btree->isBuild && GinPageRightMost(oldpage))
|
|
|
|
separator = GinNonLeafDataPageGetFreeSpace(rpage) / sizeof(PostingItem);
|
2013-10-03 10:47:17 +02:00
|
|
|
else
|
2014-01-22 17:51:48 +01:00
|
|
|
separator = nitems / 2;
|
2014-04-14 20:03:01 +02:00
|
|
|
nleftitems = separator;
|
|
|
|
nrightitems = nitems - separator;
|
2013-10-03 10:47:17 +02:00
|
|
|
|
2014-04-14 20:03:01 +02:00
|
|
|
memcpy(GinDataPageGetPostingItem(lpage, FirstOffsetNumber),
|
|
|
|
allitems,
|
|
|
|
nleftitems * sizeof(PostingItem));
|
|
|
|
GinPageGetOpaque(lpage)->maxoff = nleftitems;
|
2014-01-22 17:51:48 +01:00
|
|
|
memcpy(GinDataPageGetPostingItem(rpage, FirstOffsetNumber),
|
2014-04-14 20:03:01 +02:00
|
|
|
&allitems[separator],
|
|
|
|
nrightitems * sizeof(PostingItem));
|
|
|
|
GinPageGetOpaque(rpage)->maxoff = nrightitems;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also set pd_lower for both pages, like GinDataPageAddPostingItem does.
|
|
|
|
*/
|
|
|
|
GinDataPageSetDataSize(lpage, nleftitems * sizeof(PostingItem));
|
|
|
|
GinDataPageSetDataSize(rpage, nrightitems * sizeof(PostingItem));
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-05-02 13:28:56 +02:00
|
|
|
/* set up right bound for left page */
|
|
|
|
bound = GinDataPageGetRightBound(lpage);
|
2014-04-14 20:03:01 +02:00
|
|
|
*bound = GinDataPageGetPostingItem(lpage, nleftitems)->key;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
/* set up right bound for right page */
|
2014-01-22 17:51:48 +01:00
|
|
|
*GinDataPageGetRightBound(rpage) = oldbound;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
/* return temp pages to caller */
|
2014-01-22 17:51:48 +01:00
|
|
|
*newlpage = lpage;
|
|
|
|
*newrpage = rpage;
|
2013-11-20 15:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-11-27 14:43:05 +01:00
|
|
|
* Construct insertion payload for inserting the downlink for given buffer.
|
2013-11-20 15:57:41 +01:00
|
|
|
*/
|
2013-11-27 14:43:05 +01:00
|
|
|
static void *
|
2013-11-20 15:57:41 +01:00
|
|
|
dataPrepareDownlink(GinBtree btree, Buffer lbuf)
|
|
|
|
{
|
2013-11-27 14:43:05 +01:00
|
|
|
PostingItem *pitem = palloc(sizeof(PostingItem));
|
2016-04-20 15:31:19 +02:00
|
|
|
Page lpage = BufferGetPage(lbuf);
|
2013-11-20 15:57:41 +01:00
|
|
|
|
2013-11-27 14:43:05 +01:00
|
|
|
PostingItemSetBlockNumber(pitem, BufferGetBlockNumber(lbuf));
|
|
|
|
pitem->key = *GinDataPageGetRightBound(lpage);
|
|
|
|
|
|
|
|
return pitem;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Fills new root by right bound values from child.
|
2006-05-02 13:28:56 +02:00
|
|
|
* Also called from ginxlog, should not use btree
|
|
|
|
*/
|
|
|
|
void
|
2013-11-27 18:21:23 +01:00
|
|
|
ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
|
|
|
PostingItem li,
|
|
|
|
ri;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
li.key = *GinDataPageGetRightBound(lpage);
|
2013-11-27 18:21:23 +01:00
|
|
|
PostingItemSetBlockNumber(&li, lblkno);
|
|
|
|
GinDataPageAddPostingItem(root, &li, InvalidOffsetNumber);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
ri.key = *GinDataPageGetRightBound(rpage);
|
2013-11-27 18:21:23 +01:00
|
|
|
PostingItemSetBlockNumber(&ri, rblkno);
|
|
|
|
GinDataPageAddPostingItem(root, &ri, InvalidOffsetNumber);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/*** Functions to work with disassembled leaf pages ***/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disassemble page into a disassembledLeaf struct.
|
|
|
|
*/
|
|
|
|
static disassembledLeaf *
|
|
|
|
disassembleLeaf(Page page)
|
|
|
|
{
|
|
|
|
disassembledLeaf *leaf;
|
|
|
|
GinPostingList *seg;
|
|
|
|
Pointer segbegin;
|
|
|
|
Pointer segend;
|
|
|
|
|
|
|
|
leaf = palloc0(sizeof(disassembledLeaf));
|
|
|
|
dlist_init(&leaf->segments);
|
|
|
|
|
|
|
|
if (GinPageIsCompressed(page))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Create a leafSegment entry for each segment.
|
|
|
|
*/
|
|
|
|
seg = GinDataLeafPageGetPostingList(page);
|
|
|
|
segbegin = (Pointer) seg;
|
|
|
|
segend = segbegin + GinDataLeafPageGetPostingListSize(page);
|
|
|
|
while ((Pointer) seg < segend)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = palloc(sizeof(leafSegmentInfo));
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
seginfo->action = GIN_SEGMENT_UNMODIFIED;
|
2014-01-22 17:51:48 +01:00
|
|
|
seginfo->seg = seg;
|
|
|
|
seginfo->items = NULL;
|
|
|
|
seginfo->nitems = 0;
|
|
|
|
dlist_push_tail(&leaf->segments, &seginfo->node);
|
|
|
|
|
|
|
|
seg = GinNextPostingListSegment(seg);
|
|
|
|
}
|
2014-03-31 14:15:19 +02:00
|
|
|
leaf->oldformat = false;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* A pre-9.4 format uncompressed page is represented by a single
|
2018-07-19 20:04:17 +02:00
|
|
|
* segment, with an array of items. The corner case is uncompressed
|
|
|
|
* page containing no items, which is represented as no segments.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
ItemPointer uncompressed;
|
|
|
|
int nuncompressed;
|
|
|
|
leafSegmentInfo *seginfo;
|
|
|
|
|
|
|
|
uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed);
|
|
|
|
|
2018-07-19 20:04:17 +02:00
|
|
|
if (nuncompressed > 0)
|
|
|
|
{
|
|
|
|
seginfo = palloc(sizeof(leafSegmentInfo));
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2018-07-19 20:04:17 +02:00
|
|
|
seginfo->action = GIN_SEGMENT_REPLACE;
|
|
|
|
seginfo->seg = NULL;
|
|
|
|
seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
|
|
|
|
memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
|
|
|
|
seginfo->nitems = nuncompressed;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2018-07-19 20:04:17 +02:00
|
|
|
dlist_push_tail(&leaf->segments, &seginfo->node);
|
|
|
|
}
|
2014-03-31 14:15:19 +02:00
|
|
|
|
|
|
|
leaf->oldformat = true;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return leaf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Distribute newItems to the segments.
|
|
|
|
*
|
|
|
|
* Any segments that acquire new items are decoded, and the new items are
|
|
|
|
* merged with the old items.
|
|
|
|
*
|
|
|
|
* Returns true if any new items were added. False means they were all
|
|
|
|
* duplicates of existing items on the page.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems, int nNewItems)
|
|
|
|
{
|
|
|
|
dlist_iter iter;
|
|
|
|
ItemPointer nextnew = newItems;
|
|
|
|
int newleft = nNewItems;
|
|
|
|
bool modified = false;
|
2014-03-31 14:15:19 +02:00
|
|
|
leafSegmentInfo *newseg;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* If the page is completely empty, just construct one new segment to hold
|
|
|
|
* all the new items.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
if (dlist_is_empty(&leaf->segments))
|
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
newseg = palloc(sizeof(leafSegmentInfo));
|
|
|
|
newseg->seg = NULL;
|
|
|
|
newseg->items = newItems;
|
|
|
|
newseg->nitems = nNewItems;
|
|
|
|
newseg->action = GIN_SEGMENT_INSERT;
|
|
|
|
dlist_push_tail(&leaf->segments, &newseg->node);
|
2014-01-22 17:51:48 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
2014-05-06 18:12:18 +02:00
|
|
|
leafSegmentInfo *cur = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node, iter.cur);
|
2014-01-22 17:51:48 +01:00
|
|
|
int nthis;
|
2014-05-06 18:12:18 +02:00
|
|
|
ItemPointer tmpitems;
|
2014-01-22 17:51:48 +01:00
|
|
|
int ntmpitems;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* How many of the new items fall into this segment?
|
|
|
|
*/
|
|
|
|
if (!dlist_has_next(&leaf->segments, iter.cur))
|
|
|
|
nthis = newleft;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leafSegmentInfo *next;
|
|
|
|
ItemPointerData next_first;
|
|
|
|
|
|
|
|
next = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
dlist_next_node(&leaf->segments, iter.cur));
|
2014-01-22 17:51:48 +01:00
|
|
|
if (next->items)
|
|
|
|
next_first = next->items[0];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(next->seg != NULL);
|
|
|
|
next_first = next->seg->first;
|
|
|
|
}
|
|
|
|
|
|
|
|
nthis = 0;
|
|
|
|
while (nthis < newleft && ginCompareItemPointers(&nextnew[nthis], &next_first) < 0)
|
|
|
|
nthis++;
|
|
|
|
}
|
|
|
|
if (nthis == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Merge the new items with the existing items. */
|
|
|
|
if (!cur->items)
|
|
|
|
cur->items = ginPostingListDecode(cur->seg, &cur->nitems);
|
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* Fast path for the important special case that we're appending to
|
|
|
|
* the end of the page: don't let the last segment on the page grow
|
|
|
|
* larger than the target, create a new segment before that happens.
|
|
|
|
*/
|
|
|
|
if (!dlist_has_next(&leaf->segments, iter.cur) &&
|
|
|
|
ginCompareItemPointers(&cur->items[cur->nitems - 1], &nextnew[0]) < 0 &&
|
|
|
|
cur->seg != NULL &&
|
|
|
|
SizeOfGinPostingList(cur->seg) >= GinPostingListSegmentTargetSize)
|
|
|
|
{
|
|
|
|
newseg = palloc(sizeof(leafSegmentInfo));
|
|
|
|
newseg->seg = NULL;
|
|
|
|
newseg->items = nextnew;
|
|
|
|
newseg->nitems = nthis;
|
|
|
|
newseg->action = GIN_SEGMENT_INSERT;
|
|
|
|
dlist_push_tail(&leaf->segments, &newseg->node);
|
|
|
|
modified = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-24 17:40:26 +01:00
|
|
|
tmpitems = ginMergeItemPointers(cur->items, cur->nitems,
|
|
|
|
nextnew, nthis,
|
|
|
|
&ntmpitems);
|
2014-01-22 17:51:48 +01:00
|
|
|
if (ntmpitems != cur->nitems)
|
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* If there are no duplicates, track the added items so that we
|
|
|
|
* can emit a compact ADDITEMS WAL record later on. (it doesn't
|
|
|
|
* seem worth re-checking which items were duplicates, if there
|
|
|
|
* were any)
|
|
|
|
*/
|
|
|
|
if (ntmpitems == nthis + cur->nitems &&
|
|
|
|
cur->action == GIN_SEGMENT_UNMODIFIED)
|
|
|
|
{
|
|
|
|
cur->action = GIN_SEGMENT_ADDITEMS;
|
|
|
|
cur->modifieditems = nextnew;
|
|
|
|
cur->nmodifieditems = nthis;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cur->action = GIN_SEGMENT_REPLACE;
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
cur->items = tmpitems;
|
|
|
|
cur->nitems = ntmpitems;
|
|
|
|
cur->seg = NULL;
|
2014-03-31 14:15:19 +02:00
|
|
|
modified = true;
|
2014-01-22 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
nextnew += nthis;
|
|
|
|
newleft -= nthis;
|
|
|
|
if (newleft == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recompresses all segments that have been modified.
|
|
|
|
*
|
|
|
|
* If not all the items fit on two pages (ie. after split), we store as
|
|
|
|
* many items as fit, and set *remaining to the first item that didn't fit.
|
|
|
|
* If all items fit, *remaining is set to invalid.
|
|
|
|
*
|
|
|
|
* Returns true if the page has to be split.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining)
|
|
|
|
{
|
|
|
|
int pgused = 0;
|
2014-03-31 14:15:19 +02:00
|
|
|
bool needsplit = false;
|
|
|
|
dlist_iter iter;
|
|
|
|
int segsize;
|
|
|
|
leafSegmentInfo *nextseg;
|
|
|
|
int npacked;
|
|
|
|
bool modified;
|
|
|
|
dlist_node *cur_node;
|
|
|
|
dlist_node *next_node;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
|
|
|
ItemPointerSetInvalid(remaining);
|
|
|
|
|
|
|
|
/*
|
2014-03-31 14:15:19 +02:00
|
|
|
* cannot use dlist_foreach_modify here because we insert adjacent items
|
|
|
|
* while iterating.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
2014-03-31 14:15:19 +02:00
|
|
|
for (cur_node = dlist_head_node(&leaf->segments);
|
|
|
|
cur_node != NULL;
|
|
|
|
cur_node = next_node)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
cur_node);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (dlist_has_next(&leaf->segments, cur_node))
|
|
|
|
next_node = dlist_next_node(&leaf->segments, cur_node);
|
|
|
|
else
|
|
|
|
next_node = NULL;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/* Compress the posting list, if necessary */
|
|
|
|
if (seginfo->action != GIN_SEGMENT_DELETE)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->seg == NULL)
|
|
|
|
{
|
|
|
|
if (seginfo->nitems > GinPostingListSegmentMaxSize)
|
2014-05-06 18:12:18 +02:00
|
|
|
npacked = 0; /* no chance that it would fit. */
|
2014-03-31 14:15:19 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
seginfo->seg = ginCompressPostingList(seginfo->items,
|
|
|
|
seginfo->nitems,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
GinPostingListSegmentMaxSize,
|
2014-03-31 14:15:19 +02:00
|
|
|
&npacked);
|
|
|
|
}
|
|
|
|
if (npacked != seginfo->nitems)
|
|
|
|
{
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Too large. Compress again to the target size, and
|
|
|
|
* create a new segment to represent the remaining items.
|
|
|
|
* The new segment is inserted after this one, so it will
|
|
|
|
* be processed in the next iteration of this loop.
|
2014-03-31 14:15:19 +02:00
|
|
|
*/
|
|
|
|
if (seginfo->seg)
|
|
|
|
pfree(seginfo->seg);
|
|
|
|
seginfo->seg = ginCompressPostingList(seginfo->items,
|
|
|
|
seginfo->nitems,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
GinPostingListSegmentTargetSize,
|
2014-03-31 14:15:19 +02:00
|
|
|
&npacked);
|
|
|
|
if (seginfo->action != GIN_SEGMENT_INSERT)
|
|
|
|
seginfo->action = GIN_SEGMENT_REPLACE;
|
|
|
|
|
|
|
|
nextseg = palloc(sizeof(leafSegmentInfo));
|
|
|
|
nextseg->action = GIN_SEGMENT_INSERT;
|
|
|
|
nextseg->seg = NULL;
|
|
|
|
nextseg->items = &seginfo->items[npacked];
|
|
|
|
nextseg->nitems = seginfo->nitems - npacked;
|
|
|
|
next_node = &nextseg->node;
|
|
|
|
dlist_insert_after(cur_node, next_node);
|
|
|
|
}
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* If the segment is very small, merge it with the next segment.
|
|
|
|
*/
|
|
|
|
if (SizeOfGinPostingList(seginfo->seg) < GinPostingListSegmentMinSize && next_node)
|
|
|
|
{
|
2014-05-06 18:12:18 +02:00
|
|
|
int nmerged;
|
2014-03-31 14:15:19 +02:00
|
|
|
|
|
|
|
nextseg = dlist_container(leafSegmentInfo, node, next_node);
|
|
|
|
|
|
|
|
if (seginfo->items == NULL)
|
|
|
|
seginfo->items = ginPostingListDecode(seginfo->seg,
|
|
|
|
&seginfo->nitems);
|
|
|
|
if (nextseg->items == NULL)
|
|
|
|
nextseg->items = ginPostingListDecode(nextseg->seg,
|
|
|
|
&nextseg->nitems);
|
|
|
|
nextseg->items =
|
|
|
|
ginMergeItemPointers(seginfo->items, seginfo->nitems,
|
|
|
|
nextseg->items, nextseg->nitems,
|
|
|
|
&nmerged);
|
|
|
|
Assert(nmerged == seginfo->nitems + nextseg->nitems);
|
|
|
|
nextseg->nitems = nmerged;
|
|
|
|
nextseg->seg = NULL;
|
|
|
|
|
|
|
|
nextseg->action = GIN_SEGMENT_REPLACE;
|
|
|
|
nextseg->modifieditems = NULL;
|
|
|
|
nextseg->nmodifieditems = 0;
|
|
|
|
|
|
|
|
if (seginfo->action == GIN_SEGMENT_INSERT)
|
|
|
|
{
|
|
|
|
dlist_delete(cur_node);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
seginfo->action = GIN_SEGMENT_DELETE;
|
|
|
|
seginfo->seg = NULL;
|
|
|
|
}
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
seginfo->items = NULL;
|
|
|
|
seginfo->nitems = 0;
|
|
|
|
}
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
if (seginfo->action == GIN_SEGMENT_DELETE)
|
|
|
|
continue;
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* OK, we now have a compressed version of this segment ready for
|
|
|
|
* copying to the page. Did we exceed the size that fits on one page?
|
|
|
|
*/
|
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
2014-04-14 20:03:01 +02:00
|
|
|
if (pgused + segsize > GinDataPageMaxDataSize)
|
2014-01-22 17:51:48 +01:00
|
|
|
{
|
|
|
|
if (!needsplit)
|
|
|
|
{
|
|
|
|
/* switch to right page */
|
|
|
|
Assert(pgused > 0);
|
2014-03-31 14:15:19 +02:00
|
|
|
leaf->lastleft = dlist_prev_node(&leaf->segments, cur_node);
|
2014-01-22 17:51:48 +01:00
|
|
|
needsplit = true;
|
|
|
|
leaf->lsize = pgused;
|
|
|
|
pgused = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* Filled both pages. The last segment we constructed did not
|
|
|
|
* fit.
|
|
|
|
*/
|
|
|
|
*remaining = seginfo->seg->first;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remove all segments that did not fit from the list.
|
|
|
|
*/
|
|
|
|
while (dlist_has_next(&leaf->segments, cur_node))
|
|
|
|
dlist_delete(dlist_next_node(&leaf->segments, cur_node));
|
|
|
|
dlist_delete(cur_node);
|
2014-01-22 17:51:48 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pgused += segsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!needsplit)
|
|
|
|
{
|
|
|
|
leaf->lsize = pgused;
|
|
|
|
leaf->rsize = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
leaf->rsize = pgused;
|
|
|
|
|
2014-04-14 20:03:01 +02:00
|
|
|
Assert(leaf->lsize <= GinDataPageMaxDataSize);
|
|
|
|
Assert(leaf->rsize <= GinDataPageMaxDataSize);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
2014-03-31 14:15:19 +02:00
|
|
|
/*
|
|
|
|
* Make a palloc'd copy of every segment after the first modified one,
|
|
|
|
* because as we start copying items to the original page, we might
|
|
|
|
* overwrite an existing segment.
|
|
|
|
*/
|
|
|
|
modified = false;
|
|
|
|
dlist_foreach(iter, &leaf->segments)
|
|
|
|
{
|
|
|
|
leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
|
|
|
|
iter.cur);
|
|
|
|
|
|
|
|
if (!modified && seginfo->action != GIN_SEGMENT_UNMODIFIED)
|
|
|
|
{
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
else if (modified && seginfo->action == GIN_SEGMENT_UNMODIFIED)
|
|
|
|
{
|
|
|
|
GinPostingList *tmp;
|
|
|
|
|
|
|
|
segsize = SizeOfGinPostingList(seginfo->seg);
|
|
|
|
tmp = palloc(segsize);
|
|
|
|
memcpy(tmp, seginfo->seg, segsize);
|
|
|
|
seginfo->seg = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
return needsplit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** Functions that are exported to the rest of the GIN code ***/
|
|
|
|
|
2013-11-06 09:31:38 +01:00
|
|
|
/*
|
|
|
|
* Creates new posting tree containing the given TIDs. Returns the page
|
|
|
|
* number of the root of the new posting tree.
|
|
|
|
*
|
|
|
|
* items[] must be in sorted order with no duplicates.
|
|
|
|
*/
|
|
|
|
BlockNumber
|
|
|
|
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
|
2018-03-30 13:23:17 +02:00
|
|
|
GinStatsData *buildStats, Buffer entrybuffer)
|
2013-11-06 09:31:38 +01:00
|
|
|
{
|
|
|
|
BlockNumber blkno;
|
|
|
|
Buffer buffer;
|
2014-04-05 21:02:28 +02:00
|
|
|
Page tmppage;
|
2013-11-06 09:31:38 +01:00
|
|
|
Page page;
|
2014-01-22 17:51:48 +01:00
|
|
|
Pointer ptr;
|
2013-11-13 12:44:46 +01:00
|
|
|
int nrootitems;
|
2014-01-22 17:51:48 +01:00
|
|
|
int rootsize;
|
Generate less WAL during GiST, GIN and SP-GiST index build.
Instead of WAL-logging every modification during the build separately,
first build the index without any WAL-logging, and make a separate pass
through the index at the end, to write all pages to the WAL. This
significantly reduces the amount of WAL generated, and is usually also
faster, despite the extra I/O needed for the extra scan through the index.
WAL generated this way is also faster to replay.
For GiST, the LSN-NSN interlock makes this a little tricky. All pages must
be marked with a valid (i.e. non-zero) LSN, so that the parent-child
LSN-NSN interlock works correctly. We now use magic value 1 for that during
index build. Change the fake LSN counter to begin from 1000, so that 1 is
safely smaller than any real or fake LSN. 2 would've been enough for our
purposes, but let's reserve a bigger range, in case we need more special
values in the future.
Author: Anastasia Lubennikova, Andrey V. Lepikhov
Reviewed-by: Heikki Linnakangas, Dmitry Dolgov
2019-04-03 16:03:15 +02:00
|
|
|
bool is_build = (buildStats != NULL);
|
2013-11-06 09:31:38 +01:00
|
|
|
|
2014-04-05 21:02:28 +02:00
|
|
|
/* Construct the new root page in memory first. */
|
|
|
|
tmppage = (Page) palloc(BLCKSZ);
|
|
|
|
GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
|
|
|
|
GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
|
2013-11-06 09:31:38 +01:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Write as many of the items to the root page as fit. In segments of max
|
|
|
|
* GinPostingListSegmentMaxSize bytes each.
|
2014-01-22 17:51:48 +01:00
|
|
|
*/
|
|
|
|
nrootitems = 0;
|
|
|
|
rootsize = 0;
|
2014-04-05 21:02:28 +02:00
|
|
|
ptr = (Pointer) GinDataLeafPageGetPostingList(tmppage);
|
2014-01-22 17:51:48 +01:00
|
|
|
while (nrootitems < nitems)
|
|
|
|
{
|
|
|
|
GinPostingList *segment;
|
|
|
|
int npacked;
|
|
|
|
int segsize;
|
|
|
|
|
|
|
|
segment = ginCompressPostingList(&items[nrootitems],
|
|
|
|
nitems - nrootitems,
|
|
|
|
GinPostingListSegmentMaxSize,
|
|
|
|
&npacked);
|
|
|
|
segsize = SizeOfGinPostingList(segment);
|
2014-04-14 20:03:01 +02:00
|
|
|
if (rootsize + segsize > GinDataPageMaxDataSize)
|
2014-01-22 17:51:48 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy(ptr, segment, segsize);
|
|
|
|
ptr += segsize;
|
|
|
|
rootsize += segsize;
|
|
|
|
nrootitems += npacked;
|
|
|
|
pfree(segment);
|
|
|
|
}
|
2014-04-14 20:03:01 +02:00
|
|
|
GinDataPageSetDataSize(tmppage, rootsize);
|
2013-11-06 09:31:38 +01:00
|
|
|
|
2014-04-05 21:02:28 +02:00
|
|
|
/*
|
|
|
|
* All set. Get a new physical page, and copy the in-memory page to it.
|
|
|
|
*/
|
|
|
|
buffer = GinNewBuffer(index);
|
2016-04-20 15:31:19 +02:00
|
|
|
page = BufferGetPage(buffer);
|
2014-04-05 21:02:28 +02:00
|
|
|
blkno = BufferGetBlockNumber(buffer);
|
|
|
|
|
2018-03-30 13:23:17 +02:00
|
|
|
/*
|
Re-think predicate locking on GIN indexes.
The principle behind the locking was not very well thought-out, and not
documented. Add a section in the README to explain how it's supposed to
work, and change the code so that it actually works that way.
This fixes two bugs:
1. If fast update was turned on concurrently, subsequent inserts to the
pending list would not conflict with predicate locks that were acquired
earlier, on entry pages. The included 'predicate-gin-fastupdate' test
demonstrates that. To fix, make all scans acquire a predicate lock on
the metapage. That lock represents a scan of the pending list, whether
or not there is a pending list at the moment. Forget about the
optimization to skip locking/checking for locks, when fastupdate=off.
2. If a scan finds no match, it still needs to lock the entry page. The
point of predicate locks is to lock the gabs between values, whether
or not there is a match. The included 'predicate-gin-nomatch' test
tests that case.
In addition to those two bug fixes, this removes some unnecessary locking,
following the principle laid out in the README. Because all items in
a posting tree have the same key value, a lock on the posting tree root is
enough to cover all the items. (With a very large posting tree, it would
possibly be better to lock the posting tree leaf pages instead, so that a
"skip scan" with a query like "A & B", you could avoid unnecessary conflict
if a new tuple is inserted with A but !B. But let's keep this simple.)
Also, some spelling fixes.
Author: Heikki Linnakangas with some editorization by me
Review: Andrey Borodin, Alexander Korotkov
Discussion: https://www.postgresql.org/message-id/0b3ad2c2-2692-62a9-3a04-5724f2af9114@iki.fi
2018-05-04 10:27:50 +02:00
|
|
|
* Copy any predicate locks from the entry tree leaf (containing posting
|
|
|
|
* list) to the posting tree.
|
2018-03-30 13:23:17 +02:00
|
|
|
*/
|
|
|
|
PredicateLockPageSplit(index, BufferGetBlockNumber(entrybuffer), blkno);
|
|
|
|
|
2014-04-05 21:02:28 +02:00
|
|
|
START_CRIT_SECTION();
|
|
|
|
|
|
|
|
PageRestoreTempPage(tmppage, page);
|
|
|
|
MarkBufferDirty(buffer);
|
2014-01-22 17:51:48 +01:00
|
|
|
|
Generate less WAL during GiST, GIN and SP-GiST index build.
Instead of WAL-logging every modification during the build separately,
first build the index without any WAL-logging, and make a separate pass
through the index at the end, to write all pages to the WAL. This
significantly reduces the amount of WAL generated, and is usually also
faster, despite the extra I/O needed for the extra scan through the index.
WAL generated this way is also faster to replay.
For GiST, the LSN-NSN interlock makes this a little tricky. All pages must
be marked with a valid (i.e. non-zero) LSN, so that the parent-child
LSN-NSN interlock works correctly. We now use magic value 1 for that during
index build. Change the fake LSN counter to begin from 1000, so that 1 is
safely smaller than any real or fake LSN. 2 would've been enough for our
purposes, but let's reserve a bigger range, in case we need more special
values in the future.
Author: Anastasia Lubennikova, Andrey V. Lepikhov
Reviewed-by: Heikki Linnakangas, Dmitry Dolgov
2019-04-03 16:03:15 +02:00
|
|
|
if (RelationNeedsWAL(index) && !is_build)
|
2013-11-06 09:31:38 +01:00
|
|
|
{
|
|
|
|
XLogRecPtr recptr;
|
|
|
|
ginxlogCreatePostingTree data;
|
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
data.size = rootsize;
|
2013-11-06 09:31:38 +01:00
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
XLogBeginInsert();
|
|
|
|
XLogRegisterData((char *) &data, sizeof(ginxlogCreatePostingTree));
|
2013-11-06 09:31:38 +01:00
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
XLogRegisterData((char *) GinDataLeafPageGetPostingList(page),
|
|
|
|
rootsize);
|
|
|
|
XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
|
2013-11-06 09:31:38 +01:00
|
|
|
|
Revamp the WAL record format.
Each WAL record now carries information about the modified relation and
block(s) in a standardized format. That makes it easier to write tools that
need that information, like pg_rewind, prefetching the blocks to speed up
recovery, etc.
There's a whole new API for building WAL records, replacing the XLogRecData
chains used previously. The new API consists of XLogRegister* functions,
which are called for each buffer and chunk of data that is added to the
record. The new API also gives more control over when a full-page image is
written, by passing flags to the XLogRegisterBuffer function.
This also simplifies the XLogReadBufferForRedo() calls. The function can dig
the relation and block number from the WAL record, so they no longer need to
be passed as arguments.
For the convenience of redo routines, XLogReader now disects each WAL record
after reading it, copying the main data part and the per-block data into
MAXALIGNed buffers. The data chunks are not aligned within the WAL record,
but the redo routines can assume that the pointers returned by XLogRecGet*
functions are. Redo routines are now passed the XLogReaderState, which
contains the record in the already-disected format, instead of the plain
XLogRecord.
The new record format also makes the fixed size XLogRecord header smaller,
by removing the xl_len field. The length of the "main data" portion is now
stored at the end of the WAL record, and there's a separate header after
XLogRecord for it. The alignment padding at the end of XLogRecord is also
removed. This compansates for the fact that the new format would otherwise
be more bulky than the old format.
Reviewed by Andres Freund, Amit Kapila, Michael Paquier, Alvaro Herrera,
Fujii Masao.
2014-11-20 16:56:26 +01:00
|
|
|
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE);
|
2013-11-06 09:31:38 +01:00
|
|
|
PageSetLSN(page, recptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(buffer);
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
|
|
|
|
|
|
|
/* During index build, count the newly-added data page */
|
|
|
|
if (buildStats)
|
|
|
|
buildStats->nDataPages++;
|
|
|
|
|
2014-04-05 21:02:28 +02:00
|
|
|
elog(DEBUG2, "created GIN posting tree with %d items", nrootitems);
|
|
|
|
|
2013-11-06 09:31:38 +01:00
|
|
|
/*
|
|
|
|
* Add any remaining TIDs to the newly-created posting tree.
|
|
|
|
*/
|
2013-11-13 12:44:46 +01:00
|
|
|
if (nitems > nrootitems)
|
2013-11-06 09:31:38 +01:00
|
|
|
{
|
2013-11-20 15:09:14 +01:00
|
|
|
ginInsertItemPointers(index, blkno,
|
2013-11-13 12:44:46 +01:00
|
|
|
items + nrootitems,
|
|
|
|
nitems - nrootitems,
|
2013-11-06 09:31:38 +01:00
|
|
|
buildStats);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blkno;
|
|
|
|
}
|
|
|
|
|
Re-think predicate locking on GIN indexes.
The principle behind the locking was not very well thought-out, and not
documented. Add a section in the README to explain how it's supposed to
work, and change the code so that it actually works that way.
This fixes two bugs:
1. If fast update was turned on concurrently, subsequent inserts to the
pending list would not conflict with predicate locks that were acquired
earlier, on entry pages. The included 'predicate-gin-fastupdate' test
demonstrates that. To fix, make all scans acquire a predicate lock on
the metapage. That lock represents a scan of the pending list, whether
or not there is a pending list at the moment. Forget about the
optimization to skip locking/checking for locks, when fastupdate=off.
2. If a scan finds no match, it still needs to lock the entry page. The
point of predicate locks is to lock the gabs between values, whether
or not there is a match. The included 'predicate-gin-nomatch' test
tests that case.
In addition to those two bug fixes, this removes some unnecessary locking,
following the principle laid out in the README. Because all items in
a posting tree have the same key value, a lock on the posting tree root is
enough to cover all the items. (With a very large posting tree, it would
possibly be better to lock the posting tree leaf pages instead, so that a
"skip scan" with a query like "A & B", you could avoid unnecessary conflict
if a new tuple is inserted with A but !B. But let's keep this simple.)
Also, some spelling fixes.
Author: Heikki Linnakangas with some editorization by me
Review: Andrey Borodin, Alexander Korotkov
Discussion: https://www.postgresql.org/message-id/0b3ad2c2-2692-62a9-3a04-5724f2af9114@iki.fi
2018-05-04 10:27:50 +02:00
|
|
|
static void
|
2013-11-27 14:43:05 +01:00
|
|
|
ginPrepareDataScan(GinBtree btree, Relation index, BlockNumber rootBlkno)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
memset(btree, 0, sizeof(GinBtreeData));
|
2010-10-18 02:52:32 +02:00
|
|
|
|
2006-05-02 13:28:56 +02:00
|
|
|
btree->index = index;
|
2013-11-27 14:43:05 +01:00
|
|
|
btree->rootBlkno = rootBlkno;
|
2010-10-18 02:52:32 +02:00
|
|
|
|
2006-05-02 13:28:56 +02:00
|
|
|
btree->findChildPage = dataLocateItem;
|
2013-11-20 15:09:14 +01:00
|
|
|
btree->getLeftMostChild = dataGetLeftMostPage;
|
2010-10-18 02:52:32 +02:00
|
|
|
btree->isMoveRight = dataIsMoveRight;
|
2014-01-22 17:51:48 +01:00
|
|
|
btree->findItem = NULL;
|
2006-05-02 13:28:56 +02:00
|
|
|
btree->findChildPtr = dataFindChildPtr;
|
Fix memory leak and other bugs in ginPlaceToPage() & subroutines.
Commit 36a35c550ac114ca turned the interface between ginPlaceToPage and
its subroutines in gindatapage.c and ginentrypage.c into a royal mess:
page-update critical sections were started in one place and finished in
another place not even in the same file, and the very same subroutine
might return having started a critical section or not. Subsequent patches
band-aided over some of the problems with this design by making things
even messier.
One user-visible resulting problem is memory leaks caused by the need for
the subroutines to allocate storage that would survive until ginPlaceToPage
calls XLogInsert (as reported by Julien Rouhaud). This would not typically
be noticeable during retail index updates. It could be visible in a GIN
index build, in the form of memory consumption swelling to several times
the commanded maintenance_work_mem.
Another rather nasty problem is that in the internal-page-splitting code
path, we would clear the child page's GIN_INCOMPLETE_SPLIT flag well before
entering the critical section that it's supposed to be cleared in; a
failure in between would leave the index in a corrupt state. There were
also assorted coding-rule violations with little immediate consequence but
possible long-term hazards, such as beginning an XLogInsert sequence before
entering a critical section, or calling elog(DEBUG) inside a critical
section.
To fix, redefine the API between ginPlaceToPage() and its subroutines
by splitting the subroutines into two parts. The "beginPlaceToPage"
subroutine does what can be done outside a critical section, including
full computation of the result pages into temporary storage when we're
going to split the target page. The "execPlaceToPage" subroutine is called
within a critical section established by ginPlaceToPage(), and it handles
the actual page update in the non-split code path. The critical section,
as well as the XLOG insertion call sequence, are both now always started
and finished in ginPlaceToPage(). Also, make ginPlaceToPage() create and
work in a short-lived memory context to eliminate the leakage problem.
(Since a short-lived memory context had been getting created in the most
common code path in the subroutines, this shouldn't cause any noticeable
performance penalty; we're just moving the overhead up one call level.)
In passing, fix a bunch of comments that had gone unmaintained throughout
all this klugery.
Report: <571276DD.5050303@dalibo.com>
2016-04-20 20:25:15 +02:00
|
|
|
btree->beginPlaceToPage = dataBeginPlaceToPage;
|
|
|
|
btree->execPlaceToPage = dataExecPlaceToPage;
|
2010-10-18 03:43:26 +02:00
|
|
|
btree->fillRoot = ginDataFillRoot;
|
2013-11-20 15:57:41 +01:00
|
|
|
btree->prepareDownlink = dataPrepareDownlink;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2017-08-16 06:22:32 +02:00
|
|
|
btree->isData = true;
|
|
|
|
btree->fullScan = false;
|
|
|
|
btree->isBuild = false;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Inserts array of item pointers, may execute several tree scan (very rare)
|
|
|
|
*/
|
|
|
|
void
|
2013-11-20 15:09:14 +01:00
|
|
|
ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
|
Fix GIN to support null keys, empty and null items, and full index scans.
Per my recent proposal(s). Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue. This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans. A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.
Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.
Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.
This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators. The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.
Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.
Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend. I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
2011-01-08 01:16:24 +01:00
|
|
|
ItemPointerData *items, uint32 nitem,
|
|
|
|
GinStatsData *buildStats)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2013-11-20 15:09:14 +01:00
|
|
|
GinBtreeData btree;
|
2013-11-27 14:43:05 +01:00
|
|
|
GinBtreeDataLeafInsertData insertdata;
|
2013-11-20 15:09:14 +01:00
|
|
|
GinBtreeStack *stack;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2013-11-27 14:43:05 +01:00
|
|
|
ginPrepareDataScan(&btree, index, rootBlkno);
|
2013-11-20 15:09:14 +01:00
|
|
|
btree.isBuild = (buildStats != NULL);
|
2013-11-27 14:43:05 +01:00
|
|
|
insertdata.items = items;
|
|
|
|
insertdata.nitem = nitem;
|
|
|
|
insertdata.curitem = 0;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2013-11-27 14:43:05 +01:00
|
|
|
while (insertdata.curitem < insertdata.nitem)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2013-11-27 14:43:05 +01:00
|
|
|
/* search for the leaf page where the first item should go to */
|
|
|
|
btree.itemptr = insertdata.items[insertdata.curitem];
|
2018-12-27 02:10:51 +01:00
|
|
|
stack = ginFindLeafPage(&btree, false, true, NULL);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2014-01-22 17:51:48 +01:00
|
|
|
ginInsertValue(&btree, stack, &insertdata, buildStats);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-20 15:09:14 +01:00
|
|
|
/*
|
|
|
|
* Starts a new scan on a posting tree.
|
|
|
|
*/
|
|
|
|
GinBtreeStack *
|
2016-04-08 21:36:30 +02:00
|
|
|
ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno,
|
|
|
|
Snapshot snapshot)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2013-11-20 15:09:14 +01:00
|
|
|
GinBtreeStack *stack;
|
|
|
|
|
2014-01-29 20:22:08 +01:00
|
|
|
ginPrepareDataScan(btree, index, rootBlkno);
|
2013-11-20 15:09:14 +01:00
|
|
|
|
2017-08-16 06:22:32 +02:00
|
|
|
btree->fullScan = true;
|
2013-11-20 15:09:14 +01:00
|
|
|
|
2018-12-27 02:10:51 +01:00
|
|
|
stack = ginFindLeafPage(btree, true, false, snapshot);
|
2013-11-20 15:09:14 +01:00
|
|
|
|
|
|
|
return stack;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|