/* * gistSplit -- split a page in the tree. */ static IndexTuple * gistSplit(Relation r, Buffer buffer, IndexTuple *itup, /* contains compressed entry */ int *len, GISTSTATE *giststate, InsertIndexResult *res) { Page p; Buffer leftbuf, rightbuf; Page left, right; IndexTuple *lvectup, *rvectup, *newtup; BlockNumber lbknum, rbknum; GISTPageOpaque opaque; GIST_SPLITVEC v; GistEntryVector *entryvec; bool *decompvec; int i, j, nlen; int MaxGrpId = 1; Datum datum; bool IsNull; p = (Page) BufferGetPage(buffer); opaque = (GISTPageOpaque) PageGetSpecialPointer(p); /* * The root of the tree is the first block in the relation. If we're * about to split the root, we need to do some hocus-pocus to enforce * this guarantee. */ if (BufferGetBlockNumber(buffer) == GISTP_ROOT) { leftbuf = ReadBuffer(r, P_NEW); GISTInitBuffer(leftbuf, opaque->flags); lbknum = BufferGetBlockNumber(leftbuf); left = (Page) BufferGetPage(leftbuf); } else { leftbuf = buffer; IncrBufferRefCount(buffer); lbknum = BufferGetBlockNumber(buffer); left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData)); } rightbuf = ReadBuffer(r, P_NEW); GISTInitBuffer(rightbuf, opaque->flags); rbknum = BufferGetBlockNumber(rightbuf); right = (Page) BufferGetPage(rightbuf); /* generate the item array */ entryvec = palloc(GEVHDRSZ + (*len + 1) * sizeof(GISTENTRY)); entryvec->n = *len + 1; decompvec = (bool *) palloc((*len + 1) * sizeof(bool)); for (i = 1; i <= *len; i++) { datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull); gistdentryinit(giststate, 0, &(entryvec->vector[i]), datum, r, p, i, ATTSIZE(datum, giststate->tupdesc, 1, IsNull), FALSE, IsNull); if ((!isAttByVal(giststate, 0)) && entryvec->vector[i].key != datum) decompvec[i] = TRUE; else decompvec[i] = FALSE; } /* * now let the user-defined picksplit function set up the split * vector; in entryvec have no null value!! */ FunctionCall2(&giststate->picksplitFn[0], PointerGetDatum(entryvec), PointerGetDatum(&v)); /* compatibility with old code */ if (v.spl_left[v.spl_nleft - 1] == InvalidOffsetNumber) v.spl_left[v.spl_nleft - 1] = (OffsetNumber) *len; if (v.spl_right[v.spl_nright - 1] == InvalidOffsetNumber) v.spl_right[v.spl_nright - 1] = (OffsetNumber) *len; v.spl_lattr[0] = v.spl_ldatum; v.spl_rattr[0] = v.spl_rdatum; v.spl_lisnull[0] = false; v.spl_risnull[0] = false; /* * if index is multikey, then we must to try get smaller bounding box * for subkey(s) */ if (r->rd_att->natts > 1) { v.spl_idgrp = (int *) palloc0(sizeof(int) * (*len + 1)); v.spl_grpflag = (char *) palloc0(sizeof(char) * (*len + 1)); v.spl_ngrp = (int *) palloc(sizeof(int) * (*len + 1)); MaxGrpId = gistfindgroup(giststate, entryvec->vector, &v); /* form union of sub keys for each page (l,p) */ gistunionsubkey(r, giststate, itup, &v); /* * if possible, we insert equivalent tuples with control by * penalty for a subkey(s) */ if (MaxGrpId > 1) gistadjsubkey(r, itup, len, &v, giststate); pfree(v.spl_idgrp); pfree(v.spl_grpflag); pfree(v.spl_ngrp); } /* clean up the entry vector: its keys need to be deleted, too */ for (i = 1; i <= *len; i++) if (decompvec[i]) pfree(DatumGetPointer(entryvec->vector[i].key)); pfree(entryvec); pfree(decompvec); /* form left and right vector */ lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nleft); rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nright); for (i = 0; i < v.spl_nleft; i++) lvectup[i] = itup[v.spl_left[i] - 1]; for (i = 0; i < v.spl_nright; i++) rvectup[i] = itup[v.spl_right[i] - 1]; /* write on disk (may be need another split) */ if (gistnospace(right, rvectup, v.spl_nright)) { nlen = v.spl_nright; newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate, (res && rvectup[nlen - 1] == itup[*len - 1]) ? res : NULL); ReleaseBuffer(rightbuf); for (j = 1; j < r->rd_att->natts; j++) if ((!isAttByVal(giststate, j)) && !v.spl_risnull[j]) pfree(DatumGetPointer(v.spl_rattr[j])); } else { OffsetNumber l; l = gistwritebuffer(r, right, rvectup, v.spl_nright, FirstOffsetNumber); WriteBuffer(rightbuf); if (res) ItemPointerSet(&((*res)->pointerData), rbknum, l); nlen = 1; newtup = (IndexTuple *) palloc(sizeof(IndexTuple) * 1); newtup[0] = gistFormTuple(giststate, r, v.spl_rattr, v.spl_rattrsize, v.spl_risnull); ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1); } if (gistnospace(left, lvectup, v.spl_nleft)) { int llen = v.spl_nleft; IndexTuple *lntup; lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate, (res && lvectup[llen - 1] == itup[*len - 1]) ? res : NULL); ReleaseBuffer(leftbuf); for (j = 1; j < r->rd_att->natts; j++) if ((!isAttByVal(giststate, j)) && !v.spl_lisnull[j]) pfree(DatumGetPointer(v.spl_lattr[j])); newtup = gistjoinvector(newtup, &nlen, lntup, llen); pfree(lntup); } else { OffsetNumber l; l = gistwritebuffer(r, left, lvectup, v.spl_nleft, FirstOffsetNumber); if (BufferGetBlockNumber(buffer) != GISTP_ROOT) PageRestoreTempPage(left, p); WriteBuffer(leftbuf); if (res) ItemPointerSet(&((*res)->pointerData), lbknum, l); nlen += 1; newtup = (IndexTuple *) repalloc((void *) newtup, sizeof(IndexTuple) * nlen); newtup[nlen - 1] = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lattrsize, v.spl_lisnull); ItemPointerSet(&(newtup[nlen - 1]->t_tid), lbknum, 1); } /* !!! pfree */ pfree(rvectup); pfree(lvectup); pfree(v.spl_left); pfree(v.spl_right); *len = nlen; return newtup; }
/* * Calls user picksplit method for attno columns to split vector to * two vectors. May use attno+n columns data to * get better split. * Returns TRUE and v->spl_equiv = NULL if left and right unions of attno columns are the same, * so caller may find better split * Returns TRUE and v->spl_equiv != NULL if there is tuples which may be freely moved */ static bool gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVector *v, IndexTuple *itup, int len, GISTSTATE *giststate) { GIST_SPLITVEC *sv = &v->splitVector; /* * now let the user-defined picksplit function set up the split vector; in * entryvec there is no null value!! */ sv->spl_ldatum_exists = (v->spl_lisnull[attno]) ? false : true; sv->spl_rdatum_exists = (v->spl_risnull[attno]) ? false : true; sv->spl_ldatum = v->spl_lattr[attno]; sv->spl_rdatum = v->spl_rattr[attno]; FunctionCall2(&giststate->picksplitFn[attno], PointerGetDatum(entryvec), PointerGetDatum(sv)); if ( sv->spl_nleft == 0 || sv->spl_nright == 0 ) { ereport(DEBUG1, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("picksplit method for column %d of index \"%s\" failed", attno+1, RelationGetRelationName(r)), errhint("The index is not optimal. To optimize it, contact a developer, or try to use the column as the second one in the CREATE INDEX command."))); /* * Reinit GIST_SPLITVEC. Although that fields are not used * by genericPickSplit(), let us set up it for further processing */ sv->spl_ldatum_exists = (v->spl_lisnull[attno]) ? false : true; sv->spl_rdatum_exists = (v->spl_risnull[attno]) ? false : true; sv->spl_ldatum = v->spl_lattr[attno]; sv->spl_rdatum = v->spl_rattr[attno]; genericPickSplit(giststate, entryvec, sv, attno); if (sv->spl_ldatum_exists || sv->spl_rdatum_exists) supportSecondarySplit(r, giststate, attno, sv, v->spl_lattr[attno], v->spl_rattr[attno]); } else { /* compatibility with old code */ if (sv->spl_left[sv->spl_nleft - 1] == InvalidOffsetNumber) sv->spl_left[sv->spl_nleft - 1] = (OffsetNumber) (entryvec->n - 1); if (sv->spl_right[sv->spl_nright - 1] == InvalidOffsetNumber) sv->spl_right[sv->spl_nright - 1] = (OffsetNumber) (entryvec->n - 1); if (sv->spl_ldatum_exists || sv->spl_rdatum_exists) { elog(LOG, "PickSplit method of %d columns of index '%s' doesn't support secondary split", attno + 1, RelationGetRelationName(r)); supportSecondarySplit(r, giststate, attno, sv, v->spl_lattr[attno], v->spl_rattr[attno]); } } v->spl_lattr[attno] = sv->spl_ldatum; v->spl_rattr[attno] = sv->spl_rdatum; v->spl_lisnull[attno] = false; v->spl_risnull[attno] = false; /* * if index is multikey, then we must to try get smaller bounding box for * subkey(s) */ v->spl_equiv = NULL; if (giststate->tupdesc->natts > 1 && attno + 1 != giststate->tupdesc->natts) { if (gistKeyIsEQ(giststate, attno, sv->spl_ldatum, sv->spl_rdatum)) { /* * Left and right key's unions are equial, so we can get better * split by following columns. Note, unions for attno columns are * already done. */ return true; } else { int LenEquiv; v->spl_equiv = (bool *) palloc0(sizeof(bool) * (entryvec->n + 1)); LenEquiv = gistfindgroup(r, giststate, entryvec->vector, v, attno); /* * if possible, we should distribute equivalent tuples */ if (LenEquiv == 0) { gistunionsubkey(giststate, itup, v, attno + 1); } else { cleanupOffsets(sv->spl_left, &sv->spl_nleft, v->spl_equiv, &LenEquiv); cleanupOffsets(sv->spl_right, &sv->spl_nright, v->spl_equiv, &LenEquiv); gistunionsubkey(giststate, itup, v, attno + 1); if (LenEquiv == 1) { /* * In case with one tuple we just choose left-right by * penalty. It's simplify user-defined pickSplit */ OffsetNumber toMove = InvalidOffsetNumber; for (toMove = FirstOffsetNumber; toMove < entryvec->n; toMove++) if (v->spl_equiv[toMove]) break; Assert(toMove < entryvec->n); placeOne(r, giststate, v, itup[toMove - 1], toMove, attno + 1); /* * redo gistunionsubkey(): it will not degradate * performance, because it's very rarely */ v->spl_equiv = NULL; gistunionsubkey(giststate, itup, v, attno + 1); return false; } else if (LenEquiv > 1) return true; } } } return false; }