diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 99cbcaa..864ec4e 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); @@ -240,6 +241,15 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, itvec = gistjoinvector(itvec, &tlen, itup, ntup); dist = gistSplit(rel, page, itvec, tlen, giststate); + /* Count the number of pages the original page was split into */ + npage = 0; + for (ptr = dist; ptr; ptr = ptr->next) + 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 @@ -402,7 +412,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, /* Write the WAL record */ if (RelationNeedsWAL(rel)) recptr = gistXLogSplit(rel->rd_node, blkno, is_leaf, - dist, oldrlink, oldnsn, leftchildbuf, + dist, npage, oldrlink, oldnsn, leftchildbuf, markfollowright); else recptr = gistGetFakeLSN(rel); diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c index 17946bf..512c2f5 100644 --- a/src/backend/access/gist/gistxlog.c +++ b/src/backend/access/gist/gistxlog.c @@ -375,21 +375,23 @@ gist_xlog_cleanup(void) */ XLogRecPtr gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf, - SplitedPageLayout *dist, + SplitedPageLayout *dist, int npage, 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, - cur; + int cur; XLogRecPtr recptr; - 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 (%d, maximum %d)", + npage, GIST_MAX_SPLIT_PAGES); xlrec.node = node; xlrec.origblkno = blkno; @@ -439,7 +441,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 +463,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 +515,5 @@ gistXLogUpdate(RelFileNode node, Buffer buffer, recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata); - pfree(rdata); return recptr; } diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index cae6dbc..2d04af6 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -22,6 +22,22 @@ #include "utils/rbtree.h" #include "utils/hsearch.h" +/* + * Maximum number of "halves" a page can be split into in one operation. + * Typically a split produces 2 halves, but can be more if keys have very + * different lengths, or when inserting multiple keys in one operation (as + * when inserting downlinks to an internal node). There is no theoretical + * limit on this, but in practice if you get more than a handful page halves + * in one split, there'something wrong with the opclass implementation. + * GIST_MAX_SPLIT_PAGES is an arbitrary limit on that, used to size some + * local arrays used during split. Note that there is also a limit on the + * number of buffers that can be held locked at a time, MAX_SIMUL_LWLOCKS, + * so if you raise this higher than that limit, you'll just get a different + * error. + */ +#define GIST_MAX_SPLIT_PAGES 75 + + /* Buffer lock modes */ #define GIST_SHARE BUFFER_LOCK_SHARE #define GIST_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE @@ -447,7 +463,7 @@ extern XLogRecPtr gistXLogUpdate(RelFileNode node, Buffer buffer, extern XLogRecPtr gistXLogSplit(RelFileNode node, BlockNumber blkno, bool page_is_leaf, - SplitedPageLayout *dist, + SplitedPageLayout *dist, int npage, BlockNumber origrlink, GistNSN oldnsn, Buffer leftchild, bool markfollowright);