2011-12-17 22:41:16 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* spginsert.c
|
|
|
|
* Externally visible index creation/insertion routines
|
|
|
|
*
|
|
|
|
* All the actual insertion logic is in spgdoinsert.c.
|
|
|
|
*
|
2014-01-07 22:05:30 +01:00
|
|
|
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
|
2011-12-17 22:41:16 +01:00
|
|
|
* 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"
|
2014-11-06 12:52:08 +01:00
|
|
|
#include "access/xlog.h"
|
|
|
|
#include "access/xloginsert.h"
|
2011-12-17 22:41:16 +01:00
|
|
|
#include "catalog/index.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "storage/bufmgr.h"
|
|
|
|
#include "storage/smgr.h"
|
|
|
|
#include "utils/memutils.h"
|
2012-08-31 23:04:31 +02:00
|
|
|
#include "utils/rel.h"
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
2012-03-11 21:29:04 +01:00
|
|
|
MemoryContext oldCtx;
|
2011-12-17 22:41:16 +01:00
|
|
|
|
2012-03-11 21:29:04 +01:00
|
|
|
/* Work in temp context, and reset it after each tuple */
|
|
|
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
2013-11-02 21:45:42 +01:00
|
|
|
/*
|
|
|
|
* Even though no concurrent insertions can be happening, we still might
|
|
|
|
* get a buffer-locking failure due to bgwriter or checkpointer taking a
|
|
|
|
* lock on some buffer. So we need to be willing to retry. We can flush
|
|
|
|
* any temp data when retrying.
|
|
|
|
*/
|
|
|
|
while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
|
|
|
|
*values, *isnull))
|
|
|
|
{
|
|
|
|
MemoryContextReset(buildstate->tmpCtx);
|
|
|
|
}
|
2011-12-17 22:41:16 +01:00
|
|
|
|
2012-03-11 21:29:04 +01:00
|
|
|
MemoryContextSwitchTo(oldCtx);
|
|
|
|
MemoryContextReset(buildstate->tmpCtx);
|
2011-12-17 22:41:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
2012-03-11 21:29:04 +01:00
|
|
|
rootbuffer,
|
|
|
|
nullbuffer;
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
if (RelationGetNumberOfBlocks(index) != 0)
|
|
|
|
elog(ERROR, "index \"%s\" already contains data",
|
|
|
|
RelationGetRelationName(index));
|
|
|
|
|
|
|
|
/*
|
2012-03-11 21:29:04 +01:00
|
|
|
* Initialize the meta page and root pages
|
2011-12-17 22:41:16 +01:00
|
|
|
*/
|
|
|
|
metabuffer = SpGistNewBuffer(index);
|
|
|
|
rootbuffer = SpGistNewBuffer(index);
|
2012-03-11 21:29:04 +01:00
|
|
|
nullbuffer = SpGistNewBuffer(index);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
|
2012-03-11 21:29:04 +01:00
|
|
|
Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
|
|
|
|
Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
START_CRIT_SECTION();
|
|
|
|
|
|
|
|
SpGistInitMetapage(BufferGetPage(metabuffer));
|
|
|
|
MarkBufferDirty(metabuffer);
|
|
|
|
SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
|
|
|
|
MarkBufferDirty(rootbuffer);
|
2012-03-11 21:29:04 +01:00
|
|
|
SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
|
|
|
|
MarkBufferDirty(nullbuffer);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
PageSetLSN(BufferGetPage(rootbuffer), recptr);
|
2012-03-11 21:29:04 +01:00
|
|
|
PageSetLSN(BufferGetPage(nullbuffer), recptr);
|
2011-12-17 22:41:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
|
|
|
|
|
|
|
UnlockReleaseBuffer(metabuffer);
|
|
|
|
UnlockReleaseBuffer(rootbuffer);
|
2012-03-11 21:29:04 +01:00
|
|
|
UnlockReleaseBuffer(nullbuffer);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now insert all the heap data into the index
|
|
|
|
*/
|
|
|
|
initSpGistState(&buildstate.spgstate, index);
|
|
|
|
buildstate.spgstate.isBuild = true;
|
|
|
|
|
|
|
|
buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
2012-06-10 21:20:04 +02:00
|
|
|
"SP-GiST build temporary context",
|
2011-12-17 22:41:16 +01:00
|
|
|
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);
|
|
|
|
|
2014-05-06 18:12:18 +02:00
|
|
|
/* Write the page. If archiving/streaming, XLOG it. */
|
2013-03-22 14:54:07 +01:00
|
|
|
PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
|
2011-12-17 22:41:16 +01:00
|
|
|
smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
|
|
|
|
(char *) page, true);
|
|
|
|
if (XLogIsNeeded())
|
|
|
|
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
|
2013-12-03 23:10:47 +01:00
|
|
|
SPGIST_METAPAGE_BLKNO, page, false);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
/* Likewise for the root page. */
|
|
|
|
SpGistInitPage(page, SPGIST_LEAF);
|
|
|
|
|
2013-03-22 14:54:07 +01:00
|
|
|
PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
|
2012-03-11 21:29:04 +01:00
|
|
|
smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
|
|
|
|
(char *) page, true);
|
|
|
|
if (XLogIsNeeded())
|
|
|
|
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
|
2013-12-03 23:10:47 +01:00
|
|
|
SPGIST_ROOT_BLKNO, page, true);
|
2012-03-11 21:29:04 +01:00
|
|
|
|
|
|
|
/* Likewise for the null-tuples root page. */
|
|
|
|
SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
|
|
|
|
|
2013-03-22 14:54:07 +01:00
|
|
|
PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
|
2012-03-11 21:29:04 +01:00
|
|
|
smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
|
2011-12-17 22:41:16 +01:00
|
|
|
(char *) page, true);
|
|
|
|
if (XLogIsNeeded())
|
|
|
|
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
|
2013-12-03 23:10:47 +01:00
|
|
|
SPGIST_NULL_BLKNO, page, true);
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
|
|
|
"SP-GiST insert temporary context",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
oldCtx = MemoryContextSwitchTo(insertCtx);
|
|
|
|
|
|
|
|
initSpGistState(&spgstate, index);
|
|
|
|
|
2013-06-14 20:26:43 +02:00
|
|
|
/*
|
|
|
|
* We might have to repeat spgdoinsert() multiple times, if conflicts
|
|
|
|
* occur with concurrent insertions. If so, reset the insertCtx each time
|
2014-05-06 18:12:18 +02:00
|
|
|
* to avoid cumulative memory consumption. That means we also have to
|
2013-06-14 20:26:43 +02:00
|
|
|
* redo initSpGistState(), but it's cheap enough not to matter.
|
|
|
|
*/
|
|
|
|
while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
|
|
|
|
{
|
|
|
|
MemoryContextReset(insertCtx);
|
|
|
|
initSpGistState(&spgstate, index);
|
|
|
|
}
|
2011-12-17 22:41:16 +01:00
|
|
|
|
|
|
|
SpGistUpdateMetaPage(index);
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldCtx);
|
|
|
|
MemoryContextDelete(insertCtx);
|
|
|
|
|
|
|
|
/* return false since we've not done any unique check */
|
|
|
|
PG_RETURN_BOOL(false);
|
|
|
|
}
|