1996-08-26 22:02:12 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* gist.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* interface routines for the postgres GiST index access method.
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-01-12 01:12:58 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.68 2001/01/12 00:12:58 scrappy Exp $
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-10-20 10:32:11 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/gist.h"
|
|
|
|
#include "access/gistscan.h"
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/index.h"
|
2000-06-15 05:33:12 +02:00
|
|
|
#include "catalog/pg_index.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "executor/executor.h"
|
1999-09-18 21:08:25 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-10-21 07:11:00 +02:00
|
|
|
|
2000-10-21 17:43:36 +02:00
|
|
|
#include "access/xlogutils.h"
|
1996-10-21 07:11:00 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* result's status */
|
|
|
|
#define INSERTED 0x01
|
|
|
|
#define SPLITED 0x02
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/* non-export function prototypes */
|
2001-01-12 01:12:58 +01:00
|
|
|
static void gistdoinsert(Relation r,
|
|
|
|
IndexTuple itup,
|
|
|
|
InsertIndexResult *res,
|
|
|
|
GISTSTATE *GISTstate);
|
|
|
|
static int gistlayerinsert( Relation r, BlockNumber blkno,
|
|
|
|
IndexTuple **itup,
|
|
|
|
int *len,
|
|
|
|
InsertIndexResult *res,
|
|
|
|
GISTSTATE *giststate );
|
|
|
|
static OffsetNumber gistwritebuffer( Relation r,
|
|
|
|
Page page,
|
|
|
|
IndexTuple *itup,
|
|
|
|
int len,
|
|
|
|
OffsetNumber off,
|
|
|
|
GISTSTATE *giststate );
|
|
|
|
static int gistnospace( Page page,
|
|
|
|
IndexTuple *itvec, int len );
|
|
|
|
static IndexTuple * gistreadbuffer( Relation r,
|
|
|
|
Buffer buffer, int *len );
|
|
|
|
static IndexTuple * gistjoinvector(
|
|
|
|
IndexTuple *itvec, int *len,
|
|
|
|
IndexTuple *additvec, int addlen );
|
|
|
|
static IndexTuple gistunion( Relation r, IndexTuple *itvec,
|
|
|
|
int len, GISTSTATE *giststate );
|
|
|
|
static IndexTuple gistgetadjusted( Relation r,
|
|
|
|
IndexTuple oldtup,
|
|
|
|
IndexTuple addtup,
|
|
|
|
GISTSTATE *giststate );
|
|
|
|
static IndexTuple * gistSplit(Relation r,
|
|
|
|
Buffer buffer,
|
|
|
|
IndexTuple *itup,
|
|
|
|
int *len,
|
|
|
|
GISTSTATE *giststate,
|
|
|
|
InsertIndexResult *res);
|
|
|
|
static void gistnewroot(GISTSTATE *giststate, Relation r,
|
|
|
|
IndexTuple *itup, int len);
|
1997-09-08 04:41:22 +02:00
|
|
|
static void GISTInitBuffer(Buffer b, uint32 f);
|
2001-01-12 01:12:58 +01:00
|
|
|
static OffsetNumber gistchoose(Relation r, Page p,
|
|
|
|
IndexTuple it,
|
|
|
|
GISTSTATE *giststate);
|
|
|
|
static IndexTuple gist_tuple_replacekey(Relation r,
|
|
|
|
GISTENTRY entry, IndexTuple t);
|
|
|
|
static void gistcentryinit(GISTSTATE *giststate,
|
|
|
|
GISTENTRY *e, char *pr,
|
|
|
|
Relation r, Page pg,
|
|
|
|
OffsetNumber o, int b, bool l);
|
|
|
|
|
|
|
|
#undef GISTDEBUG
|
1999-07-19 04:06:15 +02:00
|
|
|
#ifdef GISTDEBUG
|
2001-01-12 01:12:58 +01:00
|
|
|
static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff);
|
1999-07-19 04:06:15 +02:00
|
|
|
#endif
|
1996-08-26 22:02:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
** routine to build an index. Basically calls insert over and over
|
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistbuild(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2000-06-13 09:35:40 +02:00
|
|
|
Relation heap = (Relation) PG_GETARG_POINTER(0);
|
|
|
|
Relation index = (Relation) PG_GETARG_POINTER(1);
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
|
|
|
|
Node *oldPred = (Node *) PG_GETARG_POINTER(3);
|
2000-06-13 09:35:40 +02:00
|
|
|
#ifdef NOT_USED
|
2000-07-15 00:18:02 +02:00
|
|
|
IndexStrategy istrat = (IndexStrategy) PG_GETARG_POINTER(4);
|
2000-06-13 09:35:40 +02:00
|
|
|
#endif
|
2000-07-15 00:18:02 +02:00
|
|
|
HeapScanDesc hscan;
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple htup;
|
|
|
|
IndexTuple itup;
|
2000-07-15 00:18:02 +02:00
|
|
|
TupleDesc htupdesc,
|
|
|
|
itupdesc;
|
|
|
|
Datum attdata[INDEX_MAX_KEYS];
|
|
|
|
char nulls[INDEX_MAX_KEYS];
|
|
|
|
int nhtups,
|
|
|
|
nitups;
|
|
|
|
Node *pred = indexInfo->ii_Predicate;
|
1996-10-23 09:42:13 +02:00
|
|
|
#ifndef OMIT_PARTIAL_INDEX
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleTable tupleTable;
|
1997-09-07 07:04:48 +02:00
|
|
|
TupleTableSlot *slot;
|
1996-10-23 09:42:13 +02:00
|
|
|
#endif
|
2000-07-15 00:18:02 +02:00
|
|
|
ExprContext *econtext;
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTSTATE giststate;
|
|
|
|
GISTENTRY tmpcentry;
|
1998-08-19 04:04:17 +02:00
|
|
|
Buffer buffer = InvalidBuffer;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool *compvec;
|
2000-07-15 00:18:02 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-12-15 13:47:01 +01:00
|
|
|
/* no locking is needed */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
initGISTstate(&giststate, index);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We expect to be called exactly once for any index relation. If
|
|
|
|
* that's not the case, big trouble's what we have.
|
|
|
|
*/
|
2000-07-15 00:18:02 +02:00
|
|
|
if (oldPred == NULL && RelationGetNumberOfBlocks(index) != 0)
|
1999-11-08 00:08:36 +01:00
|
|
|
elog(ERROR, "%s already contains data", RelationGetRelationName(index));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* initialize the root page (if this is a new index) */
|
|
|
|
if (oldPred == NULL)
|
|
|
|
{
|
|
|
|
buffer = ReadBuffer(index, P_NEW);
|
|
|
|
GISTInitBuffer(buffer, F_LEAF);
|
|
|
|
WriteBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/* get tuple descriptors for heap and index relations */
|
|
|
|
htupdesc = RelationGetDescr(heap);
|
|
|
|
itupdesc = RelationGetDescr(index);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a predicate (partial) index, we will need to evaluate
|
|
|
|
* the predicate using ExecQual, which requires the current tuple to
|
|
|
|
* be in a slot of a TupleTable. In addition, ExecQual must have an
|
|
|
|
* ExprContext referring to that slot. Here, we initialize dummy
|
2000-07-15 00:18:02 +02:00
|
|
|
* TupleTable and ExprContext objects for this purpose. --Nels, Feb 92
|
|
|
|
*
|
|
|
|
* We construct the ExprContext anyway since we need a per-tuple
|
|
|
|
* temporary memory context for function evaluation -- tgl July 00
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1996-08-26 22:02:12 +02:00
|
|
|
#ifndef OMIT_PARTIAL_INDEX
|
1997-09-07 07:04:48 +02:00
|
|
|
if (pred != NULL || oldPred != NULL)
|
|
|
|
{
|
|
|
|
tupleTable = ExecCreateTupleTable(1);
|
|
|
|
slot = ExecAllocTableSlot(tupleTable);
|
2000-07-15 00:18:02 +02:00
|
|
|
ExecSetSlotDescriptor(slot, htupdesc);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
1996-10-23 09:42:13 +02:00
|
|
|
{
|
|
|
|
tupleTable = NULL;
|
|
|
|
slot = NULL;
|
|
|
|
}
|
2000-07-15 00:18:02 +02:00
|
|
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
|
|
|
#else
|
|
|
|
econtext = MakeExprContext(NULL, TransactionCommandContext);
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* OMIT_PARTIAL_INDEX */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/* build the index */
|
|
|
|
nhtups = nitups = 0;
|
|
|
|
|
|
|
|
compvec = (bool *) palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
|
|
|
|
|
|
|
|
/* start a heap scan */
|
|
|
|
hscan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
|
1998-08-19 04:04:17 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
MemoryContextReset(econtext->ecxt_per_tuple_memory);
|
|
|
|
|
|
|
|
nhtups++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
#ifndef OMIT_PARTIAL_INDEX
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
|
|
|
* this tuple if it was already in the existing partial index
|
|
|
|
*/
|
|
|
|
if (oldPred != NULL)
|
|
|
|
{
|
|
|
|
slot->val = htup;
|
2000-01-20 00:55:03 +01:00
|
|
|
if (ExecQual((List *) oldPred, econtext, false))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
nitups++;
|
1997-09-07 07:04:48 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip this tuple if it doesn't satisfy the partial-index
|
|
|
|
* predicate
|
|
|
|
*/
|
|
|
|
if (pred != NULL)
|
|
|
|
{
|
|
|
|
slot->val = htup;
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!ExecQual((List *) pred, econtext, false))
|
1997-09-07 07:04:48 +02:00
|
|
|
continue;
|
|
|
|
}
|
2000-07-12 04:37:39 +02:00
|
|
|
#endif /* OMIT_PARTIAL_INDEX */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
nitups++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For the current heap tuple, extract all the attributes we use
|
|
|
|
* in this index, and note which are null.
|
|
|
|
*/
|
2000-07-15 00:18:02 +02:00
|
|
|
FormIndexDatum(indexInfo,
|
|
|
|
htup,
|
|
|
|
htupdesc,
|
|
|
|
econtext->ecxt_per_tuple_memory,
|
|
|
|
attdata,
|
|
|
|
nulls);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* immediately compress keys to normalize */
|
2000-07-15 00:18:02 +02:00
|
|
|
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
gistcentryinit(&giststate, &tmpcentry, (char *) attdata[i],
|
1997-09-07 07:04:48 +02:00
|
|
|
(Relation) NULL, (Page) NULL, (OffsetNumber) 0,
|
|
|
|
-1 /* size is currently bogus */ , TRUE);
|
2000-07-15 00:18:02 +02:00
|
|
|
if (attdata[i] != (Datum) tmpcentry.pred &&
|
|
|
|
!(giststate.keytypbyval))
|
1997-09-07 07:04:48 +02:00
|
|
|
compvec[i] = TRUE;
|
|
|
|
else
|
|
|
|
compvec[i] = FALSE;
|
2000-07-15 00:18:02 +02:00
|
|
|
attdata[i] = (Datum) tmpcentry.pred;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* form an index tuple and point it at the heap tuple */
|
2000-07-15 00:18:02 +02:00
|
|
|
itup = index_formtuple(itupdesc, attdata, nulls);
|
1998-11-27 20:52:36 +01:00
|
|
|
itup->t_tid = htup->t_self;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
gistdoinsert(index, itup, NULL, &giststate);
|
2000-07-15 00:18:02 +02:00
|
|
|
|
|
|
|
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
|
|
|
|
if (compvec[i])
|
|
|
|
pfree(DatumGetPointer(attdata[i]));
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(itup);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* okay, all heap tuples are indexed */
|
2000-07-15 00:18:02 +02:00
|
|
|
heap_endscan(hscan);
|
|
|
|
|
|
|
|
pfree(compvec);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
#ifndef OMIT_PARTIAL_INDEX
|
1997-09-07 07:04:48 +02:00
|
|
|
if (pred != NULL || oldPred != NULL)
|
|
|
|
{
|
1999-12-10 04:56:14 +01:00
|
|
|
ExecDropTupleTable(tupleTable, true);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
2000-07-12 04:37:39 +02:00
|
|
|
#endif /* OMIT_PARTIAL_INDEX */
|
2000-07-15 00:18:02 +02:00
|
|
|
FreeExprContext(econtext);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/*
|
1999-09-18 21:08:25 +02:00
|
|
|
* Since we just counted the tuples in the heap, we update its stats
|
|
|
|
* in pg_class to guarantee that the planner takes advantage of the
|
2000-04-12 19:17:23 +02:00
|
|
|
* index we just created. But, only update statistics during normal
|
|
|
|
* index definitions, not for indices on system catalogs created
|
|
|
|
* during bootstrap processing. We must close the relations before
|
|
|
|
* updating statistics to guarantee that the relcache entries are
|
|
|
|
* flushed when we increment the command counter in UpdateStats(). But
|
|
|
|
* we do not release any locks on the relations; those will be held
|
|
|
|
* until end of transaction.
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
1999-09-18 21:08:25 +02:00
|
|
|
if (IsNormalProcessingMode())
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Oid hrelid = RelationGetRelid(heap);
|
|
|
|
Oid irelid = RelationGetRelid(index);
|
1999-09-18 21:08:25 +02:00
|
|
|
|
|
|
|
heap_close(heap, NoLock);
|
|
|
|
index_close(index);
|
2000-11-08 23:10:03 +01:00
|
|
|
UpdateStats(hrelid, nhtups);
|
|
|
|
UpdateStats(irelid, nitups);
|
|
|
|
if (oldPred != NULL)
|
1999-09-18 21:08:25 +02:00
|
|
|
{
|
2000-07-15 00:18:02 +02:00
|
|
|
if (nitups == nhtups)
|
1999-09-18 21:08:25 +02:00
|
|
|
pred = NULL;
|
|
|
|
UpdateIndexPredicate(irelid, oldPred, pred);
|
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
#ifdef GISTDEBUG
|
|
|
|
gist_dumptree(index, 0, GISTP_ROOT, 0);
|
|
|
|
#endif
|
|
|
|
|
2000-06-14 07:24:50 +02:00
|
|
|
PG_RETURN_VOID();
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* gistinsert -- wrapper for GiST tuple insertion.
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistinsert(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2000-06-13 09:35:40 +02:00
|
|
|
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);
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
InsertIndexResult res;
|
1997-09-08 04:41:22 +02:00
|
|
|
IndexTuple itup;
|
|
|
|
GISTSTATE giststate;
|
|
|
|
GISTENTRY tmpentry;
|
|
|
|
int i;
|
|
|
|
bool *compvec;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
initGISTstate(&giststate, r);
|
|
|
|
|
|
|
|
/* immediately compress keys to normalize */
|
|
|
|
compvec = (bool *) palloc(sizeof(bool) * r->rd_att->natts);
|
|
|
|
for (i = 0; i < r->rd_att->natts; i++)
|
|
|
|
{
|
|
|
|
gistcentryinit(&giststate, &tmpentry, (char *) datum[i],
|
|
|
|
(Relation) NULL, (Page) NULL, (OffsetNumber) 0,
|
|
|
|
-1 /* size is currently bogus */ , TRUE);
|
|
|
|
if (datum[i] != (Datum) tmpentry.pred && !(giststate.keytypbyval))
|
|
|
|
compvec[i] = TRUE;
|
|
|
|
else
|
|
|
|
compvec[i] = FALSE;
|
|
|
|
datum[i] = (Datum) tmpentry.pred;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
itup = index_formtuple(RelationGetDescr(r), datum, nulls);
|
1997-09-07 07:04:48 +02:00
|
|
|
itup->t_tid = *ht_ctid;
|
|
|
|
|
1998-12-15 13:47:01 +01:00
|
|
|
/*
|
|
|
|
* Notes in ExecUtils:ExecOpenIndices()
|
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* RelationSetLockForWrite(r);
|
1998-12-15 13:47:01 +01:00
|
|
|
*/
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
|
|
|
|
gistdoinsert(r, itup, &res, &giststate);
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < r->rd_att->natts; i++)
|
|
|
|
if (compvec[i] == TRUE)
|
|
|
|
pfree((char *) datum[i]);
|
|
|
|
pfree(itup);
|
|
|
|
pfree(compvec);
|
|
|
|
|
2000-06-13 09:35:40 +02:00
|
|
|
PG_RETURN_POINTER(res);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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.)
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static OffsetNumber
|
1997-09-08 23:56:23 +02:00
|
|
|
gistPageAddItem(GISTSTATE *giststate,
|
1997-09-07 07:04:48 +02:00
|
|
|
Relation r,
|
|
|
|
Page page,
|
|
|
|
Item item,
|
|
|
|
Size size,
|
|
|
|
OffsetNumber offsetNumber,
|
|
|
|
ItemIdFlags flags,
|
1997-09-08 23:56:23 +02:00
|
|
|
GISTENTRY *dentry,
|
|
|
|
IndexTuple *newtup)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTENTRY tmpcentry;
|
|
|
|
IndexTuple itup = (IndexTuple) item;
|
2001-01-12 01:12:58 +01:00
|
|
|
OffsetNumber retval;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* recompress the item given that we now know the exact page and
|
|
|
|
* offset for insertion
|
|
|
|
*/
|
|
|
|
gistdentryinit(giststate, dentry,
|
|
|
|
(((char *) itup) + sizeof(IndexTupleData)),
|
|
|
|
(Relation) 0, (Page) 0, (OffsetNumber) InvalidOffsetNumber,
|
|
|
|
IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE);
|
|
|
|
gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page,
|
|
|
|
offsetNumber, dentry->bytes, FALSE);
|
2001-01-12 01:12:58 +01:00
|
|
|
*newtup = gist_tuple_replacekey(r, tmpcentry, itup);
|
|
|
|
retval = PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup),
|
|
|
|
offsetNumber, flags);
|
1997-09-07 07:04:48 +02:00
|
|
|
/* be tidy */
|
2001-01-12 01:12:58 +01:00
|
|
|
if (tmpcentry.pred && tmpcentry.pred != dentry->pred
|
1997-09-07 07:04:48 +02:00
|
|
|
&& tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
|
|
|
|
pfree(tmpcentry.pred);
|
2001-01-12 01:12:58 +01:00
|
|
|
return (retval);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
static void
|
|
|
|
gistdoinsert( Relation r,
|
|
|
|
IndexTuple itup,
|
|
|
|
InsertIndexResult *res,
|
|
|
|
GISTSTATE *giststate ) {
|
|
|
|
IndexTuple *instup;
|
|
|
|
int i,ret,len = 1;
|
|
|
|
|
|
|
|
instup = ( IndexTuple* ) palloc( sizeof(IndexTuple) );
|
|
|
|
instup[0] = ( IndexTuple ) palloc( IndexTupleSize( itup ) );
|
|
|
|
memcpy( instup[0], itup, IndexTupleSize( itup ) );
|
|
|
|
|
|
|
|
ret = gistlayerinsert(r, GISTP_ROOT, &instup, &len, res, giststate);
|
|
|
|
if ( ret & SPLITED )
|
|
|
|
gistnewroot( giststate, r, instup, len );
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
for(i=0;i<len;i++)
|
|
|
|
pfree( instup[i] );
|
|
|
|
pfree( instup );
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
static int
|
|
|
|
gistlayerinsert( Relation r, BlockNumber blkno,
|
|
|
|
IndexTuple **itup, /* in - out, has compressed entry */
|
|
|
|
int *len , /* in - out */
|
|
|
|
InsertIndexResult *res, /* out */
|
|
|
|
GISTSTATE *giststate ) {
|
|
|
|
Buffer buffer;
|
|
|
|
Page page;
|
|
|
|
OffsetNumber child;
|
|
|
|
int ret;
|
|
|
|
GISTPageOpaque opaque;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
buffer = ReadBuffer(r, blkno);
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
if (!(opaque->flags & F_LEAF)) {
|
|
|
|
/* internal page, so we must walk on tree */
|
|
|
|
/* len IS equial 1 */
|
|
|
|
ItemId iid;
|
|
|
|
BlockNumber nblkno;
|
|
|
|
ItemPointerData oldtid;
|
|
|
|
IndexTuple oldtup;
|
|
|
|
|
|
|
|
child = gistchoose( r, page, *(*itup), giststate );
|
|
|
|
iid = PageGetItemId(page, child);
|
|
|
|
oldtup = (IndexTuple) PageGetItem(page, iid);
|
|
|
|
nblkno = ItemPointerGetBlockNumber(&(oldtup->t_tid));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After this call:
|
|
|
|
* 1. if child page was splited, then itup contains
|
|
|
|
* keys for each page
|
|
|
|
* 2. if child page wasn't splited, then itup contains
|
|
|
|
* additional for adjustement of current key
|
|
|
|
*/
|
|
|
|
ret = gistlayerinsert( r, nblkno, itup, len, res, giststate );
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* nothing inserted in child */
|
|
|
|
if ( ! (ret & INSERTED) ) {
|
|
|
|
ReleaseBuffer(buffer);
|
|
|
|
return 0x00;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* child does not splited */
|
|
|
|
if ( ! (ret & SPLITED) ) {
|
|
|
|
IndexTuple newtup = gistgetadjusted( r, oldtup, (*itup)[0], giststate );
|
|
|
|
if ( ! newtup ) {
|
|
|
|
/* not need to update key */
|
|
|
|
ReleaseBuffer(buffer);
|
|
|
|
return 0x00;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
pfree( (*itup)[0] ); /* !!! */
|
|
|
|
(*itup)[0] = newtup;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* key is modified, so old version must be deleted */
|
|
|
|
ItemPointerSet(&oldtid, blkno, child);
|
|
|
|
DirectFunctionCall2(gistdelete,
|
|
|
|
PointerGetDatum(r),
|
|
|
|
PointerGetDatum(&oldtid));
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
ret = INSERTED;
|
|
|
|
|
|
|
|
if ( gistnospace(page, (*itup), *len) ) {
|
|
|
|
/* no space for insertion */
|
|
|
|
IndexTuple *itvec;
|
|
|
|
int tlen;
|
|
|
|
|
|
|
|
ret |= SPLITED;
|
|
|
|
itvec = gistreadbuffer( r, buffer, &tlen );
|
|
|
|
itvec = gistjoinvector( itvec, &tlen, (*itup), *len );
|
|
|
|
pfree( (*itup) );
|
|
|
|
(*itup) = gistSplit( r, buffer, itvec, &tlen, giststate,
|
|
|
|
(opaque->flags & F_LEAF) ? res : NULL ); /*res only for inserting in leaf*/
|
|
|
|
ReleaseBuffer( buffer );
|
|
|
|
pfree( itvec );
|
|
|
|
*len = tlen; /* now tlen >= 2 */
|
|
|
|
} else {
|
|
|
|
/* enogth space */
|
|
|
|
OffsetNumber off, l;
|
|
|
|
|
|
|
|
off = ( PageIsEmpty(page) ) ?
|
|
|
|
FirstOffsetNumber
|
|
|
|
:
|
|
|
|
OffsetNumberNext(PageGetMaxOffsetNumber(page));
|
|
|
|
l = gistwritebuffer( r, page, (*itup), *len, off, giststate );
|
|
|
|
WriteBuffer(buffer);
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* set res if insert into leaf page, in
|
|
|
|
this case, len = 1 always */
|
|
|
|
if ( res && (opaque->flags & F_LEAF) )
|
|
|
|
ItemPointerSet(&((*res)->pointerData), blkno, l);
|
|
|
|
|
|
|
|
if ( *len > 1 ) { /* previos insert ret & SPLITED != 0 */
|
|
|
|
int i;
|
|
|
|
/* child was splited, so we must form union
|
|
|
|
* for insertion in parent */
|
|
|
|
IndexTuple newtup = gistunion(r, (*itup), *len, giststate);
|
|
|
|
for(i=0; i<*len; i++)
|
|
|
|
pfree( (*itup)[i] );
|
|
|
|
(*itup)[0] = newtup;
|
|
|
|
*len = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* Write itup vector to page, has no control of free space
|
|
|
|
*/
|
|
|
|
static OffsetNumber
|
|
|
|
gistwritebuffer( Relation r, Page page, IndexTuple *itup,
|
|
|
|
int len, OffsetNumber off, GISTSTATE *giststate) {
|
|
|
|
OffsetNumber l = InvalidOffsetNumber;
|
|
|
|
int i;
|
|
|
|
GISTENTRY tmpdentry;
|
|
|
|
IndexTuple newtup;
|
|
|
|
|
|
|
|
for(i=0; i<len; i++) {
|
|
|
|
l = gistPageAddItem(giststate, r, page,
|
|
|
|
(Item) itup[i], IndexTupleSize(itup[i]),
|
|
|
|
off, LP_USED, &tmpdentry, &newtup);
|
|
|
|
off = OffsetNumberNext( off );
|
|
|
|
if (tmpdentry.pred != (((char *) itup[i]) + sizeof(IndexTupleData)) && tmpdentry.pred)
|
|
|
|
pfree(tmpdentry.pred);
|
|
|
|
if (itup[i] != newtup)
|
|
|
|
pfree(newtup);
|
|
|
|
}
|
|
|
|
return l;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* Check space for itup vector on page
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
gistnospace( Page page, IndexTuple *itvec, int len ) {
|
|
|
|
int size = 0;
|
|
|
|
int i;
|
|
|
|
for(i=0; i<len; i++)
|
|
|
|
size += IndexTupleSize( itvec[i] )+4; /* ??? */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
return (PageGetFreeSpace(page) < size);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* Read buffer into itup vector
|
|
|
|
*/
|
|
|
|
static IndexTuple *
|
|
|
|
gistreadbuffer( Relation r, Buffer buffer, int *len /*out*/) {
|
|
|
|
OffsetNumber i, maxoff;
|
|
|
|
IndexTuple *itvec;
|
|
|
|
Page p = (Page) BufferGetPage(buffer);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
*len=0;
|
|
|
|
maxoff = PageGetMaxOffsetNumber(p);
|
|
|
|
itvec = palloc( sizeof(IndexTuple) * maxoff );
|
|
|
|
for(i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
|
|
itvec[ (*len)++ ] = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
return itvec;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* join two vectors into one
|
|
|
|
*/
|
|
|
|
static IndexTuple *
|
|
|
|
gistjoinvector( IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen ) {
|
|
|
|
itvec = (IndexTuple*) repalloc( (void*)itvec, sizeof(IndexTuple) * ( (*len) + addlen ) );
|
|
|
|
memmove( &itvec[*len], additvec, sizeof(IndexTuple) * addlen );
|
|
|
|
*len += addlen;
|
|
|
|
return itvec;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* return union of itup vector
|
|
|
|
*/
|
|
|
|
static IndexTuple
|
|
|
|
gistunion( Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate ) {
|
|
|
|
bytea *evec;
|
|
|
|
char *datum;
|
|
|
|
int datumsize, i;
|
|
|
|
GISTENTRY centry;
|
|
|
|
char isnull;
|
|
|
|
IndexTuple newtup;
|
|
|
|
|
|
|
|
evec = (bytea *) palloc(len * sizeof(GISTENTRY) + VARHDRSZ);
|
|
|
|
VARATT_SIZEP(evec) = len * sizeof(GISTENTRY) + VARHDRSZ;
|
|
|
|
|
|
|
|
for ( i = 0 ; i< len ; i++ )
|
|
|
|
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[i],
|
|
|
|
(char*) itvec[i] + sizeof(IndexTupleData),
|
|
|
|
(Relation)NULL, (Page)NULL, (OffsetNumber)NULL,
|
|
|
|
IndexTupleSize((IndexTuple)itvec[i]) - sizeof(IndexTupleData), FALSE);
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
datum = (char *)
|
|
|
|
DatumGetPointer(FunctionCall2(&giststate->unionFn,
|
|
|
|
PointerGetDatum(evec),
|
|
|
|
PointerGetDatum(&datumsize)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
for ( i = 0 ; i< len ; i++ )
|
|
|
|
if ( ((GISTENTRY *) VARDATA(evec))[i].pred &&
|
|
|
|
((GISTENTRY *) VARDATA(evec))[i].pred !=
|
|
|
|
((char*)( itvec[i] )+ sizeof(IndexTupleData)) )
|
|
|
|
pfree( ((GISTENTRY *) VARDATA(evec))[i].pred );
|
|
|
|
|
|
|
|
pfree( evec );
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
gistcentryinit(giststate, ¢ry, datum,
|
|
|
|
(Relation)NULL, (Page)NULL, (OffsetNumber)NULL,
|
|
|
|
datumsize, FALSE);
|
|
|
|
|
|
|
|
isnull = (centry.pred) ? ' ' : 'n';
|
|
|
|
newtup = (IndexTuple) index_formtuple( r->rd_att, (Datum *) ¢ry.pred, &isnull );
|
|
|
|
if (centry.pred != datum)
|
|
|
|
pfree( datum );
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
return newtup;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/*
|
|
|
|
* Forms union of oldtup and addtup, if union == oldtup then return NULL
|
|
|
|
*/
|
|
|
|
static IndexTuple
|
|
|
|
gistgetadjusted( Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate ) {
|
|
|
|
bytea *evec;
|
|
|
|
char *datum;
|
|
|
|
int datumsize;
|
|
|
|
bool result;
|
|
|
|
char isnull;
|
|
|
|
GISTENTRY centry, *ev0p, *ev1p;
|
|
|
|
IndexTuple newtup = NULL;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ);
|
2000-07-04 01:10:14 +02:00
|
|
|
VARATT_SIZEP(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0],
|
2001-01-12 01:12:58 +01:00
|
|
|
(char*) oldtup + sizeof(IndexTupleData), (Relation) NULL,
|
|
|
|
(Page) NULL, (OffsetNumber) 0,
|
|
|
|
IndexTupleSize((IndexTuple)oldtup) - sizeof(IndexTupleData), FALSE);
|
1997-09-07 07:04:48 +02:00
|
|
|
ev0p = &((GISTENTRY *) VARDATA(evec))[0];
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[1],
|
|
|
|
(char*) addtup + sizeof(IndexTupleData), (Relation) NULL,
|
|
|
|
(Page) NULL, (OffsetNumber) 0,
|
|
|
|
IndexTupleSize((IndexTuple)addtup) - sizeof(IndexTupleData), FALSE);
|
1997-09-07 07:04:48 +02:00
|
|
|
ev1p = &((GISTENTRY *) VARDATA(evec))[1];
|
|
|
|
|
2000-05-30 06:25:00 +02:00
|
|
|
datum = (char *)
|
|
|
|
DatumGetPointer(FunctionCall2(&giststate->unionFn,
|
2001-01-12 01:12:58 +01:00
|
|
|
PointerGetDatum(evec),
|
|
|
|
PointerGetDatum(&datumsize)));
|
|
|
|
|
|
|
|
if ( ! ( ev0p->pred && ev1p->pred ) ) {
|
|
|
|
result = ( ev0p->pred == NULL && ev1p->pred == NULL );
|
|
|
|
} else {
|
|
|
|
FunctionCall3(&giststate->equalFn,
|
|
|
|
PointerGetDatum(ev0p->pred),
|
|
|
|
PointerGetDatum(datum),
|
|
|
|
PointerGetDatum(&result));
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
if ( result ) {
|
|
|
|
/* not need to update key */
|
|
|
|
pfree( datum );
|
|
|
|
} else {
|
1997-09-07 07:04:48 +02:00
|
|
|
gistcentryinit(giststate, ¢ry, datum, ev0p->rel, ev0p->page,
|
2001-01-12 01:12:58 +01:00
|
|
|
ev0p->offset, datumsize, FALSE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
isnull = (centry.pred) ? ' ' : 'n';
|
|
|
|
newtup = (IndexTuple) index_formtuple( r->rd_att, (Datum *) ¢ry.pred, &isnull );
|
|
|
|
newtup->t_tid = oldtup->t_tid;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (centry.pred != datum)
|
2001-01-12 01:12:58 +01:00
|
|
|
pfree( datum );
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
if ( ev0p->pred &&
|
|
|
|
ev0p->pred != (char*) oldtup + sizeof(IndexTupleData) )
|
|
|
|
pfree( ev0p->pred );
|
|
|
|
if ( ev1p->pred &&
|
|
|
|
ev1p->pred != (char*) addtup + sizeof(IndexTupleData) )
|
|
|
|
pfree( ev1p->pred );
|
|
|
|
pfree( evec );
|
|
|
|
|
|
|
|
return newtup;
|
|
|
|
}
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* gistSplit -- split a page in the tree.
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
2001-01-12 01:12:58 +01:00
|
|
|
static IndexTuple *
|
1996-08-26 22:02:12 +02:00
|
|
|
gistSplit(Relation r,
|
1997-09-07 07:04:48 +02:00
|
|
|
Buffer buffer,
|
2001-01-12 01:12:58 +01:00
|
|
|
IndexTuple *itup, /* contains compressed entry */
|
|
|
|
int *len,
|
|
|
|
GISTSTATE *giststate,
|
|
|
|
InsertIndexResult *res)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Page p;
|
2001-01-12 01:12:58 +01:00
|
|
|
Buffer leftbuf, rightbuf;
|
|
|
|
Page left, right;
|
|
|
|
OffsetNumber *spl_left, *spl_right;
|
|
|
|
IndexTuple *lvectup, *rvectup, *newtup;
|
|
|
|
int leftoff, rightoff;
|
|
|
|
BlockNumber lbknum, rbknum;
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTPageOpaque opaque;
|
2001-01-12 01:12:58 +01:00
|
|
|
char isnull;
|
1997-09-08 04:41:22 +02:00
|
|
|
GIST_SPLITVEC v;
|
|
|
|
bytea *entryvec;
|
|
|
|
bool *decompvec;
|
2001-01-12 01:12:58 +01:00
|
|
|
GISTENTRY tmpentry;
|
|
|
|
int i, nlen;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
p = (Page) BufferGetPage(buffer);
|
|
|
|
opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The root of the tree is the first block in the relation. If we're
|
|
|
|
* about to split the root, we need to do some hocus-pocus to enforce
|
|
|
|
* this guarantee.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (BufferGetBlockNumber(buffer) == GISTP_ROOT)
|
|
|
|
{
|
|
|
|
leftbuf = ReadBuffer(r, P_NEW);
|
|
|
|
GISTInitBuffer(leftbuf, opaque->flags);
|
|
|
|
lbknum = BufferGetBlockNumber(leftbuf);
|
|
|
|
left = (Page) BufferGetPage(leftbuf);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
leftbuf = buffer;
|
|
|
|
IncrBufferRefCount(buffer);
|
|
|
|
lbknum = BufferGetBlockNumber(buffer);
|
|
|
|
left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData));
|
|
|
|
}
|
|
|
|
|
|
|
|
rightbuf = ReadBuffer(r, P_NEW);
|
|
|
|
GISTInitBuffer(rightbuf, opaque->flags);
|
|
|
|
rbknum = BufferGetBlockNumber(rightbuf);
|
|
|
|
right = (Page) BufferGetPage(rightbuf);
|
|
|
|
|
|
|
|
/* generate the item array */
|
2001-01-12 01:12:58 +01:00
|
|
|
entryvec = (bytea *) palloc(VARHDRSZ + (*len+1) * sizeof(GISTENTRY));
|
|
|
|
decompvec = (bool *) palloc(VARHDRSZ + (*len+1) * sizeof(bool));
|
|
|
|
VARATT_SIZEP(entryvec) = (*len+1) * sizeof(GISTENTRY) + VARHDRSZ;
|
|
|
|
for (i = 1; i <= *len; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i],
|
2001-01-12 01:12:58 +01:00
|
|
|
(((char *) itup[i-1]) + sizeof(IndexTupleData)),
|
1997-09-07 07:04:48 +02:00
|
|
|
r, p, i,
|
2001-01-12 01:12:58 +01:00
|
|
|
IndexTupleSize(itup[i-1]) - sizeof(IndexTupleData), FALSE);
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred)
|
2001-01-12 01:12:58 +01:00
|
|
|
== (((char *) itup[i-1]) + sizeof(IndexTupleData)))
|
1997-09-07 07:04:48 +02:00
|
|
|
decompvec[i] = FALSE;
|
|
|
|
else
|
|
|
|
decompvec[i] = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now let the user-defined picksplit function set up the split vector */
|
2000-05-30 06:25:00 +02:00
|
|
|
FunctionCall2(&giststate->picksplitFn,
|
2001-01-12 01:12:58 +01:00
|
|
|
PointerGetDatum(entryvec),
|
|
|
|
PointerGetDatum(&v));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* clean up the entry vector: its preds need to be deleted, too */
|
2001-01-12 01:12:58 +01:00
|
|
|
for (i = 1; i <= *len; i++)
|
|
|
|
if (decompvec[i] && ((GISTENTRY *) VARDATA(entryvec))[i].pred)
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred);
|
|
|
|
pfree(entryvec);
|
|
|
|
pfree(decompvec);
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
spl_left = v.spl_left; spl_right = v.spl_right;
|
|
|
|
|
|
|
|
/* form left and right vector */
|
|
|
|
lvectup = (IndexTuple*) palloc( sizeof( IndexTuple )*v.spl_nleft );
|
|
|
|
rvectup = (IndexTuple*) palloc( sizeof( IndexTuple )*v.spl_nright );
|
|
|
|
leftoff = rightoff = 0;
|
|
|
|
for( i=1; i <= *len; i++ ) {
|
|
|
|
if (i == *(spl_left) || ( i==*len && *(spl_left) != FirstOffsetNumber ) ) {
|
|
|
|
lvectup[ leftoff++ ] = itup[ i-1 ];
|
|
|
|
spl_left++;
|
|
|
|
} else {
|
|
|
|
rvectup[ rightoff++ ] = itup[ i-1 ];
|
|
|
|
spl_right++;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* write on disk (may be need another split) */
|
|
|
|
if ( gistnospace(right, rvectup, v.spl_nright) ) {
|
|
|
|
nlen = v.spl_nright;
|
|
|
|
newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate,
|
|
|
|
( res && rvectup[ nlen-1 ] == itup[ *len - 1 ] ) ? res : NULL );
|
|
|
|
ReleaseBuffer( rightbuf );
|
|
|
|
} else {
|
|
|
|
OffsetNumber l;
|
|
|
|
|
|
|
|
l = gistwritebuffer( r, right, rvectup, v.spl_nright, FirstOffsetNumber, giststate );
|
|
|
|
WriteBuffer(rightbuf);
|
|
|
|
|
|
|
|
if ( res )
|
|
|
|
ItemPointerSet(&((*res)->pointerData), rbknum, l);
|
|
|
|
gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL,
|
|
|
|
(Page) NULL, (OffsetNumber) 0,
|
|
|
|
-1, FALSE);
|
|
|
|
if (v.spl_rdatum != tmpentry.pred)
|
|
|
|
pfree(v.spl_rdatum);
|
|
|
|
v.spl_rdatum = tmpentry.pred;
|
|
|
|
|
|
|
|
nlen = 1;
|
|
|
|
newtup = (IndexTuple*) palloc( sizeof(IndexTuple) * 1);
|
|
|
|
isnull = ( v.spl_rdatum ) ? ' ' : 'n';
|
|
|
|
newtup[0] = (IndexTuple) index_formtuple(r->rd_att, (Datum *) &(v.spl_rdatum), &isnull);
|
|
|
|
ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
if ( gistnospace(left, lvectup, v.spl_nleft) ) {
|
|
|
|
int llen = v.spl_nleft;
|
|
|
|
IndexTuple *lntup;
|
|
|
|
|
|
|
|
lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate,
|
|
|
|
( res && lvectup[ llen-1 ] == itup[ *len - 1 ] ) ? res : NULL );
|
|
|
|
ReleaseBuffer( leftbuf );
|
|
|
|
|
|
|
|
newtup = gistjoinvector( newtup, &nlen, lntup, llen );
|
|
|
|
pfree( lntup );
|
|
|
|
} else {
|
|
|
|
OffsetNumber l;
|
|
|
|
|
|
|
|
l = gistwritebuffer( r, left, lvectup, v.spl_nleft, FirstOffsetNumber, giststate );
|
|
|
|
if ( BufferGetBlockNumber(buffer) != GISTP_ROOT)
|
|
|
|
PageRestoreTempPage(left, p);
|
|
|
|
|
|
|
|
WriteBuffer(leftbuf);
|
|
|
|
|
|
|
|
if ( res )
|
|
|
|
ItemPointerSet(&((*res)->pointerData), lbknum, l);
|
|
|
|
gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL,
|
|
|
|
(Page) NULL, (OffsetNumber) 0,
|
|
|
|
-1, FALSE);
|
|
|
|
if (v.spl_ldatum != tmpentry.pred)
|
|
|
|
pfree(v.spl_ldatum);
|
|
|
|
v.spl_ldatum = tmpentry.pred;
|
|
|
|
|
|
|
|
nlen += 1;
|
|
|
|
newtup = (IndexTuple*) repalloc( (void*)newtup, sizeof(IndexTuple) * nlen);
|
|
|
|
isnull = ( v.spl_ldatum ) ? ' ' : 'n';
|
|
|
|
newtup[nlen-1] = (IndexTuple) index_formtuple(r->rd_att, (Datum *) &(v.spl_ldatum), &isnull);
|
|
|
|
ItemPointerSet(&(newtup[nlen-1]->t_tid), lbknum, 1);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* adjust active scans */
|
|
|
|
gistadjscans(r, GISTOP_SPLIT, BufferGetBlockNumber(buffer), FirstOffsetNumber);
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* !!! pfree */
|
|
|
|
pfree( rvectup );
|
|
|
|
pfree( lvectup );
|
|
|
|
pfree( v.spl_left );
|
|
|
|
pfree( v.spl_right );
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
*len = nlen;
|
|
|
|
return newtup;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
|
|
|
|
static void
|
2001-01-12 01:12:58 +01:00
|
|
|
gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple *itup, int len)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Buffer b;
|
|
|
|
Page p;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
b = ReadBuffer(r, GISTP_ROOT);
|
|
|
|
GISTInitBuffer(b, 0);
|
|
|
|
p = BufferGetPage(b);
|
2001-01-12 01:12:58 +01:00
|
|
|
|
|
|
|
gistwritebuffer( r, p, itup, len, FirstOffsetNumber, giststate );
|
1997-09-07 07:04:48 +02:00
|
|
|
WriteBuffer(b);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
GISTInitBuffer(Buffer b, uint32 f)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTPageOpaque opaque;
|
|
|
|
Page page;
|
|
|
|
Size pageSize;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
pageSize = BufferGetPageSize(b);
|
|
|
|
|
|
|
|
page = BufferGetPage(b);
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(page, 0, (int) pageSize);
|
1997-09-07 07:04:48 +02:00
|
|
|
PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
|
|
|
|
|
|
|
|
opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
|
|
|
|
opaque->flags = f;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** find entry with lowest penalty
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static OffsetNumber
|
1997-09-07 07:04:48 +02:00
|
|
|
gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
1997-09-08 23:56:23 +02:00
|
|
|
GISTSTATE *giststate)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
OffsetNumber maxoff;
|
|
|
|
OffsetNumber i;
|
|
|
|
char *id;
|
|
|
|
char *datum;
|
|
|
|
float usize;
|
|
|
|
OffsetNumber which;
|
|
|
|
float which_grow;
|
|
|
|
GISTENTRY entry,
|
|
|
|
identry;
|
|
|
|
int size,
|
|
|
|
idsize;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
idsize = IndexTupleSize(it) - sizeof(IndexTupleData);
|
|
|
|
id = ((char *) it) + sizeof(IndexTupleData);
|
|
|
|
maxoff = PageGetMaxOffsetNumber(p);
|
|
|
|
which_grow = -1.0;
|
|
|
|
which = -1;
|
|
|
|
|
|
|
|
gistdentryinit(giststate, &identry, id, (Relation) NULL, (Page) NULL,
|
|
|
|
(OffsetNumber) 0, idsize, FALSE);
|
|
|
|
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
|
|
{
|
|
|
|
datum = (char *) PageGetItem(p, PageGetItemId(p, i));
|
|
|
|
size = IndexTupleSize(datum) - sizeof(IndexTupleData);
|
|
|
|
datum += sizeof(IndexTupleData);
|
|
|
|
gistdentryinit(giststate, &entry, datum, r, p, i, size, FALSE);
|
2000-05-30 06:25:00 +02:00
|
|
|
FunctionCall3(&giststate->penaltyFn,
|
|
|
|
PointerGetDatum(&entry),
|
|
|
|
PointerGetDatum(&identry),
|
|
|
|
PointerGetDatum(&usize));
|
1997-09-07 07:04:48 +02:00
|
|
|
if (which_grow < 0 || usize < which_grow)
|
|
|
|
{
|
|
|
|
which = i;
|
|
|
|
which_grow = usize;
|
|
|
|
if (which_grow == 0)
|
|
|
|
break;
|
|
|
|
}
|
2001-01-12 01:12:58 +01:00
|
|
|
if (entry.pred && entry.pred != datum)
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(entry.pred);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
2001-01-12 01:12:58 +01:00
|
|
|
if (identry.pred && identry.pred != id)
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(identry.pred);
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return which;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
gistfreestack(GISTSTACK *s)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTSTACK *p;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
while (s != (GISTSTACK *) NULL)
|
|
|
|
{
|
|
|
|
p = s->gs_parent;
|
|
|
|
pfree(s);
|
|
|
|
s = p;
|
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
** remove an entry from a page
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistdelete(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2000-06-13 09:35:40 +02:00
|
|
|
Relation r = (Relation) PG_GETARG_POINTER(0);
|
|
|
|
ItemPointer tid = (ItemPointer) PG_GETARG_POINTER(1);
|
1997-09-08 04:41:22 +02:00
|
|
|
BlockNumber blkno;
|
|
|
|
OffsetNumber offnum;
|
|
|
|
Buffer buf;
|
|
|
|
Page page;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-12-15 13:47:01 +01:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Notes in ExecUtils:ExecOpenIndices() Also note that only vacuum
|
|
|
|
* deletes index tuples now...
|
1998-12-15 13:47:01 +01:00
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* RelationSetLockForWrite(r);
|
1998-12-15 13:47:01 +01:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
blkno = ItemPointerGetBlockNumber(tid);
|
|
|
|
offnum = ItemPointerGetOffsetNumber(tid);
|
|
|
|
|
|
|
|
/* adjust any scans that will be affected by this deletion */
|
|
|
|
gistadjscans(r, GISTOP_DEL, blkno, offnum);
|
|
|
|
|
|
|
|
/* delete the index tuple */
|
|
|
|
buf = ReadBuffer(r, blkno);
|
|
|
|
page = BufferGetPage(buf);
|
|
|
|
|
|
|
|
PageIndexTupleDelete(page, offnum);
|
|
|
|
|
|
|
|
WriteBuffer(buf);
|
|
|
|
|
2000-06-14 07:24:50 +02:00
|
|
|
PG_RETURN_VOID();
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
initGISTstate(GISTSTATE *giststate, Relation index)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
RegProcedure consistent_proc,
|
|
|
|
union_proc,
|
|
|
|
compress_proc,
|
|
|
|
decompress_proc;
|
|
|
|
RegProcedure penalty_proc,
|
|
|
|
picksplit_proc,
|
|
|
|
equal_proc;
|
|
|
|
HeapTuple htup;
|
1998-09-01 05:29:17 +02:00
|
|
|
Form_pg_index itupform;
|
2000-11-16 23:30:52 +01:00
|
|
|
Oid indexrelid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
consistent_proc = index_getprocid(index, 1, GIST_CONSISTENT_PROC);
|
|
|
|
union_proc = index_getprocid(index, 1, GIST_UNION_PROC);
|
|
|
|
compress_proc = index_getprocid(index, 1, GIST_COMPRESS_PROC);
|
|
|
|
decompress_proc = index_getprocid(index, 1, GIST_DECOMPRESS_PROC);
|
|
|
|
penalty_proc = index_getprocid(index, 1, GIST_PENALTY_PROC);
|
|
|
|
picksplit_proc = index_getprocid(index, 1, GIST_PICKSPLIT_PROC);
|
|
|
|
equal_proc = index_getprocid(index, 1, GIST_EQUAL_PROC);
|
1998-01-15 20:46:37 +01:00
|
|
|
fmgr_info(consistent_proc, &giststate->consistentFn);
|
|
|
|
fmgr_info(union_proc, &giststate->unionFn);
|
|
|
|
fmgr_info(compress_proc, &giststate->compressFn);
|
|
|
|
fmgr_info(decompress_proc, &giststate->decompressFn);
|
|
|
|
fmgr_info(penalty_proc, &giststate->penaltyFn);
|
|
|
|
fmgr_info(picksplit_proc, &giststate->picksplitFn);
|
|
|
|
fmgr_info(equal_proc, &giststate->equalFn);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* see if key type is different from type of attribute being indexed */
|
2000-11-16 23:30:52 +01:00
|
|
|
htup = SearchSysCache(INDEXRELID,
|
|
|
|
ObjectIdGetDatum(RelationGetRelid(index)),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(htup))
|
1999-05-10 02:46:32 +02:00
|
|
|
elog(ERROR, "initGISTstate: index %u not found",
|
1998-09-01 06:40:42 +02:00
|
|
|
RelationGetRelid(index));
|
2000-11-16 23:30:52 +01:00
|
|
|
itupform = (Form_pg_index) GETSTRUCT(htup);
|
1997-09-07 07:04:48 +02:00
|
|
|
giststate->haskeytype = itupform->indhaskeytype;
|
2000-11-16 23:30:52 +01:00
|
|
|
indexrelid = itupform->indexrelid;
|
|
|
|
ReleaseSysCache(htup);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (giststate->haskeytype)
|
|
|
|
{
|
|
|
|
/* key type is different -- is it byval? */
|
2000-11-16 23:30:52 +01:00
|
|
|
htup = SearchSysCache(ATTNUM,
|
|
|
|
ObjectIdGetDatum(indexrelid),
|
|
|
|
UInt16GetDatum(FirstOffsetNumber),
|
|
|
|
0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!HeapTupleIsValid(htup))
|
1999-05-10 02:46:32 +02:00
|
|
|
elog(ERROR, "initGISTstate: no attribute tuple %u %d",
|
2000-11-16 23:30:52 +01:00
|
|
|
indexrelid, FirstOffsetNumber);
|
1998-09-01 05:29:17 +02:00
|
|
|
giststate->keytypbyval = (((Form_pg_attribute) htup)->attbyval);
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(htup);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
giststate->keytypbyval = FALSE;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static IndexTuple
|
1996-08-26 22:02:12 +02:00
|
|
|
gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *datum = (((char *) t) + sizeof(IndexTupleData));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* if new entry fits in index tuple, copy it in */
|
2001-01-12 01:12:58 +01:00
|
|
|
if ((Size) entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData) || (Size) entry.bytes == 0 )
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
memcpy(datum, entry.pred, entry.bytes);
|
|
|
|
/* clear out old size */
|
|
|
|
t->t_info &= 0xe000;
|
|
|
|
/* or in new size */
|
|
|
|
t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData));
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return t;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* generate a new index tuple for the compressed entry */
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc = r->rd_att;
|
|
|
|
IndexTuple newtup;
|
2001-01-12 01:12:58 +01:00
|
|
|
char isnull;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
isnull = ( entry.pred ) ? ' ' : 'n';
|
1997-09-07 07:04:48 +02:00
|
|
|
newtup = (IndexTuple) index_formtuple(tupDesc,
|
1997-09-08 23:56:23 +02:00
|
|
|
(Datum *) &(entry.pred),
|
2001-01-12 01:12:58 +01:00
|
|
|
&isnull);
|
1997-09-07 07:04:48 +02:00
|
|
|
newtup->t_tid = t->t_tid;
|
1998-09-01 05:29:17 +02:00
|
|
|
return newtup;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
** initialize a GiST entry with a decompressed version of pred
|
|
|
|
*/
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
gistdentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
|
1997-09-07 07:04:48 +02:00
|
|
|
Page pg, OffsetNumber o, int b, bool l)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTENTRY *dep;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
gistentryinit(*e, pr, r, pg, o, b, l);
|
|
|
|
if (giststate->haskeytype)
|
|
|
|
{
|
2000-05-30 06:25:00 +02:00
|
|
|
dep = (GISTENTRY *)
|
|
|
|
DatumGetPointer(FunctionCall1(&giststate->decompressFn,
|
|
|
|
PointerGetDatum(e)));
|
1997-09-07 07:04:48 +02:00
|
|
|
gistentryinit(*e, dep->pred, dep->rel, dep->page, dep->offset, dep->bytes,
|
|
|
|
dep->leafkey);
|
|
|
|
if (dep != e)
|
|
|
|
pfree(dep);
|
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** initialize a GiST entry with a compressed version of pred
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
|
1997-09-07 07:04:48 +02:00
|
|
|
Page pg, OffsetNumber o, int b, bool l)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTENTRY *cep;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
gistentryinit(*e, pr, r, pg, o, b, l);
|
|
|
|
if (giststate->haskeytype)
|
|
|
|
{
|
2000-05-30 06:25:00 +02:00
|
|
|
cep = (GISTENTRY *)
|
|
|
|
DatumGetPointer(FunctionCall1(&giststate->compressFn,
|
|
|
|
PointerGetDatum(e)));
|
1997-09-07 07:04:48 +02:00
|
|
|
gistentryinit(*e, cep->pred, cep->rel, cep->page, cep->offset, cep->bytes,
|
|
|
|
cep->leafkey);
|
|
|
|
if (cep != e)
|
|
|
|
pfree(cep);
|
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
#ifdef GISTDEBUG
|
2001-01-12 01:12:58 +01:00
|
|
|
static void
|
|
|
|
gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2001-01-12 01:12:58 +01:00
|
|
|
Buffer buffer;
|
1997-09-08 04:41:22 +02:00
|
|
|
Page page;
|
2001-01-12 01:12:58 +01:00
|
|
|
GISTPageOpaque opaque;
|
|
|
|
IndexTuple which;
|
|
|
|
ItemId iid;
|
|
|
|
OffsetNumber i,maxoff;
|
|
|
|
BlockNumber cblk;
|
|
|
|
char *pred;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
pred = (char*) palloc( sizeof(char)*level+1 );
|
|
|
|
MemSet(pred, '\t', level);
|
|
|
|
pred[level]='\0';
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
buffer = ReadBuffer(r, blk);
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
|
|
|
|
|
|
|
|
maxoff = PageGetMaxOffsetNumber( page );
|
|
|
|
|
|
|
|
elog(NOTICE,"%sPage: %d %s blk: %d maxoff: %d free: %d", pred, coff, ( opaque->flags & F_LEAF ) ? "LEAF" : "INTE", (int)blk, (int)maxoff, PageGetFreeSpace(page));
|
|
|
|
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
|
|
|
|
iid = PageGetItemId(page, i);
|
|
|
|
which = (IndexTuple) PageGetItem(page, iid);
|
|
|
|
cblk = ItemPointerGetBlockNumber(&(which->t_tid));
|
|
|
|
#ifdef PRINTTUPLE
|
|
|
|
elog(NOTICE,"%s Tuple. blk: %d size: %d", pred, (int)cblk, IndexTupleSize( which ) );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( ! ( opaque->flags & F_LEAF ) ) {
|
|
|
|
gist_dumptree( r, level+1, cblk, i );
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
2001-01-12 01:12:58 +01:00
|
|
|
ReleaseBuffer(buffer);
|
|
|
|
pfree(pred);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* defined GISTDEBUG */
|
2000-10-21 17:43:36 +02:00
|
|
|
|
|
|
|
void
|
|
|
|
gist_redo(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
elog(STOP, "gist_redo: unimplemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gist_undo(XLogRecPtr lsn, XLogRecord *record)
|
|
|
|
{
|
|
|
|
elog(STOP, "gist_undo: unimplemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gist_desc(char *buf, uint8 xl_info, char* rec)
|
|
|
|
{
|
|
|
|
}
|
2001-01-12 01:12:58 +01:00
|
|
|
|