IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool newValues) { GISTENTRY centry[INDEX_MAX_KEYS]; Datum compatt[INDEX_MAX_KEYS]; int i; IndexTuple res; for (i = 0; i < r->rd_att->natts; i++) { if (isnull[i]) compatt[i] = (Datum) 0; else { gistcentryinit(giststate, i, ¢ry[i], attdata[i], r, NULL, (OffsetNumber) 0, newValues, FALSE); compatt[i] = centry[i].key; } } res = index_form_tuple(giststate->tupdesc, compatt, isnull); GistTupleSetValid(res); return res; }
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool newValues) { GISTENTRY centry[INDEX_MAX_KEYS]; Datum compatt[INDEX_MAX_KEYS]; int i; IndexTuple res; for (i = 0; i < r->rd_att->natts; i++) { if (isnull[i]) compatt[i] = (Datum) 0; else { gistcentryinit(giststate, i, ¢ry[i], attdata[i], r, NULL, (OffsetNumber) 0, newValues, FALSE); compatt[i] = centry[i].key; } } res = index_form_tuple(giststate->tupdesc, compatt, isnull); /* * The offset number on tuples on internal pages is unused. For historical * reasons, it is set 0xffff. */ ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); return res; }
static IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], int datumsize[], bool isnull[]) { IndexTuple tup; char isnullchar[INDEX_MAX_KEYS]; bool whatfree[INDEX_MAX_KEYS]; GISTENTRY centry[INDEX_MAX_KEYS]; Datum compatt[INDEX_MAX_KEYS]; int j; for (j = 0; j < r->rd_att->natts; j++) { if (isnull[j]) { isnullchar[j] = 'n'; compatt[j] = (Datum) 0; whatfree[j] = FALSE; } else { gistcentryinit(giststate, j, ¢ry[j], attdata[j], NULL, NULL, (OffsetNumber) 0, datumsize[j], FALSE, FALSE); isnullchar[j] = ' '; compatt[j] = centry[j].key; if (!isAttByVal(giststate, j)) { whatfree[j] = TRUE; if (centry[j].key != attdata[j]) pfree(DatumGetPointer(attdata[j])); } else whatfree[j] = FALSE; } } tup = (IndexTuple) index_formtuple(giststate->tupdesc, compatt, isnullchar); for (j = 0; j < r->rd_att->natts; j++) if (whatfree[j]) pfree(DatumGetPointer(compatt[j])); return tup; }
/* * Take a compressed entry, and install it on a page. Since we now know * where the entry will live, we decompress it and recompress it using * that knowledge (some compression routines may want to fish around * on the page, for example, or do something special for leaf nodes.) */ static OffsetNumber gistPageAddItem(GISTSTATE *giststate, Relation r, Page page, Item item, Size size, OffsetNumber offsetNumber, ItemIdFlags flags, GISTENTRY *dentry, IndexTuple *newtup) { GISTENTRY tmpcentry; IndexTuple itup = (IndexTuple) item; OffsetNumber retval; Datum datum; bool IsNull; /* * recompress the item given that we now know the exact page and * offset for insertion */ datum = index_getattr(itup, 1, r->rd_att, &IsNull); gistdentryinit(giststate, 0, dentry, datum, (Relation) 0, (Page) 0, (OffsetNumber) InvalidOffsetNumber, ATTSIZE(datum, r, 1, IsNull), FALSE, IsNull); gistcentryinit(giststate, 0, &tmpcentry, dentry->key, r, page, offsetNumber, dentry->bytes, FALSE); *newtup = gist_tuple_replacekey(r, tmpcentry, itup); retval = PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup), offsetNumber, flags); if (retval == InvalidOffsetNumber) elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(r)); /* be tidy */ if (DatumGetPointer(tmpcentry.key) != NULL && tmpcentry.key != dentry->key && tmpcentry.key != datum) pfree(DatumGetPointer(tmpcentry.key)); return (retval); }
/* * Forms union of oldtup and addtup, if union == oldtup then return NULL */ static IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) { GistEntryVector *evec; Datum datum; int datumsize; bool result, neednew = false; char isnull[INDEX_MAX_KEYS], whatfree[INDEX_MAX_KEYS]; Datum attr[INDEX_MAX_KEYS]; GISTENTRY centry[INDEX_MAX_KEYS], oldatt[INDEX_MAX_KEYS], addatt[INDEX_MAX_KEYS], *ev0p, *ev1p; bool olddec[INDEX_MAX_KEYS], adddec[INDEX_MAX_KEYS]; bool oldisnull[INDEX_MAX_KEYS], addisnull[INDEX_MAX_KEYS]; IndexTuple newtup = NULL; int j; evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ); evec->n = 2; ev0p = &(evec->vector[0]); ev1p = &(evec->vector[1]); gistDeCompressAtt(giststate, r, oldtup, NULL, (OffsetNumber) 0, oldatt, olddec, oldisnull); gistDeCompressAtt(giststate, r, addtup, NULL, (OffsetNumber) 0, addatt, adddec, addisnull); for (j = 0; j < r->rd_att->natts; j++) { if (oldisnull[j] && addisnull[j]) { isnull[j] = 'n'; attr[j] = (Datum) 0; whatfree[j] = FALSE; } else { FILLEV( oldisnull[j], oldatt[j].key, oldatt[j].bytes, addisnull[j], addatt[j].key, addatt[j].bytes ); datum = FunctionCall2(&giststate->unionFn[j], PointerGetDatum(evec), PointerGetDatum(&datumsize)); if (oldisnull[j] || addisnull[j]) { if (oldisnull[j]) neednew = true; } else { FunctionCall3(&giststate->equalFn[j], ev0p->key, datum, PointerGetDatum(&result)); if (!result) neednew = true; } if (olddec[j]) pfree(DatumGetPointer(oldatt[j].key)); if (adddec[j]) pfree(DatumGetPointer(addatt[j].key)); gistcentryinit(giststate, j, ¢ry[j], datum, NULL, NULL, (OffsetNumber) 0, datumsize, FALSE, FALSE); isnull[j] = ' '; attr[j] = centry[j].key; if ((!isAttByVal(giststate, j))) { whatfree[j] = TRUE; if (centry[j].key != datum) pfree(DatumGetPointer(datum)); } else whatfree[j] = FALSE; } } pfree(evec); if (neednew) { /* need to update key */ newtup = (IndexTuple) index_formtuple(giststate->tupdesc, attr, isnull); newtup->t_tid = oldtup->t_tid; } for (j = 0; j < r->rd_att->natts; j++) if (whatfree[j]) pfree(DatumGetPointer(attr[j])); return newtup; }
/* * return union of itup vector */ static IndexTuple gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate) { Datum attr[INDEX_MAX_KEYS]; bool whatfree[INDEX_MAX_KEYS]; char isnull[INDEX_MAX_KEYS]; GistEntryVector *evec; Datum datum; int datumsize, i, j; GISTENTRY centry[INDEX_MAX_KEYS]; bool *needfree; IndexTuple newtup; bool IsNull; int reallen; needfree = (bool *) palloc(((len == 1) ? 2 : len) * sizeof(bool)); evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); for (j = 0; j < r->rd_att->natts; j++) { reallen = 0; for (i = 0; i < len; i++) { datum = index_getattr(itvec[i], j + 1, giststate->tupdesc, &IsNull); if (IsNull) continue; gistdentryinit(giststate, j, &(evec->vector[reallen]), datum, NULL, NULL, (OffsetNumber) 0, ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull); if ((!isAttByVal(giststate, j)) && evec->vector[reallen].key != datum) needfree[reallen] = TRUE; else needfree[reallen] = FALSE; reallen++; } if (reallen == 0) { attr[j] = (Datum) 0; isnull[j] = 'n'; whatfree[j] = FALSE; } else { if (reallen == 1) { evec->n = 2; gistentryinit(evec->vector[1], evec->vector[0].key, r, NULL, (OffsetNumber) 0, evec->vector[0].bytes, FALSE); } else evec->n = reallen; datum = FunctionCall2(&giststate->unionFn[j], PointerGetDatum(evec), PointerGetDatum(&datumsize)); for (i = 0; i < reallen; i++) if (needfree[i]) pfree(DatumGetPointer(evec->vector[i].key)); gistcentryinit(giststate, j, ¢ry[j], datum, NULL, NULL, (OffsetNumber) 0, datumsize, FALSE, FALSE); isnull[j] = ' '; attr[j] = centry[j].key; if (!isAttByVal(giststate, j)) { whatfree[j] = TRUE; if (centry[j].key != datum) pfree(DatumGetPointer(datum)); } else whatfree[j] = FALSE; } } pfree(evec); pfree(needfree); newtup = (IndexTuple) index_formtuple(giststate->tupdesc, attr, isnull); for (j = 0; j < r->rd_att->natts; j++) if (whatfree[j]) pfree(DatumGetPointer(attr[j])); return newtup; }
/* * gistinsert -- wrapper for GiST tuple insertion. * * This is the public interface routine for tuple insertion in GiSTs. * It doesn't do any work; just locks the relation and passes the buck. */ Datum gistinsert(PG_FUNCTION_ARGS) { Relation r = (Relation) PG_GETARG_POINTER(0); Datum *datum = (Datum *) PG_GETARG_POINTER(1); char *nulls = (char *) PG_GETARG_POINTER(2); ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3); #ifdef NOT_USED Relation heapRel = (Relation) PG_GETARG_POINTER(4); bool checkUnique = PG_GETARG_BOOL(5); #endif InsertIndexResult res; IndexTuple itup; GISTSTATE giststate; GISTENTRY tmpentry; int i; bool compvec[INDEX_MAX_KEYS]; /* * Since GIST is not marked "amconcurrent" in pg_am, caller should * have acquired exclusive lock on index relation. We need no locking * here. */ /* GiST cannot index tuples with leading NULLs */ if (nulls[0] == 'n') { res = NULL; PG_RETURN_POINTER(res); } initGISTstate(&giststate, r); /* immediately compress keys to normalize */ for (i = 0; i < r->rd_att->natts; i++) { if (nulls[i] == 'n') { datum[i] = (Datum) 0; compvec[i] = FALSE; } else { gistcentryinit(&giststate, i, &tmpentry, datum[i], NULL, NULL, (OffsetNumber) 0, -1 /* size is currently bogus */ , TRUE, FALSE); if (datum[i] != tmpentry.key && !(isAttByVal(&giststate, i))) compvec[i] = TRUE; else compvec[i] = FALSE; datum[i] = tmpentry.key; } } itup = index_formtuple(giststate.tupdesc, datum, nulls); itup->t_tid = *ht_ctid; res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); gistdoinsert(r, itup, &res, &giststate); for (i = 0; i < r->rd_att->natts; i++) if (compvec[i] == TRUE) pfree(DatumGetPointer(datum[i])); pfree(itup); freeGISTstate(&giststate); PG_RETURN_POINTER(res); }
/* * Per-tuple callback from IndexBuildHeapScan */ static void gistbuildCallback(Relation index, HeapTuple htup, Datum *attdata, char *nulls, bool tupleIsAlive, void *state) { GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; bool compvec[INDEX_MAX_KEYS]; GISTENTRY tmpcentry; int i; /* GiST cannot index tuples with leading NULLs */ if (nulls[0] == 'n') return; /* immediately compress keys to normalize */ for (i = 0; i < buildstate->numindexattrs; i++) { if (nulls[i] == 'n') { attdata[i] = (Datum) 0; compvec[i] = FALSE; } else { gistcentryinit(&buildstate->giststate, i, &tmpcentry, attdata[i], NULL, NULL, (OffsetNumber) 0, -1 /* size is currently bogus */ , TRUE, FALSE); if (attdata[i] != tmpcentry.key && !(isAttByVal(&buildstate->giststate, i))) compvec[i] = TRUE; else compvec[i] = FALSE; attdata[i] = tmpcentry.key; } } /* form an index tuple and point it at the heap tuple */ itup = index_formtuple(buildstate->giststate.tupdesc, attdata, nulls); itup->t_tid = htup->t_self; /* * Since we already have the index relation locked, we call * gistdoinsert directly. Normal access method calls dispatch through * gistinsert, which locks the relation for write. This is the right * thing to do if you're inserting single tups, but not when you're * initializing the whole index at once. */ gistdoinsert(index, itup, NULL, &buildstate->giststate); buildstate->indtuples += 1; for (i = 0; i < buildstate->numindexattrs; i++) if (compvec[i]) pfree(DatumGetPointer(attdata[i])); pfree(itup); }