diff --git a/src/hnsw.h b/src/hnsw.h index 6b184ec..fd34de1 100644 --- a/src/hnsw.h +++ b/src/hnsw.h @@ -133,6 +133,7 @@ HnswPtrDeclare(HnswElementData, HnswElementRelptr, HnswElementPtr); HnswPtrDeclare(HnswNeighborArray, HnswNeighborArrayRelptr, HnswNeighborArrayPtr); HnswPtrDeclare(HnswNeighborArrayPtr, HnswNeighborsRelptr, HnswNeighborsPtr); HnswPtrDeclare(char, DatumRelptr, DatumPtr); +HnswPtrDeclare(IndexTupleData, IndexTupleRelptr, IndexTuplePtr); struct HnswElementData { @@ -149,6 +150,7 @@ struct HnswElementData OffsetNumber neighborOffno; BlockNumber neighborPage; DatumPtr value; + IndexTuplePtr itup; LWLock lock; }; @@ -288,6 +290,7 @@ typedef struct HnswBuildState HnswGraph *graph; double ml; int maxLevel; + TupleDesc tupdesc; /* Memory */ MemoryContext graphCtx; @@ -428,11 +431,12 @@ void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, in void HnswAddHeapTid(HnswElement element, ItemPointer heaptid); HnswNeighborArray *HnswInitNeighborArray(int lm, HnswAllocator * allocator); void HnswInitNeighbors(char *base, HnswElement element, int m, HnswAllocator * alloc); -bool HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPointer heaptid, bool building); +bool HnswInsertTupleOnDisk(Relation index, HnswSupport * support, TupleDesc tupdesc, IndexTuple itup, ItemPointer heaptid, bool building); void HnswUpdateNeighborsOnDisk(Relation index, HnswSupport * support, HnswElement e, int m, bool checkExisting, bool building); void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec); void HnswLoadElement(HnswElement element, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance); -bool HnswFormIndexValue(Datum *out, Datum *values, bool *isnull, const HnswTypeInfo * typeInfo, HnswSupport * support); +TupleDesc HnswTupleDesc(Relation index); +bool HnswFormIndexTuple(IndexTuple *out, Datum *values, bool *isnull, const HnswTypeInfo * typeInfo, HnswSupport * support, TupleDesc tupdesc); void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element); void HnswUpdateConnection(char *base, HnswNeighborArray * neighbors, HnswElement newElement, float distance, int lm, int *updateIdx, Relation index, HnswSupport * support); bool HnswLoadNeighborTids(HnswElement element, ItemPointerData *indextids, Relation index, int m, int lm, int lc); diff --git a/src/hnswbuild.c b/src/hnswbuild.c index b667478..85fc060 100644 --- a/src/hnswbuild.c +++ b/src/hnswbuild.c @@ -476,18 +476,20 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn HnswElement element; HnswAllocator *allocator = &buildstate->allocator; HnswSupport *support = &buildstate->support; - Size valueSize; - Pointer valuePtr; LWLock *flushLock = &graph->flushLock; char *base = buildstate->hnswarea; - Datum value; + TupleDesc tupdesc = buildstate->tupdesc; + IndexTuple itup; + Size itupSize; + IndexTuple itupShared; + bool unused; /* Form index value */ - if (!HnswFormIndexValue(&value, values, isnull, buildstate->typeInfo, support)) + if (!HnswFormIndexTuple(&itup, values, isnull, buildstate->typeInfo, support, tupdesc)) return false; - /* Get datum size */ - valueSize = VARSIZE_ANY(DatumGetPointer(value)); + /* Get tuple size */ + itupSize = IndexTupleSize(itup); /* Ensure graph not flushed when inserting */ LWLockAcquire(flushLock, LW_SHARED); @@ -497,7 +499,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn { LWLockRelease(flushLock); - return HnswInsertTupleOnDisk(index, support, value, heaptid, true); + return HnswInsertTupleOnDisk(index, support, tupdesc, itup, heaptid, true); } /* @@ -529,12 +531,12 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn LWLockRelease(flushLock); - return HnswInsertTupleOnDisk(index, support, value, heaptid, true); + return HnswInsertTupleOnDisk(index, support, tupdesc, itup, heaptid, true); } /* Ok, we can proceed to allocate the element */ element = HnswInitElement(base, heaptid, buildstate->m, buildstate->ml, buildstate->maxLevel, allocator); - valuePtr = HnswAlloc(allocator, valueSize); + itupShared = HnswAlloc(allocator, itupSize); /* * We have now allocated the space needed for the element, so we don't @@ -543,9 +545,10 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn */ LWLockRelease(&graph->allocatorLock); - /* Copy the datum */ - memcpy(valuePtr, DatumGetPointer(value), valueSize); - HnswPtrStore(base, element->value, valuePtr); + /* Copy the tuple */ + memcpy(itupShared, itup, itupSize); + HnswPtrStore(base, element->itup, itupShared); + HnswPtrStore(base, element->value, DatumGetPointer(index_getattr(itupShared, 1, tupdesc, &unused))); /* Create a lock for the element */ LWLockInitialize(&element->lock, hnsw_lock_tranche_id); @@ -698,6 +701,7 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index buildstate->graph = &buildstate->graphData; buildstate->ml = HnswGetMl(buildstate->m); buildstate->maxLevel = HnswGetMaxLevel(buildstate->m); + buildstate->tupdesc = HnswTupleDesc(index); buildstate->graphCtx = GenerationContextCreate(CurrentMemoryContext, "Hnsw build graph context", @@ -722,6 +726,7 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index static void FreeBuildState(HnswBuildState * buildstate) { + pfree(buildstate->tupdesc); MemoryContextDelete(buildstate->graphCtx); MemoryContextDelete(buildstate->tmpCtx); } diff --git a/src/hnswinsert.c b/src/hnswinsert.c index a5fac4e..a1447e4 100644 --- a/src/hnswinsert.c +++ b/src/hnswinsert.c @@ -687,7 +687,7 @@ UpdateGraphOnDisk(Relation index, HnswSupport * support, HnswElement element, in * Insert a tuple into the index */ bool -HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPointer heaptid, bool building) +HnswInsertTupleOnDisk(Relation index, HnswSupport * support, TupleDesc tupdesc, IndexTuple itup, ItemPointer heaptid, bool building) { HnswElement entryPoint; HnswElement element; @@ -695,6 +695,7 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo int efConstruction = HnswGetEfConstruction(index); LOCKMODE lockmode = ShareLock; char *base = NULL; + bool unused; /* * Get a shared lock. This allows vacuum to ensure no in-flight inserts @@ -708,7 +709,8 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo /* Create an element */ element = HnswInitElement(base, heaptid, m, HnswGetMl(m), HnswGetMaxLevel(m), NULL); - HnswPtrStore(base, element->value, DatumGetPointer(value)); + HnswPtrStore(base, element->itup, itup); + HnswPtrStore(base, element->value, DatumGetPointer(index_getattr(itup, 1, tupdesc, &unused))); /* Prevent concurrent inserts when likely updating entry point */ if (entryPoint == NULL || element->level > entryPoint->level) @@ -742,17 +744,18 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo static void HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid) { - Datum value; + IndexTuple itup; const HnswTypeInfo *typeInfo = HnswGetTypeInfo(index); HnswSupport support; + TupleDesc tupdesc = HnswTupleDesc(index); HnswInitSupport(&support, index); - /* Form index value */ - if (!HnswFormIndexValue(&value, values, isnull, typeInfo, &support)) + /* Form index tuple */ + if (!HnswFormIndexTuple(&itup, values, isnull, typeInfo, &support, tupdesc)) return; - HnswInsertTupleOnDisk(index, &support, value, heaptid, false); + HnswInsertTupleOnDisk(index, &support, tupdesc, itup, heaptid, false); } /* diff --git a/src/hnswutils.c b/src/hnswutils.c index e84561e..ad3edfa 100644 --- a/src/hnswutils.c +++ b/src/hnswutils.c @@ -395,10 +395,24 @@ HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, Bloc } /* - * Form index value + * Get the tuple descriptor + */ +TupleDesc +HnswTupleDesc(Relation index) +{ + TupleDesc tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(index)); + + /* Prevent compression */ + TupleDescAttr(tupdesc, 0)->attstorage = TYPSTORAGE_PLAIN; + + return tupdesc; +} + +/* + * Form index tuple */ bool -HnswFormIndexValue(Datum *out, Datum *values, bool *isnull, const HnswTypeInfo * typeInfo, HnswSupport * support) +HnswFormIndexTuple(IndexTuple *out, Datum *values, bool *isnull, const HnswTypeInfo * typeInfo, HnswSupport * support, TupleDesc tupdesc) { /* Detoast once for all calls */ Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0])); @@ -416,7 +430,9 @@ HnswFormIndexValue(Datum *out, Datum *values, bool *isnull, const HnswTypeInfo * value = HnswNormValue(typeInfo, support->collation, value); } - *out = value; + /* TODO Combine value with values to support multiple attributes */ + Assert(tupdesc->natts == 1); + *out = index_form_tuple(tupdesc, &value, isnull); return true; }