aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/gist
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-04-03 15:09:37 +0300
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-04-03 15:43:50 +0300
commit04e298b826d452ceb838d9fda884a29f229d484d (patch)
tree95d544122aff600de78c9e7e716546ea78de925e /src/backend/access/gist
parentfc752505a99a4e2c781a070d3d42a25289c22e3c (diff)
downloadpostgresql-04e298b826d452ceb838d9fda884a29f229d484d.tar.gz
postgresql-04e298b826d452ceb838d9fda884a29f229d484d.zip
Avoid palloc in critical section in GiST WAL-logging.
Memory allocation can fail if you run out of memory, and inside a critical section that will lead to a PANIC. Use conservatively-sized arrays in stack instead. There was previously no explicit limit on the number of pages a GiST split can produce, it was only limited by the number of LWLocks that can be held simultaneously (100 at the moment). This patch adds an explicit limit of 75 pages. That should be plenty, a typical split shouldn't produce more than 2-3 page halves. The bug has been there forever, but only backpatch down to 9.1. The code was changed significantly in 9.1, and it doesn't seem worth the risk or trouble to adapt this for 9.0 and 8.4.
Diffstat (limited to 'src/backend/access/gist')
-rw-r--r--src/backend/access/gist/README2
-rw-r--r--src/backend/access/gist/gist.c14
-rw-r--r--src/backend/access/gist/gistxlog.c16
3 files changed, 23 insertions, 9 deletions
diff --git a/src/backend/access/gist/README b/src/backend/access/gist/README
index 4bcac1f2c79..dd4c9fa70a0 100644
--- a/src/backend/access/gist/README
+++ b/src/backend/access/gist/README
@@ -135,7 +135,7 @@ that didn't need to be split.
This differs from the insertion algorithm in the original paper. In the
original paper, you first walk down the tree until you reach a leaf page, and
-then you adjust the downlink in the parent, and propagating the adjustment up,
+then you adjust the downlink in the parent, and propagate the adjustment up,
all the way up to the root in the worst case. But we adjust the downlinks to
cover the new key already when we walk down, so that when we reach the leaf
page, we don't need to update the parents anymore, except to insert the
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b0711052fcc..64125d51953 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -220,6 +220,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
GistNSN oldnsn = 0;
SplitedPageLayout rootpg;
bool is_rootsplit;
+ int npage;
is_rootsplit = (blkno == GIST_ROOT_BLKNO);
@@ -241,6 +242,19 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
dist = gistSplit(rel, page, itvec, tlen, giststate);
/*
+ * Check that split didn't produce too many pages.
+ */
+ npage = 0;
+ for (ptr = dist; ptr; ptr = ptr->next)
+ npage++;
+ /* in a root split, we'll add one more page to the list below */
+ if (is_rootsplit)
+ npage++;
+ if (npage > GIST_MAX_SPLIT_PAGES)
+ elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
+ npage, GIST_MAX_SPLIT_PAGES);
+
+ /*
* Set up pages to work with. Allocate new buffers for all but the
* leftmost page. The original page becomes the new leftmost page, and
* is just replaced with the new contents.
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index d6fdc04cf28..7cc64aeb490 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -379,7 +379,7 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
BlockNumber origrlink, GistNSN orignsn,
Buffer leftchildbuf, bool markfollowright)
{
- XLogRecData *rdata;
+ XLogRecData rdata[GIST_MAX_SPLIT_PAGES * 2 + 2];
gistxlogPageSplit xlrec;
SplitedPageLayout *ptr;
int npage = 0,
@@ -388,8 +388,12 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
for (ptr = dist; ptr; ptr = ptr->next)
npage++;
-
- rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2));
+ /*
+ * the caller should've checked this already, but doesn't hurt to check
+ * again.
+ */
+ if (npage > GIST_MAX_SPLIT_PAGES)
+ elog(ERROR, "GiST page split into too many halves");
xlrec.node = node;
xlrec.origblkno = blkno;
@@ -439,7 +443,6 @@ gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
- pfree(rdata);
return recptr;
}
@@ -462,14 +465,12 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
IndexTuple *itup, int ituplen,
Buffer leftchildbuf)
{
- XLogRecData *rdata;
+ XLogRecData rdata[MaxIndexTuplesPerPage + 3];
gistxlogPageUpdate xlrec;
int cur,
i;
XLogRecPtr recptr;
- rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (3 + ituplen));
-
xlrec.node = node;
xlrec.blkno = BufferGetBlockNumber(buffer);
xlrec.ntodelete = ntodelete;
@@ -516,6 +517,5 @@ gistXLogUpdate(RelFileNode node, Buffer buffer,
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
- pfree(rdata);
return recptr;
}