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; }
/* * hashinsert() -- insert an index tuple into a hash table. * * Hash on the index tuple's key, find the appropriate location * for the new tuple, put it there, and return an InsertIndexResult * to the caller. */ Datum hashinsert(PG_FUNCTION_ARGS) { Relation rel = (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; HashItem hitem; IndexTuple itup; /* generate an index tuple */ itup = index_formtuple(RelationGetDescr(rel), datum, nulls); itup->t_tid = *ht_ctid; /* * If the single index key is null, we don't insert it into the index. * Hash tables support scans on '='. Relational algebra says that A = * B returns null if either A or B is null. This means that no * qualification used in an index scan could ever return true on a * null attribute. It also means that indices can't be used by ISNULL * or NOTNULL scans, but that's an artifact of the strategy map * architecture chosen in 1986, not of the way nulls are handled here. */ if (IndexTupleHasNulls(itup)) { pfree(itup); PG_RETURN_POINTER((InsertIndexResult) NULL); } hitem = _hash_formitem(itup); res = _hash_doinsert(rel, hitem); pfree(hitem); pfree(itup); PG_RETURN_POINTER(res); }
/* ** Given an IndexTuple to be inserted on a page, this routine replaces ** the key with another key, which may involve generating a new IndexTuple ** if the sizes don't match or if the null status changes. ** ** XXX this only works for a single-column index tuple! */ static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t) { bool IsNull; Datum datum = index_getattr(t, 1, r->rd_att, &IsNull); /* * If new entry fits in index tuple, copy it in. To avoid worrying * about null-value bitmask, pass it off to the general * index_formtuple routine if either the previous or new value is * NULL. */ if (!IsNull && DatumGetPointer(entry.key) != NULL && (Size) entry.bytes <= ATTSIZE(datum, r, 1, IsNull)) { memcpy(DatumGetPointer(datum), DatumGetPointer(entry.key), entry.bytes); /* clear out old size */ t->t_info &= ~INDEX_SIZE_MASK; /* or in new size */ t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData)); return t; } else { /* generate a new index tuple for the compressed entry */ TupleDesc tupDesc = r->rd_att; IndexTuple newtup; char isnull; isnull = DatumGetPointer(entry.key) != NULL ? ' ' : 'n'; newtup = (IndexTuple) index_formtuple(tupDesc, &(entry.key), &isnull); newtup->t_tid = t->t_tid; return newtup; } }
/* * Per-tuple callback from IndexBuildHeapScan */ static void hashbuildCallback(Relation index, HeapTuple htup, Datum *attdata, char *nulls, bool tupleIsAlive, void *state) { HashBuildState *buildstate = (HashBuildState *) state; IndexTuple itup; HashItem hitem; InsertIndexResult res; /* form an index tuple and point it at the heap tuple */ itup = index_formtuple(RelationGetDescr(index), attdata, nulls); itup->t_tid = htup->t_self; /* Hash indexes don't index nulls, see notes in hashinsert */ if (IndexTupleHasNulls(itup)) { pfree(itup); return; } hitem = _hash_formitem(itup); res = _hash_doinsert(index, hitem); if (res) pfree(res); buildstate->indtuples += 1; pfree(hitem); pfree(itup); }
/* * 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); }