/* * Forms union of oldtup and addtup, if union == oldtup then return NULL */ IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) { bool neednew = false; GISTENTRY oldentries[INDEX_MAX_KEYS], addentries[INDEX_MAX_KEYS]; bool oldisnull[INDEX_MAX_KEYS], addisnull[INDEX_MAX_KEYS]; Datum attr[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; IndexTuple newtup = NULL; int i; gistDeCompressAtt(giststate, r, oldtup, NULL, (OffsetNumber) 0, oldentries, oldisnull); gistDeCompressAtt(giststate, r, addtup, NULL, (OffsetNumber) 0, addentries, addisnull); for (i = 0; i < r->rd_att->natts; i++) { gistMakeUnionKey(giststate, i, oldentries + i, oldisnull[i], addentries + i, addisnull[i], attr + i, isnull + i); if (neednew) /* we already need new key, so we can skip check */ continue; if (isnull[i]) /* union of key may be NULL if and only if both keys are NULL */ continue; if (!addisnull[i]) { if (oldisnull[i] || !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i])) neednew = true; } } if (neednew) { /* need to update key */ newtup = gistFormTuple(giststate, r, attr, isnull, false); newtup->t_tid = oldtup->t_tid; } return newtup; }
/* * Forms union of oldtup and addtup, if union == oldtup then return NULL */ IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) { bool neednew = FALSE; GISTENTRY oldentries[INDEX_MAX_KEYS], addentries[INDEX_MAX_KEYS]; bool oldisnull[INDEX_MAX_KEYS], addisnull[INDEX_MAX_KEYS]; IndexTuple newtup = NULL; int i; if (GistTupleIsInvalid(oldtup) || GistTupleIsInvalid(addtup)) return gist_form_invalid_tuple(ItemPointerGetBlockNumber(&(oldtup->t_tid))); gistDeCompressAtt(giststate, r, oldtup, NULL, (OffsetNumber) 0, oldentries, oldisnull); gistDeCompressAtt(giststate, r, addtup, NULL, (OffsetNumber) 0, addentries, addisnull); for (i = 0; i < r->rd_att->natts; i++) { gistMakeUnionKey(giststate, i, oldentries + i, oldisnull[i], addentries + i, addisnull[i], attrS + i, isnullS + i); if (neednew) /* we already need new key, so we can skip check */ continue; if (isnullS[i]) /* union of key may be NULL if and only if both keys are NULL */ continue; if (!addisnull[i]) { if (oldisnull[i] || gistKeyIsEQ(giststate, i, oldentries[i].key, attrS[i]) == false) neednew = true; } } if (neednew) { /* need to update key */ newtup = gistFormTuple(giststate, r, attrS, isnullS, false); newtup->t_tid = oldtup->t_tid; } 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; }