/* * gistSplit -- split a page in the tree and fill struct * used for XLOG and real writes buffers. Function is recursive, ie * it will split page until keys will fit in every page. */ SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, /* contains compressed entry */ int len, GISTSTATE *giststate) { IndexTuple *lvectup, *rvectup; GistSplitVector v; GistEntryVector *entryvec; int i; SplitedPageLayout *res = NULL; /* generate the item array */ entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY)); entryvec->n = len + 1; memset(v.spl_lisnull, TRUE, sizeof(bool) * giststate->tupdesc->natts); memset(v.spl_risnull, TRUE, sizeof(bool) * giststate->tupdesc->natts); gistSplitByKey(r, page, itup, len, giststate, &v, entryvec, 0); /* form left and right vector */ lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1)); rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1)); for (i = 0; i < v.splitVector.spl_nleft; i++) lvectup[i] = itup[v.splitVector.spl_left[i] - 1]; for (i = 0; i < v.splitVector.spl_nright; i++) rvectup[i] = itup[v.splitVector.spl_right[i] - 1]; /* finalize splitting (may need another split) */ if (!gistfitpage(rvectup, v.splitVector.spl_nright)) { res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate); } else { ROTATEDIST(res); res->block.num = v.splitVector.spl_nright; res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist)); res->itup = (v.spl_rightvalid) ? gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false) : gist_form_invalid_tuple(GIST_ROOT_BLKNO); } if (!gistfitpage(lvectup, v.splitVector.spl_nleft)) { SplitedPageLayout *resptr, *subres; resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate); /* install on list's tail */ while (resptr->next) resptr = resptr->next; resptr->next = res; res = subres; } else { ROTATEDIST(res); res->block.num = v.splitVector.spl_nleft; res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist)); res->itup = (v.spl_leftvalid) ? gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false) : gist_form_invalid_tuple(GIST_ROOT_BLKNO); } return res; }
/* * trys to split page by attno key, in a case of null * values move its to separate page. */ void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, GistEntryVector *entryvec, int attno) { int i; static OffsetNumber offNullTuples[MaxOffsetNumber]; int nOffNullTuples = 0; for (i = 1; i <= len; i++) { Datum datum; bool IsNull; if (!GistPageIsLeaf(page) && GistTupleIsInvalid(itup[i - 1])) { gistSplitByInvalid(giststate, v, itup, len); return; } datum = index_getattr(itup[i - 1], attno + 1, giststate->tupdesc, &IsNull); gistdentryinit(giststate, attno, &(entryvec->vector[i]), datum, r, page, i, FALSE, IsNull); if (IsNull) offNullTuples[nOffNullTuples++] = i; } v->spl_leftvalid = v->spl_rightvalid = true; if (nOffNullTuples == len) { /* * Corner case: All keys in attno column are null, we should try to * split by keys in next column. It all keys in all columns are NULL * just split page half by half */ v->spl_risnull[attno] = v->spl_lisnull[attno] = TRUE; if (attno + 1 == r->rd_att->natts) gistSplitHalf(&v->splitVector, len); else gistSplitByKey(r, page, itup, len, giststate, v, entryvec, attno + 1); } else if (nOffNullTuples > 0) { int j = 0; /* * We don't want to mix NULLs and not-NULLs keys on one page, so move * nulls to right page */ v->splitVector.spl_right = offNullTuples; v->splitVector.spl_nright = nOffNullTuples; v->spl_risnull[attno] = TRUE; v->splitVector.spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber)); v->splitVector.spl_nleft = 0; for (i = 1; i <= len; i++) if (j < v->splitVector.spl_nright && offNullTuples[j] == i) j++; else v->splitVector.spl_left[v->splitVector.spl_nleft++] = i; v->spl_equiv = NULL; gistunionsubkey(giststate, itup, v, attno); } else { /* * all keys are not-null */ entryvec->n = len + 1; if (gistUserPicksplit(r, entryvec, attno, v, itup, len, giststate) && attno + 1 != r->rd_att->natts) { /* * Splitting on attno column is not optimized: there is a tuples * which can be freely left or right page, we will try to split * page by following columns */ if (v->spl_equiv == NULL) { /* * simple case: left and right keys for attno column are * equial */ gistSplitByKey(r, page, itup, len, giststate, v, entryvec, attno + 1); } else { /* we should clean up vector from already distributed tuples */ IndexTuple *newitup = (IndexTuple *) palloc((len + 1) * sizeof(IndexTuple)); OffsetNumber *map = (OffsetNumber *) palloc((len + 1) * sizeof(IndexTuple)); int newlen = 0; GIST_SPLITVEC backupSplit = v->splitVector; for (i = 0; i < len; i++) if (v->spl_equiv[i + 1]) { map[newlen] = i + 1; newitup[newlen++] = itup[i]; } Assert(newlen > 0); backupSplit.spl_left = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len); memcpy(backupSplit.spl_left, v->splitVector.spl_left, sizeof(OffsetNumber) * v->splitVector.spl_nleft); backupSplit.spl_right = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len); memcpy(backupSplit.spl_right, v->splitVector.spl_right, sizeof(OffsetNumber) * v->splitVector.spl_nright); gistSplitByKey(r, page, newitup, newlen, giststate, v, entryvec, attno + 1); /* merge result of subsplit */ for (i = 0; i < v->splitVector.spl_nleft; i++) backupSplit.spl_left[backupSplit.spl_nleft++] = map[v->splitVector.spl_left[i] - 1]; for (i = 0; i < v->splitVector.spl_nright; i++) backupSplit.spl_right[backupSplit.spl_nright++] = map[v->splitVector.spl_right[i] - 1]; v->splitVector = backupSplit; /* reunion left and right datums */ gistunionsubkey(giststate, itup, v, attno); } } } }