diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-12-17 16:41:16 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-12-17 16:42:30 -0500 |
commit | 8daeb5ddd698f661eb118f8e874e7c68cfd6ae09 (patch) | |
tree | 765599b73e45a6ca5529398489f31a534ab1924e /src/backend/access/spgist/spginsert.c | |
parent | 19fc0fe3ae7861a8b0d3ab8b67bd01fde33bf2da (diff) | |
download | postgresql-8daeb5ddd698f661eb118f8e874e7c68cfd6ae09.tar.gz postgresql-8daeb5ddd698f661eb118f8e874e7c68cfd6ae09.zip |
Add SP-GiST (space-partitioned GiST) index access method.
SP-GiST is comparable to GiST in flexibility, but supports non-balanced
partitioned search structures rather than balanced trees. As described at
PGCon 2011, this new indexing structure can beat GiST in both index build
time and query speed for search problems that it is well matched to.
There are a number of areas that could still use improvement, but at this
point the code seems committable.
Teodor Sigaev and Oleg Bartunov, with considerable revisions by Tom Lane
Diffstat (limited to 'src/backend/access/spgist/spginsert.c')
-rw-r--r-- | src/backend/access/spgist/spginsert.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c new file mode 100644 index 00000000000..4a059bdfedc --- /dev/null +++ b/src/backend/access/spgist/spginsert.c @@ -0,0 +1,219 @@ +/*------------------------------------------------------------------------- + * + * spginsert.c + * Externally visible index creation/insertion routines + * + * All the actual insertion logic is in spgdoinsert.c. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/spgist/spginsert.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/genam.h" +#include "access/spgist_private.h" +#include "catalog/index.h" +#include "miscadmin.h" +#include "storage/bufmgr.h" +#include "storage/smgr.h" +#include "utils/memutils.h" + + +typedef struct +{ + SpGistState spgstate; /* SPGiST's working state */ + MemoryContext tmpCtx; /* per-tuple temporary context */ +} SpGistBuildState; + + +/* Callback to process one heap tuple during IndexBuildHeapScan */ +static void +spgistBuildCallback(Relation index, HeapTuple htup, Datum *values, + bool *isnull, bool tupleIsAlive, void *state) +{ + SpGistBuildState *buildstate = (SpGistBuildState *) state; + + /* SPGiST doesn't index nulls */ + if (*isnull == false) + { + /* Work in temp context, and reset it after each tuple */ + MemoryContext oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx); + + spgdoinsert(index, &buildstate->spgstate, &htup->t_self, *values); + + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(buildstate->tmpCtx); + } +} + +/* + * Build an SP-GiST index. + */ +Datum +spgbuild(PG_FUNCTION_ARGS) +{ + Relation heap = (Relation) PG_GETARG_POINTER(0); + Relation index = (Relation) PG_GETARG_POINTER(1); + IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); + IndexBuildResult *result; + double reltuples; + SpGistBuildState buildstate; + Buffer metabuffer, + rootbuffer; + + if (RelationGetNumberOfBlocks(index) != 0) + elog(ERROR, "index \"%s\" already contains data", + RelationGetRelationName(index)); + + /* + * Initialize the meta page and root page + */ + metabuffer = SpGistNewBuffer(index); + rootbuffer = SpGistNewBuffer(index); + + Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO); + Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_HEAD_BLKNO); + + START_CRIT_SECTION(); + + SpGistInitMetapage(BufferGetPage(metabuffer)); + MarkBufferDirty(metabuffer); + SpGistInitBuffer(rootbuffer, SPGIST_LEAF); + MarkBufferDirty(rootbuffer); + + if (RelationNeedsWAL(index)) + { + XLogRecPtr recptr; + XLogRecData rdata; + + /* WAL data is just the relfilenode */ + rdata.data = (char *) &(index->rd_node); + rdata.len = sizeof(RelFileNode); + rdata.buffer = InvalidBuffer; + rdata.next = NULL; + + recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX, &rdata); + + PageSetLSN(BufferGetPage(metabuffer), recptr); + PageSetTLI(BufferGetPage(metabuffer), ThisTimeLineID); + PageSetLSN(BufferGetPage(rootbuffer), recptr); + PageSetTLI(BufferGetPage(rootbuffer), ThisTimeLineID); + } + + END_CRIT_SECTION(); + + UnlockReleaseBuffer(metabuffer); + UnlockReleaseBuffer(rootbuffer); + + /* + * Now insert all the heap data into the index + */ + initSpGistState(&buildstate.spgstate, index); + buildstate.spgstate.isBuild = true; + + buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext, + "SP-GiST build temporary context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + reltuples = IndexBuildHeapScan(heap, index, indexInfo, true, + spgistBuildCallback, (void *) &buildstate); + + MemoryContextDelete(buildstate.tmpCtx); + + SpGistUpdateMetaPage(index); + + result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult)); + result->heap_tuples = result->index_tuples = reltuples; + + PG_RETURN_POINTER(result); +} + +/* + * Build an empty SPGiST index in the initialization fork + */ +Datum +spgbuildempty(PG_FUNCTION_ARGS) +{ + Relation index = (Relation) PG_GETARG_POINTER(0); + Page page; + + /* Construct metapage. */ + page = (Page) palloc(BLCKSZ); + SpGistInitMetapage(page); + + /* Write the page. If archiving/streaming, XLOG it. */ + smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO, + (char *) page, true); + if (XLogIsNeeded()) + log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, + SPGIST_METAPAGE_BLKNO, page); + + /* Likewise for the root page. */ + SpGistInitPage(page, SPGIST_LEAF); + + smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_HEAD_BLKNO, + (char *) page, true); + if (XLogIsNeeded()) + log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, + SPGIST_HEAD_BLKNO, page); + + /* + * An immediate sync is required even if we xlog'd the pages, because the + * writes did not go through shared buffers and therefore a concurrent + * checkpoint may have moved the redo pointer past our xlog record. + */ + smgrimmedsync(index->rd_smgr, INIT_FORKNUM); + + PG_RETURN_VOID(); +} + +/* + * Insert one new tuple into an SPGiST index. + */ +Datum +spginsert(PG_FUNCTION_ARGS) +{ + Relation index = (Relation) PG_GETARG_POINTER(0); + Datum *values = (Datum *) PG_GETARG_POINTER(1); + bool *isnull = (bool *) PG_GETARG_POINTER(2); + ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3); + +#ifdef NOT_USED + Relation heapRel = (Relation) PG_GETARG_POINTER(4); + IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); +#endif + SpGistState spgstate; + MemoryContext oldCtx; + MemoryContext insertCtx; + + /* SPGiST doesn't index nulls */ + if (*isnull) + PG_RETURN_BOOL(false); + + insertCtx = AllocSetContextCreate(CurrentMemoryContext, + "SP-GiST insert temporary context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + oldCtx = MemoryContextSwitchTo(insertCtx); + + initSpGistState(&spgstate, index); + + spgdoinsert(index, &spgstate, ht_ctid, *values); + + SpGistUpdateMetaPage(index); + + MemoryContextSwitchTo(oldCtx); + MemoryContextDelete(insertCtx); + + /* return false since we've not done any unique check */ + PG_RETURN_BOOL(false); +} |