6343
(1 row)
+DROP INDEX text_idx;
+-- Repeat the same queries with an extended data set. The data set is the
+-- same that we used before, except that each element in the array is
+-- repeated three times, offset by 1000 and 2000. For example, {1, 5}
+-- becomes {1, 1001, 2001, 5, 1005, 2005}.
+--
+-- That has proven to be unreasonably effective at exercising codepaths in
+-- core GiST code related to splitting parent pages, which is not covered by
+-- other tests. This is a bit out-of-place as the point is to test core GiST
+-- code rather than this extension, but there is no suitable GiST opclass in
+-- core that would reach the same codepaths.
+CREATE TABLE more__int AS SELECT
+ -- Leave alone NULLs, empty arrays and the one row that we use to test
+ -- equality
+ CASE WHEN a IS NULL OR a = '{}' OR a = '{73,23,20}' THEN a ELSE
+ (select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000) from (select unnest(a) u) x)
+ END AS a, a as b
+ FROM test__int;
+CREATE INDEX ON more__int using gist (a gist__int_ops(numranges = 252));
+SELECT count(*) from more__int WHERE a && '{23,50}';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '23|50';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from more__int WHERE a @> '{23,50}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '23&50';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from more__int WHERE a @> '{20,23}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from more__int WHERE a <@ '{73,23,20}';
+ count
+-------
+ 10
+(1 row)
+
+SELECT count(*) from more__int WHERE a = '{73,23,20}';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '50&68';
+ count
+-------
+ 9
+(1 row)
+
+SELECT count(*) from more__int WHERE a @> '{20,23}' or a @> '{50,68}';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '(20&23)|(50&68)';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '20 | !21';
+ count
+-------
+ 6566
+(1 row)
+
+SELECT count(*) from more__int WHERE a @@ '!20 & !21';
+ count
+-------
+ 6343
+(1 row)
+
RESET enable_seqscan;
SELECT count(*) from test__int WHERE a @@ '20 | !21';
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+DROP INDEX text_idx;
+
+-- Repeat the same queries with an extended data set. The data set is the
+-- same that we used before, except that each element in the array is
+-- repeated three times, offset by 1000 and 2000. For example, {1, 5}
+-- becomes {1, 1001, 2001, 5, 1005, 2005}.
+--
+-- That has proven to be unreasonably effective at exercising codepaths in
+-- core GiST code related to splitting parent pages, which is not covered by
+-- other tests. This is a bit out-of-place as the point is to test core GiST
+-- code rather than this extension, but there is no suitable GiST opclass in
+-- core that would reach the same codepaths.
+CREATE TABLE more__int AS SELECT
+ -- Leave alone NULLs, empty arrays and the one row that we use to test
+ -- equality
+ CASE WHEN a IS NULL OR a = '{}' OR a = '{73,23,20}' THEN a ELSE
+ (select array_agg(u) || array_agg(u + 1000) || array_agg(u + 2000) from (select unnest(a) u) x)
+ END AS a, a as b
+ FROM test__int;
+CREATE INDEX ON more__int using gist (a gist__int_ops(numranges = 252));
+
+SELECT count(*) from more__int WHERE a && '{23,50}';
+SELECT count(*) from more__int WHERE a @@ '23|50';
+SELECT count(*) from more__int WHERE a @> '{23,50}';
+SELECT count(*) from more__int WHERE a @@ '23&50';
+SELECT count(*) from more__int WHERE a @> '{20,23}';
+SELECT count(*) from more__int WHERE a <@ '{73,23,20}';
+SELECT count(*) from more__int WHERE a = '{73,23,20}';
+SELECT count(*) from more__int WHERE a @@ '50&68';
+SELECT count(*) from more__int WHERE a @> '{20,23}' or a @> '{50,68}';
+SELECT count(*) from more__int WHERE a @@ '(20&23)|(50&68)';
+SELECT count(*) from more__int WHERE a @@ '20 | !21';
+SELECT count(*) from more__int WHERE a @@ '!20 & !21';
+
+
RESET enable_seqscan;
* remain so at exit, but it might not be the same page anymore.
*/
static void
-gistFindCorrectParent(Relation r, GISTInsertStack *child)
+gistFindCorrectParent(Relation r, GISTInsertStack *child, bool is_build)
{
GISTInsertStack *parent = child->parent;
+ ItemId iid;
+ IndexTuple idxtuple;
+ OffsetNumber maxoff;
+ GISTInsertStack *ptr;
gistcheckpage(r, parent->buffer);
parent->page = (Page) BufferGetPage(parent->buffer);
+ maxoff = PageGetMaxOffsetNumber(parent->page);
- /* here we don't need to distinguish between split and page update */
- if (child->downlinkoffnum == InvalidOffsetNumber ||
- parent->lsn != PageGetLSN(parent->page))
+ /* Check if the downlink is still where it was before */
+ if (child->downlinkoffnum != InvalidOffsetNumber && child->downlinkoffnum <= maxoff)
{
- /* parent is changed, look child in right links until found */
- OffsetNumber i,
- maxoff;
- ItemId iid;
- IndexTuple idxtuple;
- GISTInsertStack *ptr;
+ iid = PageGetItemId(parent->page, child->downlinkoffnum);
+ idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
+ if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
+ return; /* still there */
+ }
- while (true)
- {
- maxoff = PageGetMaxOffsetNumber(parent->page);
- for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
- {
- iid = PageGetItemId(parent->page, i);
- idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
- if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
- {
- /* yes!!, found */
- child->downlinkoffnum = i;
- return;
- }
- }
+ /*
+ * The page has changed since we looked. During normal operation, every
+ * update of a page changes its LSN, so the LSN we memorized should have
+ * changed too. During index build, however, we don't WAL-log the changes
+ * until we have built the index, so the LSN doesn't change. There is no
+ * concurrent activity during index build, but we might have changed the
+ * parent ourselves.
+ */
+ Assert(parent->lsn != PageGetLSN(parent->page) || is_build);
+
+ /*
+ * Scan the page to re-find the downlink. If the page was split, it might
+ * have moved to a different page, so follow the right links until we find
+ * it.
+ */
+ while (true)
+ {
+ OffsetNumber i;
- parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
- UnlockReleaseBuffer(parent->buffer);
- if (parent->blkno == InvalidBlockNumber)
+ maxoff = PageGetMaxOffsetNumber(parent->page);
+ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+ {
+ iid = PageGetItemId(parent->page, i);
+ idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
+ if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
{
- /*
- * End of chain and still didn't find parent. It's a very-very
- * rare situation when root splitted.
- */
- break;
+ /* yes!!, found */
+ child->downlinkoffnum = i;
+ return;
}
- parent->buffer = ReadBuffer(r, parent->blkno);
- LockBuffer(parent->buffer, GIST_EXCLUSIVE);
- gistcheckpage(r, parent->buffer);
- parent->page = (Page) BufferGetPage(parent->buffer);
}
- /*
- * awful!!, we need search tree to find parent ... , but before we
- * should release all old parent
- */
-
- ptr = child->parent->parent; /* child->parent already released
- * above */
- while (ptr)
+ parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
+ parent->downlinkoffnum = InvalidOffsetNumber;
+ UnlockReleaseBuffer(parent->buffer);
+ if (parent->blkno == InvalidBlockNumber)
{
- ReleaseBuffer(ptr->buffer);
- ptr = ptr->parent;
+ /*
+ * End of chain and still didn't find parent. It's a very-very
+ * rare situation when root splitted.
+ */
+ break;
}
+ parent->buffer = ReadBuffer(r, parent->blkno);
+ LockBuffer(parent->buffer, GIST_EXCLUSIVE);
+ gistcheckpage(r, parent->buffer);
+ parent->page = (Page) BufferGetPage(parent->buffer);
+ }
- /* ok, find new path */
- ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
+ /*
+ * awful!!, we need search tree to find parent ... , but before we should
+ * release all old parent
+ */
- /* read all buffers as expected by caller */
- /* note we don't lock them or gistcheckpage them here! */
- while (ptr)
- {
- ptr->buffer = ReadBuffer(r, ptr->blkno);
- ptr->page = (Page) BufferGetPage(ptr->buffer);
- ptr = ptr->parent;
- }
+ ptr = child->parent->parent; /* child->parent already released above */
+ while (ptr)
+ {
+ ReleaseBuffer(ptr->buffer);
+ ptr = ptr->parent;
+ }
- /* install new chain of parents to stack */
- child->parent = parent;
+ /* ok, find new path */
+ ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
- /* make recursive call to normal processing */
- LockBuffer(child->parent->buffer, GIST_EXCLUSIVE);
- gistFindCorrectParent(r, child);
+ /* read all buffers as expected by caller */
+ /* note we don't lock them or gistcheckpage them here! */
+ while (ptr)
+ {
+ ptr->buffer = ReadBuffer(r, ptr->blkno);
+ ptr->page = (Page) BufferGetPage(ptr->buffer);
+ ptr = ptr->parent;
}
+
+ /* install new chain of parents to stack */
+ child->parent = parent;
+
+ /* make recursive call to normal processing */
+ LockBuffer(child->parent->buffer, GIST_EXCLUSIVE);
+ gistFindCorrectParent(r, child, is_build);
}
/*
*/
static IndexTuple
gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate,
- GISTInsertStack *stack)
+ GISTInsertStack *stack, bool is_build)
{
Page page = BufferGetPage(buf);
OffsetNumber maxoff;
ItemId iid;
LockBuffer(stack->parent->buffer, GIST_EXCLUSIVE);
- gistFindCorrectParent(rel, stack);
+ gistFindCorrectParent(rel, stack, is_build);
iid = PageGetItemId(stack->parent->page, stack->downlinkoffnum);
downlink = (IndexTuple) PageGetItem(stack->parent->page, iid);
downlink = CopyIndexTuple(downlink);
page = BufferGetPage(buf);
/* Form the new downlink tuples to insert to parent */
- downlink = gistformdownlink(state->r, buf, giststate, stack);
+ downlink = gistformdownlink(state->r, buf, giststate, stack, state->is_build);
si->buf = buf;
si->downlink = downlink;
right = (GISTPageSplitInfo *) list_nth(splitinfo, pos);
left = (GISTPageSplitInfo *) list_nth(splitinfo, pos - 1);
- gistFindCorrectParent(state->r, stack);
+ gistFindCorrectParent(state->r, stack, state->is_build);
if (gistinserttuples(state, stack->parent, giststate,
&right->downlink, 1,
InvalidOffsetNumber,
*/
tuples[0] = left->downlink;
tuples[1] = right->downlink;
- gistFindCorrectParent(state->r, stack);
- if (gistinserttuples(state, stack->parent, giststate,
- tuples, 2,
- stack->downlinkoffnum,
- left->buf, right->buf,
- true, /* Unlock parent */
- unlockbuf /* Unlock stack->buffer if caller wants
- * that */
- ))
- {
- /*
- * If the parent page was split, the downlink might have moved.
- */
- stack->downlinkoffnum = InvalidOffsetNumber;
- }
+ gistFindCorrectParent(state->r, stack, state->is_build);
+ (void) gistinserttuples(state, stack->parent, giststate,
+ tuples, 2,
+ stack->downlinkoffnum,
+ left->buf, right->buf,
+ true, /* Unlock parent */
+ unlockbuf /* Unlock stack->buffer if caller
+ * wants that */
+ );
+
+ /*
+ * The downlink might have moved when we updated it. Even if the page
+ * wasn't split, because gistinserttuples() implements updating the old
+ * tuple by removing and re-inserting it!
+ */
+ stack->downlinkoffnum = InvalidOffsetNumber;
Assert(left->buf == stack->buffer);